using MainShell.Common; using MainShell.ProcessResult; using MW.WorkFlow; using Stylet; using System; using System.Threading; using System.Threading.Tasks; namespace MainShell.Process { public enum ChipPreparationAction { None = 0, Load = 1, Unload = 2, Completed = 3, Prepare = 4 } public enum ChipPreparationStatus { Idle = 0, Preparing = 1, Prepared = 2, Paused = 3, Faulted = 4, Loading = 5, Loaded = 6, Unloading = 7 } public class ChipPreparationRequest { public string RequestId { get; set; } public string RecipeName { get; set; } public ChipPreparationAction Action { get; set; } = ChipPreparationAction.Prepare; public string SourceStepId { get; set; } } public class ChipPreparationWaitResult { public bool Success { get; set; } public bool IsTimeout { get; set; } public bool IsCanceled { get; set; } public string ErrorMessage { get; set; } public static ChipPreparationWaitResult CreateSuccess() { return new ChipPreparationWaitResult { Success = true }; } public static ChipPreparationWaitResult CreateTimeout(string errorMessage) { return new ChipPreparationWaitResult { IsTimeout = true, ErrorMessage = errorMessage }; } public static ChipPreparationWaitResult CreateCanceled(string errorMessage) { return new ChipPreparationWaitResult { IsCanceled = true, ErrorMessage = errorMessage }; } public static ChipPreparationWaitResult CreateFailure(string errorMessage) { return new ChipPreparationWaitResult { ErrorMessage = errorMessage }; } } public class ChipPreparationStateSnapshot { public ChipPreparationStatus Status { get; set; } public string ActiveRequestId { get; set; } public string RecipeName { get; set; } public string SourceStepId { get; set; } public string CurrentStage { get; set; } public string ErrorMessage { get; set; } public DateTime LastUpdatedTime { get; set; } public ChipPreparationAction CurrentAction { get; set; } public bool IsChipLoaded { get; set; } } public class ChipPreparationState : PropertyChangedBase { private ChipPreparationStatus _status; private string _activeRequestId; private string _recipeName; private string _sourceStepId; private string _currentStage; private string _errorMessage; private DateTime _lastUpdatedTime; private ChipPreparationAction _currentAction; private bool _isChipLoaded; public ChipPreparationStatus Status { get { return _status; } set { SetAndNotify(ref _status, value); } } public string ActiveRequestId { get { return _activeRequestId; } set { SetAndNotify(ref _activeRequestId, value); } } public string RecipeName { get { return _recipeName; } set { SetAndNotify(ref _recipeName, value); } } public string SourceStepId { get { return _sourceStepId; } set { SetAndNotify(ref _sourceStepId, value); } } public string CurrentStage { get { return _currentStage; } set { SetAndNotify(ref _currentStage, value); } } public string ErrorMessage { get { return _errorMessage; } set { SetAndNotify(ref _errorMessage, value); } } public DateTime LastUpdatedTime { get { return _lastUpdatedTime; } set { SetAndNotify(ref _lastUpdatedTime, value); } } public ChipPreparationAction CurrentAction { get { return _currentAction; } set { SetAndNotify(ref _currentAction, value); } } public bool IsChipLoaded { get { return _isChipLoaded; } set { SetAndNotify(ref _isChipLoaded, value); } } public bool IsBusy { get { return Status == ChipPreparationStatus.Preparing || Status == ChipPreparationStatus.Loading || Status == ChipPreparationStatus.Unloading; } } public bool IsReady { get { return Status == ChipPreparationStatus.Prepared || Status == ChipPreparationStatus.Loaded || (Status == ChipPreparationStatus.Idle && CurrentAction == ChipPreparationAction.Unload); } } public bool IsFaulted { get { return Status == ChipPreparationStatus.Faulted; } } public void ApplySnapshot(ChipPreparationStateSnapshot snapshot) { if (snapshot == null) { return; } 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; NotifyOfPropertyChange(nameof(IsBusy)); NotifyOfPropertyChange(nameof(IsReady)); NotifyOfPropertyChange(nameof(IsFaulted)); } } public class ChipPreparationRequestedEventArgs : EventArgs { public ChipPreparationRequestedEventArgs(ChipPreparationRequest request) { Request = request; } public ChipPreparationRequest Request { get; private set; } } public class ChipPreparationStateChangedEventArgs : EventArgs { public ChipPreparationStateChangedEventArgs(ChipPreparationStateSnapshot snapshot) { Snapshot = snapshot; } public ChipPreparationStateSnapshot Snapshot { get; private set; } } public interface IChipPreparationService { ChipPreparationState Current { get; } bool TryRequestPrepare(ChipPreparationRequest request, out string rejectReason); Task WaitUntilReadyAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)); Task WaitUntilLoadedAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)); Task EnsurePreparedAsync(ChipPreparationRequest request, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)); bool TryMarkReady(string requestId, out string rejectReason); bool TryMarkFault(string errorMessage, out string rejectReason); bool TryConsumeReady(out string rejectReason); Task CancelAsync(CancellationToken cancellationToken = default(CancellationToken)); void Pause(); void Resume(); bool TryResetFault(out string failureReason); } public class ChipPreparationService : IChipPreparationService { private readonly object _syncRoot = new object(); private readonly IEventAggregator _eventAggregator; private readonly ProcessResultManager _processResultManager; private ChipPreparationRequest _activeRequest; private ChipPreparationRequest _pendingLoadRequest; private ChipPreparationStateSnapshot _snapshot; public ChipPreparationService(IEventAggregator eventAggregator, ProcessResultManager processResultManager) { _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator)); _processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager)); _processResultManager.ReloadAll(); _snapshot = RestoreSnapshot(_processResultManager.PreparationAreaState); Current = new ChipPreparationState(); Current.ApplySnapshot(_snapshot); } public ChipPreparationState Current { get; private set; } public bool TryRequestPrepare(ChipPreparationRequest request, out string rejectReason) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (!TryValidateActionRequest(request, out rejectReason)) { return false; } if (string.IsNullOrWhiteSpace(request.RequestId)) { request.RequestId = Guid.NewGuid().ToString("N"); } ChipPreparationRequest requestToPublish = null; ChipPreparationStateSnapshot snapshotToPublish = null; lock (_syncRoot) { if (_snapshot.Status == ChipPreparationStatus.Faulted) { rejectReason = string.IsNullOrWhiteSpace(_snapshot.ErrorMessage) ? "芯片准备区当前处于故障状态。" : _snapshot.ErrorMessage; return false; } if (_snapshot.Status == ChipPreparationStatus.Paused) { rejectReason = "芯片准备区当前处于暂停状态。"; return false; } if (request.Action == ChipPreparationAction.Prepare) { if (_snapshot.Status == ChipPreparationStatus.Preparing || _snapshot.Status == ChipPreparationStatus.Prepared || _snapshot.Status == ChipPreparationStatus.Loading || _snapshot.Status == ChipPreparationStatus.Loaded) { rejectReason = string.Empty; return true; } if (_snapshot.Status == ChipPreparationStatus.Unloading) { rejectReason = "芯片准备区正在执行下料,不能发起准备。"; return false; } _activeRequest = CloneRequest(request); _pendingLoadRequest = null; _snapshot = CreateRunningSnapshot(_activeRequest, ChipPreparationStatus.Preparing, _snapshot.IsChipLoaded); snapshotToPublish = CloneSnapshot(_snapshot); requestToPublish = CloneRequest(_activeRequest); } else if (request.Action == ChipPreparationAction.Load) { if (_snapshot.Status == ChipPreparationStatus.Loaded) { rejectReason = string.Empty; return true; } if (_snapshot.Status == ChipPreparationStatus.Loading) { rejectReason = string.Empty; return true; } if (_snapshot.Status == ChipPreparationStatus.Preparing) { _pendingLoadRequest = CloneRequest(request); rejectReason = string.Empty; return true; } if (_snapshot.Status == ChipPreparationStatus.Unloading) { rejectReason = "芯片准备区正在执行下料,不能发起上料。"; return false; } _activeRequest = CloneRequest(request); _snapshot = CreateRunningSnapshot(_activeRequest, ChipPreparationStatus.Loading, _snapshot.IsChipLoaded); snapshotToPublish = CloneSnapshot(_snapshot); requestToPublish = CloneRequest(_activeRequest); } else { if (_snapshot.Status == ChipPreparationStatus.Unloading) { rejectReason = string.Empty; return true; } if (!_snapshot.IsChipLoaded && _snapshot.Status != ChipPreparationStatus.Loaded && _snapshot.Status != ChipPreparationStatus.Prepared) { rejectReason = "当前没有可执行下料的芯片。"; return false; } if (_snapshot.Status == ChipPreparationStatus.Preparing || _snapshot.Status == ChipPreparationStatus.Loading) { rejectReason = "芯片准备区正在执行准备或上料,不能发起下料。"; return false; } _activeRequest = CloneRequest(request); _pendingLoadRequest = null; _snapshot = CreateRunningSnapshot(_activeRequest, ChipPreparationStatus.Unloading, true); snapshotToPublish = CloneSnapshot(_snapshot); requestToPublish = CloneRequest(_activeRequest); } rejectReason = string.Empty; } PublishState(snapshotToPublish); if (requestToPublish != null) { _eventAggregator.Publish(new ChipPreparationRequestedEventArgs(requestToPublish)); } return true; } public async Task WaitUntilReadyAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)) { return await WaitForStateAsync(false, timeout, cancellationToken).ConfigureAwait(false); } public async Task WaitUntilLoadedAsync(TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)) { return await WaitForStateAsync(true, timeout, cancellationToken).ConfigureAwait(false); } public async Task EnsurePreparedAsync(ChipPreparationRequest request, TimeSpan timeout, CancellationToken cancellationToken = default(CancellationToken)) { string rejectReason; if (!TryRequestPrepare(request, out rejectReason)) { return ChipPreparationWaitResult.CreateFailure(rejectReason); } return await WaitUntilReadyAsync(timeout, cancellationToken).ConfigureAwait(false); } public bool TryMarkReady(string requestId, out string rejectReason) { ChipPreparationStateSnapshot snapshotToPublish = null; ChipPreparationRequest requestToPublish = null; lock (_syncRoot) { if (_activeRequest == null) { rejectReason = "当前没有正在执行的芯片准备请求。"; return false; } if (!string.IsNullOrWhiteSpace(requestId) && !string.Equals(_activeRequest.RequestId, requestId, StringComparison.OrdinalIgnoreCase)) { rejectReason = "芯片准备请求标识不匹配。"; return false; } if (_activeRequest.Action == ChipPreparationAction.Prepare) { if (_pendingLoadRequest != null) { _activeRequest = CloneRequest(_pendingLoadRequest); _pendingLoadRequest = null; _snapshot = CreateRunningSnapshot(_activeRequest, ChipPreparationStatus.Loading, true); snapshotToPublish = CloneSnapshot(_snapshot); requestToPublish = CloneRequest(_activeRequest); } else { _snapshot = CreateStableSnapshot(ChipPreparationStatus.Prepared, _activeRequest, true, "Prepared"); snapshotToPublish = CloneSnapshot(_snapshot); _activeRequest = null; } } else if (_activeRequest.Action == ChipPreparationAction.Load) { _snapshot = CreateStableSnapshot(ChipPreparationStatus.Loaded, _activeRequest, true, "LoadedPendingTransfer"); snapshotToPublish = CloneSnapshot(_snapshot); _activeRequest = null; } else if (_activeRequest.Action == ChipPreparationAction.Unload) { _snapshot = CreateIdleSnapshot(false); _snapshot.CurrentAction = ChipPreparationAction.Unload; _snapshot.CurrentStage = "UnloadCompleted"; snapshotToPublish = CloneSnapshot(_snapshot); _activeRequest = null; } else { rejectReason = "芯片准备请求动作无效。"; return false; } rejectReason = string.Empty; } PublishState(snapshotToPublish); if (requestToPublish != null) { _eventAggregator.Publish(new ChipPreparationRequestedEventArgs(requestToPublish)); } return true; } public bool TryMarkFault(string errorMessage, out string rejectReason) { ChipPreparationStateSnapshot snapshotToPublish; lock (_syncRoot) { if (_snapshot.Status == ChipPreparationStatus.Idle) { rejectReason = "当前没有执行中的芯片流程。"; return false; } _snapshot = CreateFaultedSnapshot(_activeRequest, errorMessage, _snapshot.IsChipLoaded); _activeRequest = null; _pendingLoadRequest = null; snapshotToPublish = CloneSnapshot(_snapshot); rejectReason = string.Empty; } PublishState(snapshotToPublish); return true; } public bool TryConsumeReady(out string rejectReason) { ChipPreparationStateSnapshot snapshotToPublish; lock (_syncRoot) { if (_snapshot.Status == ChipPreparationStatus.Loaded || _snapshot.Status == ChipPreparationStatus.Prepared) { ResetToIdle(false); snapshotToPublish = CloneSnapshot(_snapshot); rejectReason = string.Empty; } else if (_snapshot.Status == ChipPreparationStatus.Idle && _snapshot.CurrentAction == ChipPreparationAction.Unload) { _snapshot = CreateIdleSnapshot(false); snapshotToPublish = CloneSnapshot(_snapshot); rejectReason = string.Empty; } else { rejectReason = "当前没有可消费的准备结果。"; return false; } } PublishState(snapshotToPublish); return true; } public Task CancelAsync(CancellationToken cancellationToken = default(CancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); ChipPreparationStateSnapshot snapshotToPublish = null; lock (_syncRoot) { if (!IsTransientStatus(_snapshot.Status)) { return Task.CompletedTask; } bool keepLoadedChip = _snapshot.IsChipLoaded && _snapshot.Status != ChipPreparationStatus.Unloading; _activeRequest = null; _pendingLoadRequest = null; _snapshot = CreateIdleSnapshot(keepLoadedChip); snapshotToPublish = CloneSnapshot(_snapshot); } PublishState(snapshotToPublish); return Task.CompletedTask; } public void Pause() { ChipPreparationStateSnapshot snapshotToPublish = null; lock (_syncRoot) { if (!IsTransientStatus(_snapshot.Status)) { return; } _snapshot = CloneSnapshot(_snapshot); _snapshot.Status = ChipPreparationStatus.Paused; _snapshot.CurrentStage = "Paused"; _snapshot.LastUpdatedTime = DateTime.Now; snapshotToPublish = CloneSnapshot(_snapshot); } PublishState(snapshotToPublish); } public void Resume() { ChipPreparationStateSnapshot snapshotToPublish = null; lock (_syncRoot) { if (_snapshot.Status != ChipPreparationStatus.Paused || _activeRequest == null) { return; } ChipPreparationStatus resumedStatus = ResolveRunningStatus(_activeRequest.Action); _snapshot = CloneSnapshot(_snapshot); _snapshot.Status = resumedStatus; _snapshot.CurrentStage = ResolveStageForStatus(resumedStatus); _snapshot.LastUpdatedTime = DateTime.Now; snapshotToPublish = CloneSnapshot(_snapshot); } PublishState(snapshotToPublish); } public bool TryResetFault(out string failureReason) { ChipPreparationStateSnapshot snapshotToPublish; lock (_syncRoot) { if (_snapshot.Status != ChipPreparationStatus.Faulted) { failureReason = "当前芯片准备区未处于故障状态。"; return false; } ResetToIdle(_snapshot.IsChipLoaded); snapshotToPublish = CloneSnapshot(_snapshot); failureReason = string.Empty; } PublishState(snapshotToPublish); return true; } private async Task WaitForStateAsync(bool requireLoaded, TimeSpan timeout, CancellationToken cancellationToken) { if (timeout <= TimeSpan.Zero) { timeout = TimeSpan.FromMilliseconds(100); } DateTime deadline = DateTime.Now.Add(timeout); try { while (true) { cancellationToken.ThrowIfCancellationRequested(); ChipPreparationWaitResult immediateResult = EvaluateWaitResult(requireLoaded); if (immediateResult != null) { return immediateResult; } if (DateTime.Now >= deadline) { return ChipPreparationWaitResult.CreateTimeout(requireLoaded ? "等待芯片上料完成超时。" : "等待芯片准备完成超时。"); } await Task.Delay(50, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException) { return ChipPreparationWaitResult.CreateCanceled(requireLoaded ? "等待芯片上料已取消。" : "等待芯片准备已取消。"); } } private ChipPreparationWaitResult EvaluateWaitResult(bool requireLoaded) { lock (_syncRoot) { if (_snapshot.Status == ChipPreparationStatus.Faulted) { return ChipPreparationWaitResult.CreateFailure(string.IsNullOrWhiteSpace(_snapshot.ErrorMessage) ? "芯片准备区执行失败。" : _snapshot.ErrorMessage); } if (requireLoaded) { if (_snapshot.Status == ChipPreparationStatus.Loaded) { return ChipPreparationWaitResult.CreateSuccess(); } } else { if (_snapshot.Status == ChipPreparationStatus.Prepared || _snapshot.Status == ChipPreparationStatus.Loaded || (_snapshot.Status == ChipPreparationStatus.Idle && _snapshot.CurrentAction == ChipPreparationAction.Unload)) { return ChipPreparationWaitResult.CreateSuccess(); } } if (_snapshot.Status == ChipPreparationStatus.Idle && _activeRequest == null) { return ChipPreparationWaitResult.CreateFailure("尚未开始芯片准备流程。") ; } return null; } } private void ResetToIdle(bool keepLoadedChip) { _activeRequest = null; _pendingLoadRequest = null; _snapshot = CreateIdleSnapshot(keepLoadedChip); } private void PublishState(ChipPreparationStateSnapshot snapshot) { if (snapshot == null) { return; } Current.ApplySnapshot(snapshot); _processResultManager.SavePreparationAreaState(snapshot); _eventAggregator.Publish(new ChipPreparationStateChangedEventArgs(CloneSnapshot(snapshot))); } private static bool IsTransientStatus(ChipPreparationStatus status) { return status == ChipPreparationStatus.Preparing || status == ChipPreparationStatus.Loading || status == ChipPreparationStatus.Unloading || status == ChipPreparationStatus.Paused; } private static bool TryValidateActionRequest(ChipPreparationRequest request, out string rejectReason) { if (request == null) { rejectReason = "请求不能为空。"; return false; } if (request.Action != ChipPreparationAction.Prepare && request.Action != ChipPreparationAction.Load && request.Action != ChipPreparationAction.Unload) { rejectReason = "芯片准备请求动作无效。"; return false; } rejectReason = string.Empty; return true; } private static ChipPreparationStatus ResolveRunningStatus(ChipPreparationAction action) { if (action == ChipPreparationAction.Prepare) { return ChipPreparationStatus.Preparing; } if (action == ChipPreparationAction.Load) { return ChipPreparationStatus.Loading; } return ChipPreparationStatus.Unloading; } private static string ResolveStageForStatus(ChipPreparationStatus status) { switch (status) { case ChipPreparationStatus.Preparing: return "Preparing"; case ChipPreparationStatus.Prepared: return "Prepared"; case ChipPreparationStatus.Loading: return "Loading"; case ChipPreparationStatus.Loaded: return "LoadedPendingTransfer"; case ChipPreparationStatus.Unloading: return "Unloading"; case ChipPreparationStatus.Paused: return "Paused"; case ChipPreparationStatus.Faulted: return "Faulted"; default: return "Idle"; } } private static ChipPreparationStateSnapshot CreateIdleSnapshot(bool isChipLoaded) { return new ChipPreparationStateSnapshot { Status = ChipPreparationStatus.Idle, CurrentStage = isChipLoaded ? "LoadedPendingTransfer" : "Idle", LastUpdatedTime = DateTime.Now, CurrentAction = ChipPreparationAction.None, IsChipLoaded = isChipLoaded }; } private static ChipPreparationStateSnapshot CreateRunningSnapshot(ChipPreparationRequest request, ChipPreparationStatus status, bool isChipLoaded) { return new ChipPreparationStateSnapshot { Status = status, ActiveRequestId = request != null ? request.RequestId : null, RecipeName = request != null ? request.RecipeName : null, SourceStepId = request != null ? request.SourceStepId : null, CurrentStage = ResolveStageForStatus(status), LastUpdatedTime = DateTime.Now, CurrentAction = request != null ? request.Action : ChipPreparationAction.None, IsChipLoaded = isChipLoaded }; } private static ChipPreparationStateSnapshot CreateStableSnapshot(ChipPreparationStatus status, ChipPreparationRequest request, bool isChipLoaded, string stage) { return new ChipPreparationStateSnapshot { Status = status, ActiveRequestId = request != null ? request.RequestId : null, RecipeName = request != null ? request.RecipeName : null, SourceStepId = request != null ? request.SourceStepId : null, CurrentStage = stage, LastUpdatedTime = DateTime.Now, CurrentAction = request != null ? request.Action : ChipPreparationAction.None, IsChipLoaded = isChipLoaded }; } private static ChipPreparationStateSnapshot CreateFaultedSnapshot(ChipPreparationRequest request, string errorMessage, bool isChipLoaded) { return new ChipPreparationStateSnapshot { Status = ChipPreparationStatus.Faulted, ActiveRequestId = request != null ? request.RequestId : null, RecipeName = request != null ? request.RecipeName : null, SourceStepId = request != null ? request.SourceStepId : null, CurrentStage = "Faulted", ErrorMessage = string.IsNullOrWhiteSpace(errorMessage) ? "芯片准备区执行失败。" : errorMessage, LastUpdatedTime = DateTime.Now, CurrentAction = request != null ? request.Action : ChipPreparationAction.None, IsChipLoaded = isChipLoaded }; } 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 ChipPreparationStateSnapshot RestoreSnapshot(PreparationAreaProcessState persistedState) { if (persistedState == null) { return CreateIdleSnapshot(false); } return new ChipPreparationStateSnapshot { Status = persistedState.Status, ActiveRequestId = persistedState.ActiveRequestId, RecipeName = persistedState.RecipeName, SourceStepId = persistedState.SourceStepId, CurrentStage = string.IsNullOrWhiteSpace(persistedState.CurrentStage) ? ResolveStageForStatus(persistedState.Status) : persistedState.CurrentStage, ErrorMessage = persistedState.ErrorMessage, LastUpdatedTime = persistedState.LastUpdatedTime == default(DateTime) ? DateTime.Now : persistedState.LastUpdatedTime, CurrentAction = persistedState.CurrentAction, IsChipLoaded = persistedState.HasPreparedChip }; } } }