using MainShell.Common; using MainShell.Common.Display.ViewModel; using MainShell.EventArgsFolder; using MainShell.Models; using MainShell.Process; using MainShell.ProcessResult; using MainShell.Recipe.Models; using MainShell.Resources.CustomControl; using MaxwellFramework.Core.Interfaces; using MwFramework.AlarmManager; using MW.WorkFlow; using Stylet; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; namespace MainShell { public class MainViewModel : BaseScreen, IPage, IHandle { public string Name { get; set; } = "Home"; private readonly IAlarmManager _alarmManager; private readonly RecipeManager _recipeManager; private readonly ProcessResultManager _processResultManager; private readonly IEventAggregator _eventAggregator; private readonly FlowStageMappingConfig _flowStageMappingConfig; private readonly WaferHandlingFlowConfig _waferHandlingFlowConfig; private readonly WorkflowRunner _runner; private readonly AutoProductionWorkflowBuilder _autoProductionWorkflowBuilder; private readonly PreparationAreaService _preparationAreaService; private bool _suspendDashboardPersist; private CancellationTokenSource _simulateCts; private bool _simulateFaultRequested; private string _currentSimulatingFlowName; private bool _isProcessPaused; public WorkflowRunner Runner => _runner; public bool IsProcessPaused { get { return _isProcessPaused; } private set { SetAndNotify(ref _isProcessPaused, value); NotifyOfPropertyChange(nameof(CanPauseProcess)); NotifyOfPropertyChange(nameof(CanResumeProcess)); } } public bool CanPauseProcess { get { return Runner.IsRunning && !IsProcessPaused; } } public bool CanResumeProcess { get { return Runner.IsRunning && IsProcessPaused; } } private static readonly Brush GrayBrush = new SolidColorBrush(Color.FromRgb(128, 128, 128)); private static readonly Brush RunningBrush = new SolidColorBrush(Color.FromRgb(34, 197, 94)); private static readonly Brush PausedBrush = new SolidColorBrush(Color.FromRgb(245, 158, 11)); private static readonly Brush FaultedBrush = new SolidColorBrush(Color.FromRgb(239, 68, 68)); private static readonly Brush CompletedBrush = new SolidColorBrush(Color.FromRgb(59, 130, 246)); public CameraViewModel CameraSearchViewModel { get; } public CameraViewModel CameraLocateViewModel { get; } public CameraViewModel CameraInspectViewModel { get; } public DieMapModel SubstrateMapModel { get; } = new DieMapModel(); public DieMapModel WaferMapModel { get; } = new DieMapModel(); public ProductionDashboardState DashboardState => _processResultManager.DashboardState; public ObservableCollection FlowNodes { get; } = new ObservableCollection(); private readonly Dictionary _flowNodeMap = new Dictionary(StringComparer.OrdinalIgnoreCase); public ObservableCollection WaferFlowNodes { get; } = new ObservableCollection(); private readonly Dictionary _waferFlowNodeMap = new Dictionary(StringComparer.OrdinalIgnoreCase); private string _selectedFlowName; public string SelectedFlowName { get => _selectedFlowName; set => SetAndNotify(ref _selectedFlowName, value); } private string _currentFlowName = "未运行"; public string CurrentFlowName { get => _currentFlowName; set => SetAndNotify(ref _currentFlowName, value); } private Brush _loadStageBrush = GrayBrush; public Brush LoadStageBrush { get => _loadStageBrush; set => SetAndNotify(ref _loadStageBrush, value); } private Brush _alignStageBrush = GrayBrush; public Brush AlignStageBrush { get => _alignStageBrush; set => SetAndNotify(ref _alignStageBrush, value); } private Brush _bondStageBrush = GrayBrush; public Brush BondStageBrush { get => _bondStageBrush; set => SetAndNotify(ref _bondStageBrush, value); } private Brush _inspectStageBrush = GrayBrush; public Brush InspectStageBrush { get => _inspectStageBrush; set => SetAndNotify(ref _inspectStageBrush, value); } private Brush _unloadStageBrush = GrayBrush; public Brush UnloadStageBrush { get => _unloadStageBrush; set => SetAndNotify(ref _unloadStageBrush, value); } private string _substrateRecipe = "-"; public string SubstrateRecipe { get => _substrateRecipe; set => SetAndNotify(ref _substrateRecipe, value); } private string _chipRecipe = "-"; public string ChipRecipe { get => _chipRecipe; set => SetAndNotify(ref _chipRecipe, value); } private int _rejectCount = 12; public int RejectCount { get => _rejectCount; set => SetAndNotify(ref _rejectCount, value); } public MainViewModel( IAlarmManager alarmManager, RecipeManager recipeManager, ProcessResultManager processResultManager, IEventAggregator eventAggregator, WorkflowRunner runner, AutoProductionWorkflowBuilder autoProductionWorkflowBuilder, PreparationAreaService preparationAreaService) { _alarmManager = alarmManager; _recipeManager = recipeManager; _processResultManager = processResultManager; _eventAggregator = eventAggregator; _runner = runner; _autoProductionWorkflowBuilder = autoProductionWorkflowBuilder; _preparationAreaService = preparationAreaService ?? throw new ArgumentNullException(nameof(preparationAreaService)); _flowStageMappingConfig = FlowStageMappingConfig.LoadOrCreate(); _waferHandlingFlowConfig = WaferHandlingFlowConfig.LoadOrCreate(); CameraSearchViewModel = new CameraViewModel(_eventAggregator, CameraType.TopPositionCamera); CameraLocateViewModel = new CameraViewModel(_eventAggregator, CameraType.MapCamera); CameraInspectViewModel = new CameraViewModel(_eventAggregator, CameraType.TopWideCamera); _eventAggregator.Subscribe(this); _recipeManager.RecipesChanged += OnRecipesChanged; DashboardState.PropertyChanged += OnDashboardStatePropertyChanged; _runner.PropertyChanged += OnRunnerPropertyChanged; InitializeFlowNodes(); InitializeWaferFlowNodes(); InitMapData(); RefreshRecipeBindings(); RefreshFlowBindings(); RestoreDashboardState(); } private void OnRunnerPropertyChanged(object sender, PropertyChangedEventArgs e) { if (!string.Equals(e.PropertyName, nameof(WorkflowRunner.IsRunning), StringComparison.Ordinal)) { return; } if (!Runner.IsRunning) { IsProcessPaused = false; } NotifyOfPropertyChange(nameof(CanPauseProcess)); NotifyOfPropertyChange(nameof(CanResumeProcess)); } private void OnDashboardStatePropertyChanged(object sender, PropertyChangedEventArgs e) { PersistDashboardState(); } private void RestoreDashboardState() { var s = _processResultManager.DashboardState; _suspendDashboardPersist = true; try { if (!string.IsNullOrWhiteSpace(s.ProductId)) DashboardState.ProductId = s.ProductId; if (!string.IsNullOrWhiteSpace(s.ChipId)) DashboardState.ChipId = s.ChipId; DashboardState.ProcessedSubstrateTotal = s.ProcessedSubstrateTotal; DashboardState.PendingCount = s.PendingCount; DashboardState.ProcessedCount = s.ProcessedCount; DashboardState.RealTimeUph = s.RealTimeUph; DashboardState.YieldUph = s.YieldUph; DashboardState.MappingProgress = s.MappingProgress; } finally { _suspendDashboardPersist = false; } } private void PersistDashboardState() { if (_suspendDashboardPersist) return; var s = _processResultManager.DashboardState; s.ProductId = DashboardState.ProductId; s.ChipId = DashboardState.ChipId; s.ProcessedSubstrateTotal = DashboardState.ProcessedSubstrateTotal; s.PendingCount = DashboardState.PendingCount; s.ProcessedCount = DashboardState.ProcessedCount; s.RealTimeUph = DashboardState.RealTimeUph; s.YieldUph = DashboardState.YieldUph; s.MappingProgress = DashboardState.MappingProgress; _processResultManager.SaveDashboardState(); } private void InitializeFlowNodes() { FlowNodes.Clear(); _flowNodeMap.Clear(); var flowDisplayNameMap = new Dictionary(StringComparer.OrdinalIgnoreCase) { { ProcessFlowName.SubstrateLoadFlow, "基板上料" }, { ProcessFlowName.SubstratePositionFlow, "基板定位" }, { ProcessFlowName.ChipStraighteningFlow, "芯片拉直" }, { ProcessFlowName.DiePositionFlow, "芯片定位" }, { ProcessFlowName.DieTransferFlow, "芯片转移" }, { ProcessFlowName.DieRecheckFlow, "精度复检" }, { ProcessFlowName.SubstrateUnloadFlow, "基板下料" } }; var flows = _flowStageMappingConfig.Rules .Where(r => !string.IsNullOrWhiteSpace(r.WorkflowName)) .Where(r => !string.Equals(r.WorkflowName, ProcessFlowName.AutoProduction, StringComparison.OrdinalIgnoreCase)) .GroupBy(r => r.WorkflowName, StringComparer.OrdinalIgnoreCase) .Select(g => g.First()) .OrderBy(r => r.Stage) .ThenBy(r => r.WorkflowName) .ToList(); foreach (var f in flows) { var name = flowDisplayNameMap.ContainsKey(f.WorkflowName) ? flowDisplayNameMap[f.WorkflowName] : f.WorkflowName; var node = new FlowNodeItem { FlowName = f.WorkflowName, DisplayName = name, Brush = GrayBrush }; FlowNodes.Add(node); _flowNodeMap[f.WorkflowName] = node; } if (FlowNodes.Count > 0) { SelectedFlowName = FlowNodes[0].FlowName; } } private void InitializeWaferFlowNodes() { WaferFlowNodes.Clear(); _waferFlowNodeMap.Clear(); var nodes = _waferHandlingFlowConfig.Nodes; foreach (var node in nodes) { AddWaferFlowNode(node.Key, node.DisplayName); } } private void AddWaferFlowNode(string key, string displayName) { var node = new FlowNodeItem { FlowName = key, DisplayName = displayName, Brush = GrayBrush }; WaferFlowNodes.Add(node); _waferFlowNodeMap[key] = node; } private void OnRecipesChanged(object sender, EventArgs e) { Execute.OnUIThread(RefreshRecipeBindings); } private void RefreshRecipeBindings() { SubstrateRecipe = _recipeManager.CurrentSubstrateRecipe != null ? _recipeManager.CurrentSubstrateRecipe.RecipeName : "-"; ChipRecipe = _recipeManager.CurrentWaferRecipe != null ? _recipeManager.CurrentWaferRecipe.RecipeName : "-"; } private void RefreshFlowBindings() { var flow = _processResultManager.FlowState; var displayFlow = _flowStageMappingConfig.ResolveDisplayFlowName(flow.WorkflowName, flow.ParentActivityName, flow.CurrentActivityName); CurrentFlowName = string.IsNullOrWhiteSpace(displayFlow) ? "未运行" : displayFlow; ProcessExecutionStatus status; if (!Enum.TryParse(flow.Status, true, out status)) { status = ProcessExecutionStatus.Unknown; } UpdateFlowVisual(status, flow.WorkflowName, flow.ParentActivityName, flow.CurrentActivityName); } private void UpdateFlowVisual(ProcessExecutionStatus status, string workflowName, string parentFlowName, string activityName) { LoadStageBrush = GrayBrush; AlignStageBrush = GrayBrush; BondStageBrush = GrayBrush; InspectStageBrush = GrayBrush; UnloadStageBrush = GrayBrush; var stage = _flowStageMappingConfig.Resolve(workflowName, parentFlowName, activityName); var stageIndex = (int)stage; if (stageIndex > 0) LoadStageBrush = CompletedBrush; if (stageIndex > 1) AlignStageBrush = CompletedBrush; if (stageIndex > 2) BondStageBrush = CompletedBrush; if (stageIndex > 3) InspectStageBrush = CompletedBrush; Brush currentBrush = GrayBrush; switch (status) { case ProcessExecutionStatus.Running: currentBrush = RunningBrush; break; case ProcessExecutionStatus.Paused: case ProcessExecutionStatus.Canceled: currentBrush = PausedBrush; break; case ProcessExecutionStatus.Faulted: currentBrush = FaultedBrush; break; case ProcessExecutionStatus.Completed: currentBrush = CompletedBrush; break; } switch (stageIndex) { case 0: LoadStageBrush = currentBrush; break; case 1: AlignStageBrush = currentBrush; break; case 2: BondStageBrush = currentBrush; break; case 3: InspectStageBrush = currentBrush; break; case 4: UnloadStageBrush = currentBrush; break; } } public void Handle(ProcessFlowStateChangedEventArgs message) { Execute.OnUIThread(() => { var displayFlow = _flowStageMappingConfig.ResolveDisplayFlowName(message.WorkflowName, message.ParentActivityName, message.CurrentActivityName); CurrentFlowName = string.IsNullOrWhiteSpace(displayFlow) ? "未运行" : displayFlow; UpdateFlowVisual(message.Status, message.WorkflowName, message.ParentActivityName, message.CurrentActivityName); UpdateFlowNodes(message); UpdateWaferFlowNodes(message); }); } private void UpdateFlowNodes(ProcessFlowStateChangedEventArgs message) { foreach (var node in FlowNodes) { node.Brush = GrayBrush; } var activeFlow = ResolveActiveMainFlowKey(message); if (string.IsNullOrWhiteSpace(activeFlow) || !_flowNodeMap.ContainsKey(activeFlow)) return; _flowNodeMap[activeFlow].Brush = ResolveStatusBrush(message.Status); } private string ResolveActiveMainFlowKey(ProcessFlowStateChangedEventArgs message) { if (!string.IsNullOrWhiteSpace(message.WorkflowName) && _flowNodeMap.ContainsKey(message.WorkflowName)) { return message.WorkflowName; } if (!string.IsNullOrWhiteSpace(message.ParentActivityName) && _flowNodeMap.ContainsKey(message.ParentActivityName)) { return message.ParentActivityName; } var displayFlow = _flowStageMappingConfig.ResolveDisplayFlowName(message.WorkflowName, message.ParentActivityName, message.CurrentActivityName); if (!string.IsNullOrWhiteSpace(displayFlow) && _flowNodeMap.ContainsKey(displayFlow)) { return displayFlow; } return null; } private void UpdateWaferFlowNodes(ProcessFlowStateChangedEventArgs message) { foreach (var node in WaferFlowNodes) { node.Brush = GrayBrush; } var activeFlow = ResolveActiveWaferFlowKey(message); if (string.IsNullOrWhiteSpace(activeFlow) || !_waferFlowNodeMap.ContainsKey(activeFlow)) return; _waferFlowNodeMap[activeFlow].Brush = ResolveStatusBrush(message.Status); } private string ResolveActiveWaferFlowKey(ProcessFlowStateChangedEventArgs message) { return _waferHandlingFlowConfig.ResolveNodeKey( message.WorkflowName, message.ParentActivityName, message.CurrentActivityName); } private static Brush ResolveStatusBrush(ProcessExecutionStatus status) { switch (status) { case ProcessExecutionStatus.Running: return RunningBrush; case ProcessExecutionStatus.Paused: case ProcessExecutionStatus.Canceled: return PausedBrush; case ProcessExecutionStatus.Faulted: return FaultedBrush; case ProcessExecutionStatus.Completed: return CompletedBrush; default: return GrayBrush; } } public async System.Threading.Tasks.Task StartProcess() { if (Runner.IsRunning) return; if (_simulateCts != null) return; IsProcessPaused = false; WorkflowStartRequest workflowStartRequest = _autoProductionWorkflowBuilder.Create(SelectedFlowName); try { WorkflowRunCompletedEventArgs completedArgs = await Runner.RunWithResultAsync( workflowStartRequest.Definition, workflowStartRequest.Context, workflowStartRequest.StartStepId); if (completedArgs.FinalState == WorkflowState.Faulted) { ShowWorkflowFailureDialog(completedArgs); } } catch (Exception ex) { LocalizedMessageBox.ShowFormat( MessageKey.ProcessFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, ex.Message ?? LanguageResourceHelper.GetString(MessageKey.CommonUnknownError)); } finally { IsProcessPaused = false; } } public void PauseProcess() { if (!Runner.IsRunning || IsProcessPaused || _simulateCts != null) { return; } Runner.Pause(); _preparationAreaService.Pause(); UpdateCurrentProcessStatus(ProcessExecutionStatus.Paused); IsProcessPaused = true; } public void ResumeProcess() { if (!Runner.IsRunning || !IsProcessPaused || _simulateCts != null) { return; } _preparationAreaService.Resume(); Runner.Resume(); UpdateCurrentProcessStatus(ProcessExecutionStatus.Running); IsProcessPaused = false; } private void ShowWorkflowFailureDialog(WorkflowRunCompletedEventArgs completedArgs) { if (completedArgs == null) { return; } object[] failureMessageArguments = completedArgs.FailureMessageArguments ?? Array.Empty(); if (completedArgs.FailureMessageKey != MessageKey.None) { if (failureMessageArguments.Length > 0) { LocalizedMessageBox.ShowFormat( completedArgs.FailureMessageKey, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, failureMessageArguments); } else { LocalizedMessageBox.Show( completedArgs.FailureMessageKey, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error); } return; } string failureMessage = string.IsNullOrWhiteSpace(completedArgs.FailureMessage) ? LanguageResourceHelper.GetString(MessageKey.CommonUnknownError) : completedArgs.FailureMessage; LocalizedMessageBox.ShowFormat( MessageKey.ProcessFailedWithReason, MessageKey.TitleError, MessageBoxButton.OK, MessageBoxImage.Error, failureMessage); } public async System.Threading.Tasks.Task StopProcess() { if (_simulateCts != null) { if (!string.IsNullOrWhiteSpace(_currentSimulatingFlowName)) { _eventAggregator.Publish(new ProcessFlowStateChangedEventArgs { WorkflowName = _currentSimulatingFlowName, ParentActivityName = _currentSimulatingFlowName, CurrentActivityName = _currentSimulatingFlowName, Status = ProcessExecutionStatus.Canceled }); } _simulateCts.Cancel(); } await _preparationAreaService.CancelAsync(CancellationToken.None); await Runner.StopAsync(); IsProcessPaused = false; } private void UpdateCurrentProcessStatus(ProcessExecutionStatus status) { ProcessFlowState flowState = _processResultManager.FlowState; string workflowName = string.IsNullOrWhiteSpace(flowState.WorkflowName) ? ProcessFlowName.AutoProduction : flowState.WorkflowName; _processResultManager.UpdateFlowState( workflowName, flowState.ParentActivityName, flowState.CurrentActivityName, status, flowState.ErrorMessage); } public void TriggerSimulateFault() { _simulateFaultRequested = true; } private async Task SimulateFlowRuntimeAsync() { _simulateCts = new CancellationTokenSource(); try { var token = _simulateCts.Token; foreach (var node in FlowNodes) { token.ThrowIfCancellationRequested(); _currentSimulatingFlowName = node.FlowName; _eventAggregator.Publish(new ProcessFlowStateChangedEventArgs { WorkflowName = node.FlowName, ParentActivityName = node.FlowName, CurrentActivityName = node.FlowName, Status = ProcessExecutionStatus.Running }); await Task.Delay(700, token); if (_simulateFaultRequested) { _eventAggregator.Publish(new ProcessFlowStateChangedEventArgs { WorkflowName = node.FlowName, ParentActivityName = node.FlowName, CurrentActivityName = node.FlowName, Status = ProcessExecutionStatus.Faulted, ErrorMessage = "模拟流程异常" }); return; } _eventAggregator.Publish(new ProcessFlowStateChangedEventArgs { WorkflowName = node.FlowName, ParentActivityName = node.FlowName, CurrentActivityName = node.FlowName, Status = ProcessExecutionStatus.Completed }); await Task.Delay(250, token); } } catch (OperationCanceledException) { if (!string.IsNullOrWhiteSpace(_currentSimulatingFlowName)) { _eventAggregator.Publish(new ProcessFlowStateChangedEventArgs { WorkflowName = _currentSimulatingFlowName, ParentActivityName = _currentSimulatingFlowName, CurrentActivityName = _currentSimulatingFlowName, Status = ProcessExecutionStatus.Canceled }); } } finally { _currentSimulatingFlowName = null; _simulateFaultRequested = false; _simulateCts.Dispose(); _simulateCts = null; } } private void ResetFlowVisuals() { foreach (var node in FlowNodes) { node.Brush = GrayBrush; } foreach (var node in WaferFlowNodes) { node.Brush = GrayBrush; } LoadStageBrush = GrayBrush; AlignStageBrush = GrayBrush; BondStageBrush = GrayBrush; InspectStageBrush = GrayBrush; UnloadStageBrush = GrayBrush; CurrentFlowName = "未运行"; } public void ClearStatistics() { _suspendDashboardPersist = true; DashboardState.ProcessedSubstrateTotal = 0; DashboardState.RealTimeUph = 0; DashboardState.YieldUph = 0; DashboardState.MappingProgress = 0; DashboardState.ProcessedCount = 0; _suspendDashboardPersist = false; PersistDashboardState(); } private void InitMapData() { SubstrateMapModel.Initialize(36, 36); WaferMapModel.Initialize(36, 36); var rand = new Random(); for (int r = 0; r < 36; r++) { for (int c = 0; c < 36; c++) { var dist = Math.Sqrt(Math.Pow(r - 18, 2) + Math.Pow(c - 18, 2)); if (dist > 17) { WaferMapModel.SetDieState(r, c, DieState.NotExist); } else { var v = rand.Next(100); if (v < 3) WaferMapModel.SetDieState(r, c, DieState.Error); else if (v < 40) WaferMapModel.SetDieState(r, c, DieState.Used); else WaferMapModel.SetDieState(r, c, DieState.Available); } var sv = rand.Next(100); if (sv < 2) SubstrateMapModel.SetDieState(r, c, DieState.Error); else if (sv < 45) SubstrateMapModel.SetDieState(r, c, DieState.Used); else SubstrateMapModel.SetDieState(r, c, DieState.Available); } } SubstrateMapModel.SetDieState(16, 20, DieState.Target); WaferMapModel.SetDieState(14, 14, DieState.Current); } protected override void OnViewLoaded() { base.OnViewLoaded(); } public class FlowNodeItem : PropertyChangedBase { private Brush _brush; public string FlowName { get; set; } public string DisplayName { get; set; } public Brush Brush { get => _brush; set => SetAndNotify(ref _brush, value); } } } }