using MainShell.Alarm; using MainShell.Common; using MainShell.Log; using MainShell.Vision; using MW.WorkFlow; using Stylet; using System; using System.Threading; using System.Threading.Tasks; namespace MainShell.Process { public class ChipPreparationRequestHandler : IHandle, IHandle, IDisposable { private readonly IEventAggregator _eventAggregator; private readonly PreparationAreaService _preparationAreaService; private readonly AlarmOperate _alarmOperate; private readonly WorkflowRunner _workflowRunner; private readonly SemaphoreSlim _serialGate = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _runnerStateGate = new SemaphoreSlim(1, 1); private readonly CancellationTokenSource _disposeCts = new CancellationTokenSource(); private bool _disposed; public ChipPreparationRequestHandler(IEventAggregator eventAggregator, PreparationAreaService preparationAreaService, AlarmOperate alarmOperate) { _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator)); _preparationAreaService = preparationAreaService ?? throw new ArgumentNullException(nameof(preparationAreaService)); _alarmOperate = alarmOperate ?? throw new ArgumentNullException(nameof(alarmOperate)); _workflowRunner = new WorkflowRunner(); _eventAggregator.Subscribe(this); } public void Handle(ChipPreparationRequestedEventArgs message) { if (message == null || message.Request == null) { return; } ChipPreparationRequest request = CloneRequest(message.Request); _ = ProcessRequestAsync(request); } public void Handle(ChipPreparationStateChangedEventArgs message) { if (message == null || message.Snapshot == null) { return; } ChipPreparationStateSnapshot snapshot = CloneSnapshot(message.Snapshot); _ = SynchronizeRunnerStateAsync(snapshot); } public void Dispose() { if (_disposed) { return; } _disposed = true; _disposeCts.Cancel(); _eventAggregator.Unsubscribe(this); _workflowRunner.Dispose(); _disposeCts.Dispose(); _serialGate.Dispose(); _runnerStateGate.Dispose(); } private async Task ProcessRequestAsync(ChipPreparationRequest request) { bool gateEntered = false; try { await _serialGate.WaitAsync(_disposeCts.Token); gateEntered = true; string.Format("Chip preparation request started. {0}.", BuildRequestSummary(request)).LogProcessInfo(); ChipPreparationWaitResult executeResult = await ExecuteOnDemandActivityAsync(request); string rejectReason; if (executeResult.Success) { _preparationAreaService.TryMarkReady(request.RequestId, out rejectReason); } else { string errorMessage = string.IsNullOrWhiteSpace(executeResult.ErrorMessage) ? "芯片准备执行失败。" : executeResult.ErrorMessage; _preparationAreaService.TryMarkFault(errorMessage, out rejectReason); int? visionAlarmId = ResolveVisionAlarmId(executeResult); if (visionAlarmId.HasValue) { await _alarmOperate.AlertAsync( visionAlarmId.Value, nameof(ChipPreparationRequestHandler), "Vision alarm triggered", BuildAlarmTriggerSummary(request, executeResult)).ConfigureAwait(false); } } } catch (OperationCanceledException) { } catch (Exception ex) { string.Format("Chip preparation request failed with exception. {0}. {1}", BuildRequestSummary(request), ex).LogProcessError(); string rejectReason; _preparationAreaService.TryMarkFault("芯片准备执行异常: " + ex.Message, out rejectReason); } finally { if (gateEntered) { _serialGate.Release(); } } } private async Task ExecuteOnDemandActivityAsync(ChipPreparationRequest request) { try { WorkflowContext context = new WorkflowContext(); context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.ChipPreparationOnDemandFlow; string activityName = request != null && request.Action == ChipPreparationAction.Unload ? ProcessFlowName.ChipUnloadExecuteActivity : ProcessFlowName.ChipLoadExecuteActivity; ChipPreparationExecuteActivity activity = new ChipPreparationExecuteActivity(activityName, request); WorkflowRunCompletedEventArgs runResult = await _workflowRunner.RunActivityWithResultAsync(activity, context); if (runResult == null) { string.Format("Chip preparation request returned no workflow result. {0}.", BuildRequestSummary(request)).LogProcessError(); return ChipPreparationWaitResult.CreateFailure("芯片准备流程未返回执行结果。"); } switch (runResult.FinalState) { case WorkflowState.Completed: string.Format("Chip preparation request completed. {0}, FinalState={1}.", BuildRequestSummary(request), runResult.FinalState).LogProcessInfo(); return ChipPreparationWaitResult.CreateSuccess(); case WorkflowState.Canceled: string.Format("Chip preparation request canceled. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError(); return ChipPreparationWaitResult.CreateCanceled("芯片准备流程已取消。"); case WorkflowState.Faulted: string.Format("Chip preparation request faulted. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError(); return ChipPreparationWaitResult.CreateFailure(string.IsNullOrWhiteSpace(runResult.Error) ? "芯片准备流程执行失败。" : runResult.Error); default: string.Format("Chip preparation request returned unexpected state. {0}, FinalState={1}, ErrorMessage={2}.", BuildRequestSummary(request), runResult.FinalState, NormalizeLogValue(runResult.Error)).LogProcessError(); return ChipPreparationWaitResult.CreateFailure(string.Format("芯片准备流程返回了未预期状态: {0}。", runResult.FinalState)); } } catch (Exception ex) { string.Format("Chip preparation workflow execution threw an exception. {0}. {1}", BuildRequestSummary(request), ex).LogProcessError(); return ChipPreparationWaitResult.CreateFailure(ex.Message); } } private async Task SynchronizeRunnerStateAsync(ChipPreparationStateSnapshot snapshot) { if (snapshot == null || _disposed) { return; } await _runnerStateGate.WaitAsync(_disposeCts.Token).ConfigureAwait(false); try { if (snapshot.Status == ChipPreparationStatus.Paused) { string.Format("Chip preparation runner paused. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug(); _workflowRunner.Pause(); return; } if (snapshot.Status == ChipPreparationStatus.Preparing || snapshot.Status == ChipPreparationStatus.Loading || snapshot.Status == ChipPreparationStatus.Unloading) { string.Format("Chip preparation runner resumed. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug(); _workflowRunner.Resume(); return; } if ((snapshot.Status == ChipPreparationStatus.Idle || snapshot.Status == ChipPreparationStatus.Faulted) && _workflowRunner.IsRunning) { string.Format("Chip preparation runner stopping. {0}.", BuildSnapshotSummary(snapshot)).LogProcessDebug(); await _workflowRunner.StopAsync().ConfigureAwait(false); } } catch (OperationCanceledException) { } finally { _runnerStateGate.Release(); } } private static ChipPreparationRequest CloneRequest(ChipPreparationRequest request) { if (request == null) { return null; } return new ChipPreparationRequest { RequestId = request.RequestId, RecipeName = request.RecipeName, Action = request.Action, SourceStepId = request.SourceStepId }; } private static ChipPreparationStateSnapshot CloneSnapshot(ChipPreparationStateSnapshot snapshot) { if (snapshot == null) { return null; } return new ChipPreparationStateSnapshot { Status = snapshot.Status, ActiveRequestId = snapshot.ActiveRequestId, RecipeName = snapshot.RecipeName, SourceStepId = snapshot.SourceStepId, CurrentStage = snapshot.CurrentStage, ErrorMessage = snapshot.ErrorMessage, LastUpdatedTime = snapshot.LastUpdatedTime, CurrentAction = snapshot.CurrentAction, IsChipLoaded = snapshot.IsChipLoaded }; } private static int? ResolveVisionAlarmId(ChipPreparationWaitResult executeResult) { if (executeResult == null) { return null; } if (!string.IsNullOrWhiteSpace(executeResult.ErrorMessage) && executeResult.ErrorMessage.IndexOf("Vision", StringComparison.OrdinalIgnoreCase) >= 0) { return VisionAlarmIds.TemplateMatchFailed; } return null; } private static string BuildRequestSummary(ChipPreparationRequest request) { if (request == null) { return "RequestId=N/A, Action=None, RecipeName=N/A, SourceStepId=N/A"; } return string.Format( "RequestId={0}, Action={1}, RecipeName={2}, SourceStepId={3}", NormalizeLogValue(request.RequestId), request.Action, NormalizeLogValue(request.RecipeName), NormalizeLogValue(request.SourceStepId)); } private static string BuildSnapshotSummary(ChipPreparationStateSnapshot snapshot) { if (snapshot == null) { return "Status=N/A, ActiveRequestId=N/A, CurrentAction=None, SourceStepId=N/A, IsChipLoaded=False"; } return string.Format( "Status={0}, ActiveRequestId={1}, CurrentAction={2}, SourceStepId={3}, IsChipLoaded={4}", snapshot.Status, NormalizeLogValue(snapshot.ActiveRequestId), snapshot.CurrentAction, NormalizeLogValue(snapshot.SourceStepId), snapshot.IsChipLoaded); } private static string BuildAlarmTriggerSummary(ChipPreparationRequest request, ChipPreparationWaitResult executeResult) { return string.Format( "{0}, ErrorMessage={1}", BuildRequestSummary(request), executeResult == null ? "N/A" : NormalizeLogValue(executeResult.ErrorMessage)); } private static string NormalizeLogValue(string value) { return string.IsNullOrWhiteSpace(value) ? "N/A" : value; } } public class ChipPreparationExecuteActivity : ActivityAbstractBase { private readonly ChipPreparationRequest _request; public ChipPreparationExecuteActivity(string name, ChipPreparationRequest request) : base(name) { _request = request; } protected override async Task OnExecuteAsync(WorkflowContext context, ActivityControl activityControl) { if (_request == null) { return ActivityResult.Failure; } for (int i = 0; i < 3; i++) { activityControl.ThrowIfCancellationRequested(); await activityControl.CheckPauseAsync(); await Task.Delay(300, activityControl.CancellationToken); } return ActivityResult.Success; } } }