795 lines
29 KiB
C#
795 lines
29 KiB
C#
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<ProcessFlowStateChangedEventArgs>
|
|
{
|
|
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<FlowNodeItem> FlowNodes { get; } = new ObservableCollection<FlowNodeItem>();
|
|
private readonly Dictionary<string, FlowNodeItem> _flowNodeMap = new Dictionary<string, FlowNodeItem>(StringComparer.OrdinalIgnoreCase);
|
|
public ObservableCollection<FlowNodeItem> WaferFlowNodes { get; } = new ObservableCollection<FlowNodeItem>();
|
|
private readonly Dictionary<string, FlowNodeItem> _waferFlowNodeMap = new Dictionary<string, FlowNodeItem>(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<string, string>(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<object>();
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|