添加 MX-PD-盘古 项目文件

将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
This commit is contained in:
Shi.Ji
2026-05-18 11:43:09 +08:00
parent 03632a379d
commit e31d3560bb
739 changed files with 99783 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
using MainShell.Common;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Recipe.Models;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class ChipStraighteningViewModel : OperateViewModelBase,IHandle<ChipAngleEventArgs>
{
private double _chipAngle;
public double ChipAngle
{
get { return _chipAngle; }
set { SetAndNotify(ref _chipAngle, value); }
}
private readonly HardwareManager _hardwareManager;
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
public ChipStraighteningViewModel(HardwareManager hardwareManager, IEventAggregator eventAggregator, RecipeManager recipeManager)
{
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
eventAggregator.Subscribe(this);
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = _hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
// 手动启动流程
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
await RunManualActivityAsync(new MainShell.Process.ChipStraighteningActivity("ChipStraightening"), context);
}
public override async Task StopProcess()
{
await base.StopProcess();
}
public void Handle(ChipAngleEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
ChipAngle = message.Angle;
});
}
}
}

View File

@@ -0,0 +1,246 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Models.Wafer;
using MainShell.ParaSetting.Model;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.BaseBoard.Model;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MaxwellFramework.Core.Interfaces;
using MwFramework.ManagerService;
using Stylet;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class DieBondingViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
private readonly IDieTransferPathGenerator _dieTransferPathGenerator;
private List<DieTransferPathItem> _allDieTransferPathItems = new List<DieTransferPathItem>();
private ObservableCollection<DieTransferPathItem> _dieTransferPathItems = new ObservableCollection<DieTransferPathItem>();
/// <summary>
/// 界面上显示的传输路径集合
/// </summary>
public ObservableCollection<DieTransferPathItem> DieTransferPathItems
{
get { return _dieTransferPathItems; }
set { SetAndNotify(ref _dieTransferPathItems, value); }
}
private DieTransferPathItem _selectedDieTransferPathItem;
public DieTransferPathItem SelectedDieTransferPathItem
{
get => _selectedDieTransferPathItem;
set => SetAndNotify(ref _selectedDieTransferPathItem, value);
}
private readonly DieBondingManualSetting _dieBondingManualSetting;
public DieBondingManualSysItem DieBondingManualSysItem => _dieBondingManualSetting.DieBondingManualSysItem;
public PaginationViewModel Pagination { get; } = new PaginationViewModel();
public DieBondingViewModel(IEventAggregator eventAggregator, RecipeManager recipeManager,
ProcessResultManager processResultManager, HardwareManager hardwareManager, IDieTransferPathGenerator dieTransferPathGenerator, IParameterManager parameterManager)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_dieTransferPathGenerator = dieTransferPathGenerator ?? throw new ArgumentNullException(nameof(dieTransferPathGenerator));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
var _paramlist = parameterManager as IParamList;
_dieBondingManualSetting = _paramlist.GetParameter<DieBondingManualSetting>();
Pagination.PageChanged += (s, e) => LoadData();
LoadData();
}
private void LoadData()
{
// 如果数据源为空,清空并返回
if (_allDieTransferPathItems == null || !_allDieTransferPathItems.Any())
{
DieTransferPathItems = new ObservableCollection<DieTransferPathItem>();
Pagination.TotalItems = 0;
return;
}
// 1. 先更新总数这很重要因为TotalPages依赖它
Pagination.TotalItems = _allDieTransferPathItems.Count();
// 2. 自动修正页码:如果当前页超过了新的总页数,强制跳到最后一页
// 注意:修改 Pagination.CurrentPage 会再次触发 PageChanged -> LoadData
// 所以我们需要防止无限递归,或者只在必要时修正。
// 在这里,简单的方式是计算 offset 时进行钳制,而不强行修改 CurrentPage 属性(取决于你的需求)
// 或者更好的是:
if (Pagination.CurrentPage > Pagination.TotalItems && Pagination.TotalItems > 0)
{
// 如果这里修改 CurrentPage 触发 LoadData则本次 LoadData 可以直接中断
Pagination.CurrentPage = Pagination.TotalPages;
return;
}
// 3. 安全计算 Offset
int safePage = Math.Min(Pagination.CurrentPage, Pagination.TotalPages);
safePage = Math.Max(1, safePage); // 确保至少为1
int offset = (safePage - 1) * Pagination.PageSize;
int limit = Pagination.PageSize;
// 4. 重置集合
DieTransferPathItems = new ObservableCollection<DieTransferPathItem>(_allDieTransferPathItems.Skip(offset).Take(limit));
}
public async Task SaveDieBondingSetAsync()
{
bool success = await Loading.RunAsync(async (token, progress) =>
{
_dieBondingManualSetting.Write();
}, "保存参数...",isIndeterminate:true,canCancel:false);
}
public Task GenerateTransPathAsync()
{
DieTransferPathRequest pathRequest = CreatePathRequest();
DieTransferPathPlan pathPlan = _dieTransferPathGenerator.Generate(pathRequest);
_allDieTransferPathItems = ConvertToPathItems(pathPlan);
Pagination.CurrentPage = 1;
LoadData();
return Task.CompletedTask;
}
private DieTransferPathRequest CreatePathRequest()
{
DieTransferPathRequest pathRequest = new DieTransferPathRequest();
pathRequest.DieRegion = DieBondingManualSysItem.DieRegion;
pathRequest.SubstrateRegion = DieBondingManualSysItem.SubstrateRegion;
pathRequest.TransPathType = DieBondingManualSysItem.TransPathType;
pathRequest.PadRowDirectionStrategy = DieBondingManualSysItem.PadRowDirectionStrategy;
pathRequest.DieRowDirectionStrategy = DieBondingManualSysItem.DieRowDirectionStrategy;
pathRequest.SkipNgDie = true;
pathRequest.DieCandidates = CreateDieCandidates(DieBondingManualSysItem.DieRegion);
pathRequest.PadCandidates = CreatePadCandidates(DieBondingManualSysItem.SubstrateRegion);
return pathRequest;
}
private List<Die> CreateDieCandidates(RegionModel region)
{
List<Die> dieCandidates = new List<Die>();
if (region == null)
{
return dieCandidates;
}
for (int row = region.StartRow; row <= region.EndRow; row++)
{
for (int column = region.StartCol; column <= region.EndCol; column++)
{
Die die = new Die();
die.Row = row;
die.Column = column;
die.X = column;
die.Y = row;
die.Status = DieStatus.Normal;
dieCandidates.Add(die);
}
}
return dieCandidates;
}
private List<Pad> CreatePadCandidates(RegionModel region)
{
List<Pad> padCandidates = new List<Pad>();
if (region == null)
{
return padCandidates;
}
for (int row = region.StartRow; row <= region.EndRow; row++)
{
for (int column = region.StartCol; column <= region.EndCol; column++)
{
Pad pad = new Pad();
pad.Row = row;
pad.Column = column;
pad.X = column;
pad.Y = row;
SubstratePoint substratePoint;
if (TryGetSubstratePoint(row, column, out substratePoint))
{
pad.X = substratePoint.X;
pad.Y = substratePoint.Y;
}
padCandidates.Add(pad);
}
}
return padCandidates;
}
private bool TryGetSubstratePoint(int row, int column, out SubstratePoint substratePoint)
{
substratePoint = default(SubstratePoint);
SubstratePoint[,] substratePoints = _recipeManager.CurrentSubstrateRecipe != null
? _recipeManager.CurrentSubstrateRecipe.SubstratePoints
: null;
if (substratePoints == null)
{
return false;
}
int rowIndex = row - 1;
int columnIndex = column - 1;
if (rowIndex < 0 || rowIndex >= substratePoints.GetLength(0) || columnIndex < 0 || columnIndex >= substratePoints.GetLength(1))
{
return false;
}
substratePoint = substratePoints[rowIndex, columnIndex];
return true;
}
private static List<DieTransferPathItem> ConvertToPathItems(DieTransferPathPlan pathPlan)
{
List<DieTransferPathItem> pathItems = new List<DieTransferPathItem>();
if (pathPlan == null || pathPlan.Steps == null)
{
return pathItems;
}
foreach (DieTransferPathStep pathStep in pathPlan.Steps)
{
DieTransferPathItem pathItem = DieTransferPathItem.FromPathStep(pathStep);
if (pathItem != null)
{
pathItems.Add(pathItem);
}
}
return pathItems;
}
}
}

View File

@@ -0,0 +1,182 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using System;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class DieStatisticsModel : PropertyChangedBase
{
private int _totalDieCount;
public int TotalDieCount
{
get => _totalDieCount;
set
{
if (SetAndNotify(ref _totalDieCount, value))
{
UpdatePassRate();
}
}
}
private int _okDieCount;
public int OkDieCount
{
get => _okDieCount;
set
{
if (SetAndNotify(ref _okDieCount, value))
{
UpdatePassRate();
}
}
}
private int _ngDieCount;
public int NgDieCount
{
get => _ngDieCount;
set => SetAndNotify(ref _ngDieCount, value);
}
private double _averageSpacingX;
public double AverageSpacingX
{
get => _averageSpacingX;
set => SetAndNotify(ref _averageSpacingX, value);
}
private double _averageSpacingY;
public double AverageSpacingY
{
get => _averageSpacingY;
set => SetAndNotify(ref _averageSpacingY, value);
}
private double _passRate;
public double PassRate
{
get => _passRate;
private set => SetAndNotify(ref _passRate, value);
}
private void UpdatePassRate()
{
PassRate = TotalDieCount > 0 ? (double)OkDieCount / TotalDieCount * 100 : 0;
}
}
public class DiePositionViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator;
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
private readonly DiePositionService _diePositionService;
private DieStatisticsModel _statistics = new DieStatisticsModel();
public DieStatisticsModel Statistics
{
get => _statistics;
set => SetAndNotify(ref _statistics, value);
}
private DieMapModel _dieMapModel = new DieMapModel();
public DieMapModel DieMapModel
{
get => _dieMapModel;
set => SetAndNotify(ref _dieMapModel, value);
}
public DiePositionViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
IParameterManager parameterManager,
DiePositionService diePositionService)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_diePositionService = diePositionService ?? throw new ArgumentNullException(nameof(diePositionService));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
InitTestMap();
}
private void InitTestMap()
{
DieMapModel.Initialize(3, 3);
for (int r = 0; r < 3; r++)
{
for (int c = 0; c < 3; c++)
{
DieMapModel.SetDieState(r, c, DieState.Available);
}
}
DieMapModel.SetDieState(1, 1, DieState.Current);
}
public async Task StartProcess()
{
MW.WorkFlow.WorkflowContext context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DiePositionFlow;
var result = await RunManualActivityAsync(
new DiePositionActivity(ProcessFlowName.DiePositionFlow, _diePositionService),
context);
if (!IsWorkflowCompleted(result))
{
return;
}
DiePositionProcessResult processResult = context.GetData<DiePositionProcessResult>(WorkflowContextKeys.DiePositionResult);
if (processResult == null)
{
return;
}
UpdateStatistics(processResult);
UpdateMap(processResult);
}
private void UpdateStatistics(DiePositionProcessResult processResult)
{
Statistics.TotalDieCount = processResult.TheoryDieCount;
Statistics.OkDieCount = processResult.RecognizedDieCount;
Statistics.NgDieCount = processResult.NgDieCount;
Statistics.AverageSpacingX = processResult.AverageSpacingX;
Statistics.AverageSpacingY = processResult.AverageSpacingY;
}
private void UpdateMap(DiePositionProcessResult processResult)
{
if (processResult.RowCount <= 0 || processResult.ColumnCount <= 0)
{
return;
}
DieMapModel.Initialize(processResult.RowCount, processResult.ColumnCount);
DieMapModel.SetDieState(0, 0, DieState.Current);
}
}
}

View File

@@ -0,0 +1,581 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Log;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Motion;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using MwFramework.ManagerService;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using MW.WorkFlow;
namespace MainShell.Manual.ViewModel
{
public class DieRecheckDisplayItem : PropertyChangedBase
{
private int _stepIndex;
public int StepIndex
{
get { return _stepIndex; }
set { SetAndNotify(ref _stepIndex, value); }
}
private int _padRow;
public int PadRow
{
get { return _padRow; }
set { SetAndNotify(ref _padRow, value); }
}
private int _padColumn;
public int PadColumn
{
get { return _padColumn; }
set { SetAndNotify(ref _padColumn, value); }
}
private int _dieRow;
public int DieRow
{
get { return _dieRow; }
set { SetAndNotify(ref _dieRow, value); }
}
private int _dieColumn;
public int DieColumn
{
get { return _dieColumn; }
set { SetAndNotify(ref _dieColumn, value); }
}
private double _padX;
public double PadX
{
get { return _padX; }
set { SetAndNotify(ref _padX, value); }
}
private double _padY;
public double PadY
{
get { return _padY; }
set { SetAndNotify(ref _padY, value); }
}
private double _dieX;
public double DieX
{
get { return _dieX; }
set { SetAndNotify(ref _dieX, value); }
}
private double _dieY;
public double DieY
{
get { return _dieY; }
set { SetAndNotify(ref _dieY, value); }
}
private string _transPathType;
public string TransPathType
{
get { return _transPathType; }
set { SetAndNotify(ref _transPathType, value); }
}
private bool _isMissingBond;
public bool IsMissingBond
{
get { return _isMissingBond; }
set { SetAndNotify(ref _isMissingBond, value); }
}
private double _xError;
public double XError
{
get { return _xError; }
set { SetAndNotify(ref _xError, value); }
}
private double _yError;
public double YError
{
get { return _yError; }
set { SetAndNotify(ref _yError, value); }
}
private bool _isXThresholdExceeded;
public bool IsXThresholdExceeded
{
get { return _isXThresholdExceeded; }
set { SetAndNotify(ref _isXThresholdExceeded, value); }
}
private bool _isYThresholdExceeded;
public bool IsYThresholdExceeded
{
get { return _isYThresholdExceeded; }
set { SetAndNotify(ref _isYThresholdExceeded, value); }
}
private string _statusSummary;
public string StatusSummary
{
get { return _statusSummary; }
set { SetAndNotify(ref _statusSummary, value); }
}
}
public class DieRecheckFilterState : PropertyChangedBase
{
private enum DieRecheckFilterRule
{
None = 0,
MissingBondOnly,
XThresholdExceededOnly,
YThresholdExceededOnly,
XYThresholdExceededOnly
}
private DieRecheckFilterRule _selectedRule;
private bool _isUpdatingSelection;
public bool IsMissingBondRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.MissingBondOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.MissingBondOnly, nameof(IsMissingBondRuleSelected)); }
}
public bool IsXThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.XThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.XThresholdExceededOnly, nameof(IsXThresholdRuleSelected)); }
}
public bool IsYThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.YThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.YThresholdExceededOnly, nameof(IsYThresholdRuleSelected)); }
}
public bool IsXYThresholdRuleSelected
{
get { return _selectedRule == DieRecheckFilterRule.XYThresholdExceededOnly; }
set { SetSelectedRule(value, DieRecheckFilterRule.XYThresholdExceededOnly, nameof(IsXYThresholdRuleSelected)); }
}
private double _xThreshold = 0.01d;
public double XThreshold
{
get { return _xThreshold; }
set { SetAndNotify(ref _xThreshold, value); }
}
private double _yThreshold = 0.01d;
public double YThreshold
{
get { return _yThreshold; }
set { SetAndNotify(ref _yThreshold, value); }
}
public void ClearSelectedRule()
{
UpdateSelectedRule(DieRecheckFilterRule.None, null);
}
private void SetSelectedRule(bool isSelected, DieRecheckFilterRule targetRule, string propertyName)
{
if (_isUpdatingSelection)
{
return;
}
DieRecheckFilterRule newRule = isSelected ? targetRule : DieRecheckFilterRule.None;
UpdateSelectedRule(newRule, propertyName);
}
private void UpdateSelectedRule(DieRecheckFilterRule newRule, string propertyName)
{
if (_selectedRule == newRule)
{
if (!string.IsNullOrWhiteSpace(propertyName))
{
OnPropertyChanged(propertyName);
}
return;
}
_selectedRule = newRule;
_isUpdatingSelection = true;
try
{
OnPropertyChanged(nameof(IsMissingBondRuleSelected));
OnPropertyChanged(nameof(IsXThresholdRuleSelected));
OnPropertyChanged(nameof(IsYThresholdRuleSelected));
OnPropertyChanged(nameof(IsXYThresholdRuleSelected));
}
finally
{
_isUpdatingSelection = false;
}
}
}
public class DieRecheckViewModel : OperateViewModelBase
{
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
private readonly ProcessResultManager _processResultManager;
private readonly DieRecheckService _dieRecheckService;
private readonly SafeAxisMotion _safeAxisMotion;
private ObservableCollection<DieRecheckDisplayItem> _allPointResults = new ObservableCollection<DieRecheckDisplayItem>();
private List<DieRecheckDisplayItem> _filteredPointResults = new List<DieRecheckDisplayItem>();
private ObservableCollection<DieRecheckDisplayItem> _pointResults = new ObservableCollection<DieRecheckDisplayItem>();
public ObservableCollection<DieRecheckDisplayItem> PointResults
{
get { return _pointResults; }
set { SetAndNotify(ref _pointResults, value); }
}
public PaginationViewModel Pagination { get; } = new PaginationViewModel();
private RegionModel _recheckRegion = new RegionModel();
public RegionModel RecheckRegion
{
get { return _recheckRegion; }
set { SetAndNotify(ref _recheckRegion, value); }
}
private DieRecheckFilterState _filterState = new DieRecheckFilterState();
public DieRecheckFilterState FilterState
{
get { return _filterState; }
set { SetAndNotify(ref _filterState, value); }
}
private int _pointCount;
public int PointCount
{
get { return _pointCount; }
set { SetAndNotify(ref _pointCount, value); }
}
private int _filteredPointCount;
public int FilteredPointCount
{
get { return _filteredPointCount; }
set { SetAndNotify(ref _filteredPointCount, value); }
}
private DieRecheckDisplayItem _selectedPointResult;
public DieRecheckDisplayItem SelectedPointResult
{
get { return _selectedPointResult; }
set { SetAndNotify(ref _selectedPointResult, value); }
}
public DieRecheckViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
DieRecheckService dieRecheckService,
SafeAxisMotion safeAxisMotion)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
if (hardwareManager == null)
{
throw new ArgumentNullException(nameof(hardwareManager));
}
_dieRecheckService = dieRecheckService ?? throw new ArgumentNullException(nameof(dieRecheckService));
_safeAxisMotion = safeAxisMotion ?? throw new ArgumentNullException(nameof(safeAxisMotion));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
FilterState.PropertyChanged += FilterState_PropertyChanged;
RecheckRegion.PropertyChanged += RecheckRegion_PropertyChanged;
Pagination.PageChanged += Pagination_PageChanged;
LoadCurrentPoints();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
LoadCurrentPoints();
}
public async Task StartProcess()
{
WorkflowContext context = new WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.DieRecheckFlow;
WorkflowRunCompletedEventArgs result = await RunManualActivityAsync(
new DieRecheckActivity(ProcessFlowName.DieRecheckFlow, _dieRecheckService),
context,
false);
if (!IsWorkflowCompleted(result))
{
return;
}
LoadCurrentPoints();
}
public async Task MoveToSelectedPoint()
{
if (SelectedPointResult == null)
{
LocalizedMessageBox.Show(MessageKey.DieRecheckSelectedPointRequired, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
try
{
HardwareDevice selectedDevice = _cameraAxisViewModel.CameraAxisDevices != null
? _cameraAxisViewModel.CameraAxisDevices.SelectedHardwareDevice
: null;
if (selectedDevice == null || selectedDevice.AxisX == null || selectedDevice.AxisY == null)
{
LocalizedMessageBox.Show(MessageKey.DieRecheckMoveDeviceUnavailable, MessageKey.TitleWarning, MessageBoxButton.OK, MessageBoxImage.Warning);
return;
}
string moveMessage = $"DieRecheck move to point. StepIndex={SelectedPointResult.StepIndex}, PadX={SelectedPointResult.PadX:F4}, PadY={SelectedPointResult.PadY:F4}";
moveMessage.LogProcessInfo();
await Task.Run(() =>
_safeAxisMotion.SafeMove(
MotionMoveRequest.ForAxis(selectedDevice.AxisX, SelectedPointResult.PadX, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisX" }),
MotionMoveRequest.ForAxis(selectedDevice.AxisY, SelectedPointResult.PadY, source: nameof(DieRecheckViewModel), tags: new[] { "DieRecheck", "MoveToPoint", "AxisY" })));
}
catch (Exception exception)
{
LogManager.LogSysError(exception);
MwMessageBox.Show(exception.Message, LanguageResourceHelper.GetString(MessageKey.TitleError), MessageBoxButton.OK, MessageBoxImage.Error);
}
}
public void ResetFilters()
{
FilterState.ClearSelectedRule();
FilterState.XThreshold = 0.01d;
FilterState.YThreshold = 0.01d;
ApplyFilters();
}
private void FilterState_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
UpdateThresholdFlags();
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void RecheckRegion_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void Pagination_PageChanged(object sender, EventArgs e)
{
LoadPagedData();
}
private void LoadCurrentPoints()
{
_allPointResults.Clear();
_filteredPointResults.Clear();
PointResults.Clear();
DieRecheckProcessResult processResult = _processResultManager.DieRecheckResult;
if (processResult == null || processResult.PointResults == null)
{
PointCount = 0;
FilteredPointCount = 0;
Pagination.TotalItems = 0;
return;
}
foreach (DieRecheckPointResult pointResult in processResult.PointResults)
{
_allPointResults.Add(new DieRecheckDisplayItem
{
StepIndex = pointResult.StepIndex,
PadRow = pointResult.PadRow,
PadColumn = pointResult.PadColumn,
DieRow = pointResult.DieRow,
DieColumn = pointResult.DieColumn,
PadX = pointResult.PadX,
PadY = pointResult.PadY,
DieX = pointResult.DieX,
DieY = pointResult.DieY,
TransPathType = pointResult.TransPathType,
IsMissingBond = pointResult.IsMissingBond,
XError = pointResult.XError,
YError = pointResult.YError
});
}
PointCount = _allPointResults.Count;
InitializeRegion();
UpdateThresholdFlags();
Pagination.CurrentPage = 1;
ApplyFilters();
}
private void InitializeRegion()
{
if (_allPointResults.Count == 0)
{
RecheckRegion.StartRow = 1;
RecheckRegion.StartCol = 1;
RecheckRegion.EndRow = 1;
RecheckRegion.EndCol = 1;
return;
}
RecheckRegion.StartRow = _allPointResults.Min(item => item.PadRow);
RecheckRegion.StartCol = _allPointResults.Min(item => item.PadColumn);
RecheckRegion.EndRow = Math.Max(RecheckRegion.StartRow, _allPointResults.Max(item => item.PadRow));
RecheckRegion.EndCol = Math.Max(RecheckRegion.StartCol, _allPointResults.Max(item => item.PadColumn));
}
private void UpdateThresholdFlags()
{
foreach (DieRecheckDisplayItem item in _allPointResults)
{
item.IsXThresholdExceeded = Math.Abs(item.XError) >= FilterState.XThreshold;
item.IsYThresholdExceeded = Math.Abs(item.YError) >= FilterState.YThreshold;
item.StatusSummary = BuildStatusSummary(item);
}
}
private string BuildStatusSummary(DieRecheckDisplayItem item)
{
Collection<string> tags = new Collection<string>();
if (item.IsMissingBond)
{
tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusMissingBond));
}
if (item.IsXThresholdExceeded)
{
tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusXExceeded, item.XError));
}
if (item.IsYThresholdExceeded)
{
tags.Add(LanguageResourceHelper.Format(MessageKey.DieRecheckStatusYExceeded, item.YError));
}
if (tags.Count == 0)
{
tags.Add(LanguageResourceHelper.GetString(MessageKey.DieRecheckStatusOk));
}
return string.Join(" / ", tags);
}
private void ApplyFilters()
{
_filteredPointResults = _allPointResults.Where(MatchesFilters).ToList();
FilteredPointCount = _filteredPointResults.Count;
Pagination.TotalItems = FilteredPointCount;
if (Pagination.TotalItems == 0)
{
SelectedPointResult = null;
PointResults = new ObservableCollection<DieRecheckDisplayItem>();
return;
}
if (Pagination.CurrentPage > Pagination.TotalPages)
{
Pagination.CurrentPage = Pagination.TotalPages;
return;
}
LoadPagedData();
}
private void LoadPagedData()
{
if (_filteredPointResults == null || _filteredPointResults.Count == 0)
{
PointResults = new ObservableCollection<DieRecheckDisplayItem>();
SelectedPointResult = null;
return;
}
int safePage = Math.Max(1, Math.Min(Pagination.CurrentPage, Pagination.TotalPages));
int offset = (safePage - 1) * Pagination.PageSize;
int limit = Pagination.PageSize;
PointResults = new ObservableCollection<DieRecheckDisplayItem>(_filteredPointResults.Skip(offset).Take(limit));
if (SelectedPointResult != null && !PointResults.Contains(SelectedPointResult))
{
SelectedPointResult = null;
}
}
private bool MatchesFilters(DieRecheckDisplayItem item)
{
if (item == null)
{
return false;
}
bool isInRegion = item.PadRow >= RecheckRegion.StartRow
&& item.PadRow <= RecheckRegion.EndRow
&& item.PadColumn >= RecheckRegion.StartCol
&& item.PadColumn <= RecheckRegion.EndCol;
if (!isInRegion)
{
return false;
}
if (FilterState.IsMissingBondRuleSelected && !item.IsMissingBond)
{
return false;
}
if (FilterState.IsXYThresholdRuleSelected && !(item.IsXThresholdExceeded && item.IsYThresholdExceeded))
{
return false;
}
if (FilterState.IsXThresholdRuleSelected && !item.IsXThresholdExceeded)
{
return false;
}
if (FilterState.IsYThresholdRuleSelected && !item.IsYThresholdExceeded)
{
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,294 @@
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Manual.ViewModel.State;
using MainShell.Resources.CustomControl;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class LogisticsOperationViewModel : OperateViewModelBase
{
private readonly IDeviceIoMonitorService _ioMonitorService;
private readonly Dictionary<string, Action<bool>> _ioNameBindings = new Dictionary<string, Action<bool>>(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<int, Action<bool>> _ioIdBindings = new Dictionary<int, Action<bool>>();
private LogisticsOperationState _state;
public new LogisticsOperationState State
{
get { return _state; }
set { SetAndNotify(ref _state, value); }
}
public LogisticsOperationViewModel(IDeviceIoMonitorService ioMonitorService)
{
_ioMonitorService = ioMonitorService;
State = BuildDefaultState();
State.PropertyChanged += OnStatePropertyChanged;
InitializeIoBindings();
_ioMonitorService.IoChanged += OnIoSnapshotChanged;
_ioMonitorService.Start();
ApplyEfemMode(State.IsManualWithoutEfem);
RequestIoRefresh();
}
private LogisticsOperationState BuildDefaultState()
{
var state = new LogisticsOperationState
{
SubstrateLoad = BuildSectionState("SUBSTRATE LOAD / 基板上料", "READY", "开始上料流程", "停止流程"),
ChipLoad = BuildSectionState("CHIP LOAD / 芯片上料", "READY", "发送取料指令", "取消流程"),
SubstrateOut = BuildSectionState("SUBSTRATE OUT / 基板下料", "IDLE", "开始下料出片", "停止流程"),
ChipUnload = BuildSectionState("CHIP UNLOAD / 芯片下料", "AWAITING", "执行内部下料", "取消流程"),
MagazineMap = MagazineMapViewModel.CreateDefault(25),
FeedbackTitle = "FEEDBACK"
};
state.ChipLoad.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 1: EFEM 取料",
Status = "READY",
ActionText = "发送取料指令",
IsEnabled = true,
ControlTag = "EFEM HANDOFF",
RequiresEfemCommunication = true,
TargetLayer = 1
});
state.ChipLoad.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 2: 设备内部吸取",
Status = "内部手动",
ActionText = "执行内部动作",
IsEnabled = false,
ControlTag = "INTERNAL",
RequiresEfemCommunication = false
});
state.ChipUnload.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 1: 内部移出至对接位",
Status = "内部手动",
ActionText = "执行内部下料",
IsEnabled = true,
ControlTag = "INTERNAL",
RequiresEfemCommunication = false
});
state.ChipUnload.PhaseSteps.Add(new PhaseStepState
{
Title = "Phase 2: EFEM 返料到料盒",
Status = "AWAITING",
ActionText = "发送取料指令",
IsEnabled = false,
ControlTag = "EFEM HANDOFF",
RequiresEfemCommunication = true,
TargetLayer = 1
});
return state;
}
private SectionState BuildSectionState(string title, string status, string primaryAction, string secondaryAction)
{
return new SectionState
{
Title = title,
Status = status,
PrimaryActionText = primaryAction,
SecondaryActionText = secondaryAction,
Indicators =
{
new IndicatorState { Name = "进片感应器触发", IsOn = false },
new IndicatorState { Name = "轨道电机运行中", IsOn = false },
new IndicatorState { Name = "平移机原点检测", IsOn = false },
new IndicatorState { Name = "真空吸附确认", IsOn = false }
}
};
}
public async Task ScanAsync()
{
if (State.IsScanning)
{
return;
}
State.IsScanning = true;
State.ScanProgress = 0;
State.AddFeedback("自动扫码开始。", "INFO");
RequestIoRefresh();
for (var i = 1; i <= 100; i++)
{
State.ScanProgress = i;
await Task.Delay(20);
}
State.IsScanning = false;
State.AddFeedback("自动扫码完成,共扫描 25 个槽位。", "INFO");
RequestIoRefresh();
}
private void OnStatePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(LogisticsOperationState.IsManualWithoutEfem))
{
ApplyEfemMode(State.IsManualWithoutEfem);
}
RequestIoRefresh();
}
private void ApplyEfemMode(bool manualWithoutEfem)
{
if (State.ChipLoad.PhaseSteps.Count > 1)
{
State.ChipLoad.PhaseSteps[0].IsEnabled = !manualWithoutEfem;
State.ChipLoad.PhaseSteps[1].IsEnabled = manualWithoutEfem;
}
if (State.ChipUnload.PhaseSteps.Count > 1)
{
State.ChipUnload.PhaseSteps[1].IsEnabled = !manualWithoutEfem;
}
RequestIoRefresh();
}
public Task ChipLoadPhase1Async()
{
if (State.IsManualWithoutEfem)
{
State.AddFeedback("CHIP LOAD: 无EFEM调试模式直接执行设备内部芯片上料流程。", "INFO");
return ChipLoadPhase2Async();
}
var layer = State.ChipLoad.PhaseSteps[0].TargetLayer;
State.AddFeedback(string.Format("CHIP LOAD Phase1: 发送EFEM取料指令目标料盒层 L{0:D2}(预留通讯接口)。", layer), "INFO");
if (State.ChipLoad.PhaseSteps.Count > 1)
{
State.ChipLoad.PhaseSteps[1].IsEnabled = true;
}
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipLoadPhase2Async()
{
State.AddFeedback("CHIP LOAD Phase2: 执行设备内部吸取动作。", "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipUnloadPhase1Async()
{
State.AddFeedback("CHIP UNLOAD Phase1: 内部移出至对接位。", "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
public Task ChipUnloadPhase2Async()
{
var layer = State.ChipUnload.PhaseSteps[1].TargetLayer;
State.AddFeedback(string.Format("CHIP UNLOAD Phase2: 发送EFEM返料指令目标料盒层 L{0:D2}(预留通讯接口)。", layer), "INFO");
RequestIoRefresh();
return Task.CompletedTask;
}
protected override void OnDeactivate()
{
_ioMonitorService.IoChanged -= OnIoSnapshotChanged;
base.OnDeactivate();
}
private void InitializeIoBindings()
{
BindIoName("LG.SubstrateLoad.SensorIn", value => SetIndicator(State.SubstrateLoad, 0, value));
BindIoName("LG.SubstrateLoad.TrackMotorRun", value => SetIndicator(State.SubstrateLoad, 1, value));
BindIoName("LG.SubstrateLoad.HomeChecked", value => SetIndicator(State.SubstrateLoad, 2, value));
BindIoName("LG.SubstrateLoad.VacuumOk", value => SetIndicator(State.SubstrateLoad, 3, value));
BindIoName("LG.ChipLoad.SensorIn", value => SetIndicator(State.ChipLoad, 0, value));
BindIoName("LG.ChipLoad.TrackMotorRun", value => SetIndicator(State.ChipLoad, 1, value));
BindIoName("LG.ChipLoad.HomeChecked", value => SetIndicator(State.ChipLoad, 2, value));
BindIoName("LG.ChipLoad.VacuumOk", value => SetIndicator(State.ChipLoad, 3, value));
BindIoName("LG.SubstrateOut.SensorIn", value => SetIndicator(State.SubstrateOut, 0, value));
BindIoName("LG.SubstrateOut.TrackMotorRun", value => SetIndicator(State.SubstrateOut, 1, value));
BindIoName("LG.SubstrateOut.HomeChecked", value => SetIndicator(State.SubstrateOut, 2, value));
BindIoName("LG.SubstrateOut.VacuumOk", value => SetIndicator(State.SubstrateOut, 3, value));
BindIoName("LG.ChipUnload.SensorIn", value => SetIndicator(State.ChipUnload, 0, value));
BindIoName("LG.ChipUnload.TrackMotorRun", value => SetIndicator(State.ChipUnload, 1, value));
BindIoName("LG.ChipUnload.HomeChecked", value => SetIndicator(State.ChipUnload, 2, value));
BindIoName("LG.ChipUnload.VacuumOk", value => SetIndicator(State.ChipUnload, 3, value));
BindIoId(1001, value => SetIndicator(State.SubstrateLoad, 0, value));
BindIoId(1002, value => SetIndicator(State.SubstrateLoad, 1, value));
BindIoId(1003, value => SetIndicator(State.SubstrateLoad, 2, value));
BindIoId(1004, value => SetIndicator(State.SubstrateLoad, 3, value));
}
private void BindIoName(string name, Action<bool> setter)
{
_ioNameBindings[name] = setter;
}
private void BindIoId(int id, Action<bool> setter)
{
_ioIdBindings[id] = setter;
}
private void OnIoSnapshotChanged(object sender, DeviceIoChangedEventArgs e)
{
var changedPoints = e.ChangedPoints;
for (var i = 0; i < changedPoints.Count; i++)
{
ApplyIoPoint(changedPoints[i]);
}
}
private void ApplyIoPoint(DeviceIoPointState point)
{
if (point == null)
{
return;
}
Action<bool> setter;
if (!string.IsNullOrWhiteSpace(point.Name) && _ioNameBindings.TryGetValue(point.Name, out setter))
{
setter(point.Value);
return;
}
if (_ioIdBindings.TryGetValue(point.Id, out setter))
{
setter(point.Value);
}
}
private void RequestIoRefresh()
{
_ioMonitorService.RequestRefresh();
}
private static void SetIndicator(SectionState section, int index, bool isOn)
{
if (section == null || section.Indicators.Count <= index)
{
return;
}
section.Indicators[index].IsOn = isOn;
}
}
}

View File

@@ -0,0 +1,178 @@
using MainShell.Models;
using MainShell.Recipe.ViewModel;
using MaxwellFramework.Core.Interfaces;
using Stylet;
using StyletIoC;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class ManualOperateViewModel : Stylet.Screen,IPage
{
public string Name => "ManualOperate";
private const string LOGISTICS_OPERATION = "物流操作";
private const string SUBSTRATEPOSITION = "基板定位";
//基板测高
private const string SUBSTRATEHEIGHTMEASUREMENT = "基板测高";
//芯片拉直
private const string CHIPSTRAIGHTENING = "芯片拉直";
private const string WAFERPOSITION = "芯片定位";
//Die转移
private const string DIETRANSFER = "芯片转移";
//精度复检
private const string DIERECHECK = "精度复检";
private readonly Dictionary<string, Screen> _viewModelDict = new Dictionary<string, Screen>();
public ObservableCollection<MenuItemWrap> MenuItemWraps { get; private set; }
private MenuItemWrap _selectedMenuItem;
public MenuItemWrap SelectedMenuItem
{
get { return _selectedMenuItem; }
set
{
if (SetAndNotify(ref _selectedMenuItem, value))
{
if (value != null && _viewModelDict.TryGetValue(value.Header, out var screen))
{
CurrentScreen = screen;
}
}
}
}
private Screen _currentScreen;
public Screen CurrentScreen
{
get { return _currentScreen; }
set { SetAndNotify(ref _currentScreen, value); }
}
#region //界面ViewModel注入
private LogisticsOperationViewModel _logisticsOperationViewModel;
[Inject]
public LogisticsOperationViewModel LogisticsOperationViewModel
{
get { return _logisticsOperationViewModel; }
set { SetAndNotify(ref _logisticsOperationViewModel, value); }
}
private SubstratePositionViewModel _substratePositionViewModel;
[Inject]
public SubstratePositionViewModel SubstratePositionViewModel
{
get { return _substratePositionViewModel; }
set { SetAndNotify(ref _substratePositionViewModel, value); }
}
private WaferAngleAdjustmentViewModel _waferAngleAdjustmentViewModel;
[Inject]
public WaferAngleAdjustmentViewModel WaferAngleAdjustmentViewModel
{
get { return _waferAngleAdjustmentViewModel; }
set { SetAndNotify(ref _waferAngleAdjustmentViewModel, value); }
}
private DieBondingViewModel _dieBondingViewModel;
[Inject]
public DieBondingViewModel DieBondingViewModel
{
get { return _dieBondingViewModel; }
set { SetAndNotify(ref _dieBondingViewModel, value); }
}
private DiePositionViewModel _diePositionViewModel;
[Inject]
public DiePositionViewModel DiePositionViewModel
{
get { return _diePositionViewModel; }
set { SetAndNotify(ref _diePositionViewModel, value); }
}
private ChipStraighteningViewModel _chipStraighteningViewModel;
[Inject]
public ChipStraighteningViewModel ChipStraighteningViewModel
{
get { return _chipStraighteningViewModel; }
set { SetAndNotify(ref _chipStraighteningViewModel, value); }
}
private SubstrateHeightMeasureViewModel _substrateHeightMeasureViewModel;
[Inject]
public SubstrateHeightMeasureViewModel SubstrateHeightMeasureViewModel
{
get { return _substrateHeightMeasureViewModel; }
set { SetAndNotify(ref _substrateHeightMeasureViewModel, value); }
}
private DieRecheckViewModel _dieRecheckViewModel;
[Inject]
public DieRecheckViewModel DieRecheckViewModel
{
get { return _dieRecheckViewModel; }
set { SetAndNotify(ref _dieRecheckViewModel, value); }
}
#endregion
public ManualOperateViewModel()
{
MenuItemWraps = new ObservableCollection<MenuItemWrap>()
{
new MenuItemWrap
{
Header=LOGISTICS_OPERATION,Tag=LOGISTICS_OPERATION
},
new MenuItemWrap
{
Header= SUBSTRATEPOSITION, Tag=SUBSTRATEPOSITION
},
new MenuItemWrap
{
Header=SUBSTRATEHEIGHTMEASUREMENT, Tag=SUBSTRATEHEIGHTMEASUREMENT
},
new MenuItemWrap
{
Header=CHIPSTRAIGHTENING, Tag=CHIPSTRAIGHTENING
},
new MenuItemWrap
{
Header=WAFERPOSITION, Tag=WAFERPOSITION
},
new MenuItemWrap
{
Header=DIETRANSFER, Tag=DIETRANSFER
},
new MenuItemWrap
{
Header=DIERECHECK, Tag=DIERECHECK
}
};
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_viewModelDict.Count == 0)
{
InitViewModelDict();
}
if (SelectedMenuItem == null)
SelectedMenuItem = MenuItemWraps[0];
}
private void InitViewModelDict()
{
_viewModelDict.Clear();
_viewModelDict.Add(LOGISTICS_OPERATION, LogisticsOperationViewModel);
_viewModelDict.Add(SUBSTRATEPOSITION, SubstratePositionViewModel);
_viewModelDict.Add(SUBSTRATEHEIGHTMEASUREMENT, SubstrateHeightMeasureViewModel);
_viewModelDict.Add(CHIPSTRAIGHTENING, WaferAngleAdjustmentViewModel);
_viewModelDict.Add(DIETRANSFER, DieBondingViewModel);
_viewModelDict.Add(WAFERPOSITION, DiePositionViewModel);
_viewModelDict.Add(DIERECHECK, DieRecheckViewModel);
}
}
}

View File

@@ -0,0 +1,240 @@
using MainShell.Resources.CustomControl;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
namespace MainShell.Manual.ViewModel.State
{
public class LogisticsOperationState : PropertyChangedBase
{
private SectionState _substrateLoad;
public SectionState SubstrateLoad
{
get { return _substrateLoad; }
set { SetAndNotify(ref _substrateLoad, value); }
}
private SectionState _chipLoad;
public SectionState ChipLoad
{
get { return _chipLoad; }
set { SetAndNotify(ref _chipLoad, value); }
}
private SectionState _substrateOut;
public SectionState SubstrateOut
{
get { return _substrateOut; }
set { SetAndNotify(ref _substrateOut, value); }
}
private SectionState _chipUnload;
public SectionState ChipUnload
{
get { return _chipUnload; }
set { SetAndNotify(ref _chipUnload, value); }
}
private MagazineMapViewModel _magazineMap;
public MagazineMapViewModel MagazineMap
{
get { return _magazineMap; }
set { SetAndNotify(ref _magazineMap, value); }
}
private string _feedbackTitle;
public string FeedbackTitle
{
get { return _feedbackTitle; }
set { SetAndNotify(ref _feedbackTitle, value); }
}
private bool _isScanning;
public bool IsScanning
{
get { return _isScanning; }
set { SetAndNotify(ref _isScanning, value); }
}
private int _scanProgress;
public int ScanProgress
{
get { return _scanProgress; }
set { SetAndNotify(ref _scanProgress, value); }
}
private bool _isManualWithoutEfem;
public bool IsManualWithoutEfem
{
get { return _isManualWithoutEfem; }
set { SetAndNotify(ref _isManualWithoutEfem, value); }
}
public string LastFeedbackTime
{
get
{
var last = FeedbackRecords.LastOrDefault();
return last == null ? "--:--:--" : last.Time.ToString("HH:mm:ss");
}
}
public ObservableCollection<FeedbackRecord> FeedbackRecords { get; } = new ObservableCollection<FeedbackRecord>();
public LogisticsOperationState()
{
FeedbackRecords.CollectionChanged += OnFeedbackRecordsChanged;
AddFeedback("内部物流动作已独立触发并完成。");
}
public void AddFeedback(string message, string level = "DEBUG")
{
FeedbackRecords.Add(new FeedbackRecord
{
Time = DateTime.Now,
Level = level,
Message = message
});
while (FeedbackRecords.Count > 3)
{
FeedbackRecords.RemoveAt(0);
}
}
private void OnFeedbackRecordsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged(nameof(LastFeedbackTime));
}
}
public class SectionState : PropertyChangedBase
{
private string _title;
public string Title
{
get { return _title; }
set { SetAndNotify(ref _title, value); }
}
private string _status;
public string Status
{
get { return _status; }
set { SetAndNotify(ref _status, value); }
}
private string _primaryActionText;
public string PrimaryActionText
{
get { return _primaryActionText; }
set { SetAndNotify(ref _primaryActionText, value); }
}
private string _secondaryActionText;
public string SecondaryActionText
{
get { return _secondaryActionText; }
set { SetAndNotify(ref _secondaryActionText, value); }
}
public ObservableCollection<IndicatorState> Indicators { get; } = new ObservableCollection<IndicatorState>();
public ObservableCollection<PhaseStepState> PhaseSteps { get; } = new ObservableCollection<PhaseStepState>();
}
public class PhaseStepState : PropertyChangedBase
{
private string _title;
public string Title
{
get { return _title; }
set { SetAndNotify(ref _title, value); }
}
private string _status;
public string Status
{
get { return _status; }
set { SetAndNotify(ref _status, value); }
}
private string _actionText;
public string ActionText
{
get { return _actionText; }
set { SetAndNotify(ref _actionText, value); }
}
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set { SetAndNotify(ref _isEnabled, value); }
}
private string _controlTag;
public string ControlTag
{
get { return _controlTag; }
set { SetAndNotify(ref _controlTag, value); }
}
private bool _requiresEfemCommunication;
public bool RequiresEfemCommunication
{
get { return _requiresEfemCommunication; }
set { SetAndNotify(ref _requiresEfemCommunication, value); }
}
private int _targetLayer = 1;
public int TargetLayer
{
get { return _targetLayer; }
set { SetAndNotify(ref _targetLayer, value); }
}
}
public class IndicatorState : PropertyChangedBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetAndNotify(ref _name, value); }
}
private bool _isOn;
public bool IsOn
{
get { return _isOn; }
set { SetAndNotify(ref _isOn, value); }
}
}
public class FeedbackRecord : PropertyChangedBase
{
private DateTime _time;
public DateTime Time
{
get { return _time; }
set { SetAndNotify(ref _time, value); }
}
private string _level;
public string Level
{
get { return _level; }
set { SetAndNotify(ref _level, value); }
}
private string _message;
public string Message
{
get { return _message; }
set { SetAndNotify(ref _message, value); }
}
}
}

View File

@@ -0,0 +1,195 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Recipe.Models.SubstrateParameter;
using MW.WorkFlow;
using Stylet;
using StyletIoC;
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class SubstrateHeightMeasureDisplayItem : PropertyChangedBase
{
private string _pointName;
public string PointName
{
get { return _pointName; }
set { SetAndNotify(ref _pointName, value); }
}
private int _rowIndex;
public int RowIndex
{
get { return _rowIndex; }
set { SetAndNotify(ref _rowIndex, value); }
}
private int _columnIndex;
public int ColumnIndex
{
get { return _columnIndex; }
set { SetAndNotify(ref _columnIndex, value); }
}
private double _positionX;
public double PositionX
{
get { return _positionX; }
set { SetAndNotify(ref _positionX, value); }
}
private double _positionY;
public double PositionY
{
get { return _positionY; }
set { SetAndNotify(ref _positionY, value); }
}
private double? _heightValue;
public double? HeightValue
{
get { return _heightValue; }
set { SetAndNotify(ref _heightValue, value); }
}
}
public class SubstrateHeightMeasureViewModel : OperateViewModelBase
{
private readonly IEventAggregator _eventAggregator;
private readonly RecipeManager _recipeManager;
private readonly ProcessResultManager _processResultManager;
private readonly SubstrateHeightMeasureService _substrateHeightMeasureService;
private ObservableCollection<SubstrateHeightMeasureDisplayItem> _pointResults = new ObservableCollection<SubstrateHeightMeasureDisplayItem>();
public ObservableCollection<SubstrateHeightMeasureDisplayItem> PointResults
{
get { return _pointResults; }
set { SetAndNotify(ref _pointResults, value); }
}
private string _recipeName;
public string RecipeName
{
get { return _recipeName; }
set { SetAndNotify(ref _recipeName, value); }
}
private int _configuredPointCount;
public int ConfiguredPointCount
{
get { return _configuredPointCount; }
set { SetAndNotify(ref _configuredPointCount, value); }
}
private int _measuredPointCount;
public int MeasuredPointCount
{
get { return _measuredPointCount; }
set { SetAndNotify(ref _measuredPointCount, value); }
}
private SubstrateHeightMeasureMode _currentMeasureMode;
public SubstrateHeightMeasureMode CurrentMeasureMode
{
get { return _currentMeasureMode; }
set { SetAndNotify(ref _currentMeasureMode, value); }
}
public SubstrateHeightMeasureViewModel(
IEventAggregator eventAggregator,
RecipeManager recipeManager,
ProcessResultManager processResultManager,
HardwareManager hardwareManager,
SubstrateHeightMeasureService substrateHeightMeasureService)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_substrateHeightMeasureService = substrateHeightMeasureService ?? throw new ArgumentNullException(nameof(substrateHeightMeasureService));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
RefreshRecipeSummary();
LoadLastMeasureResult();
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
RefreshRecipeSummary();
LoadLastMeasureResult();
}
public async Task StartProcess()
{
RefreshRecipeSummary();
WorkflowContext context = new WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.SubstrateHeightMeasureFlow;
WorkflowRunCompletedEventArgs result = await RunManualActivityAsync(
new SubstrateHeightMeasureActivity(ProcessFlowName.SubstrateHeightMeasureFlow, _substrateHeightMeasureService),
context);
if (!IsWorkflowCompleted(result))
{
return;
}
LoadLastMeasureResult();
}
private void RefreshRecipeSummary()
{
SubstrateRecipe currentRecipe = _recipeManager.CurrentSubstrateRecipe;
RecipeName = currentRecipe == null ? string.Empty : currentRecipe.RecipeName;
if (currentRecipe == null || currentRecipe.HeightMeasureSetting == null)
{
ConfiguredPointCount = 0;
CurrentMeasureMode = SubstrateHeightMeasureMode.StandardTeachPosition;
return;
}
ConfiguredPointCount = currentRecipe.HeightMeasureSetting.Points == null ? 0 : currentRecipe.HeightMeasureSetting.Points.Count;
CurrentMeasureMode = currentRecipe.HeightMeasureSetting.Mode;
}
private void LoadLastMeasureResult()
{
PointResults.Clear();
SubstrateHeightMeasureProcessResult processResult = _processResultManager.SubstrateHeightMeasureResult;
if (processResult == null || processResult.PointResults == null)
{
MeasuredPointCount = 0;
return;
}
foreach (SubstrateHeightMeasurePointResult pointResult in processResult.PointResults)
{
PointResults.Add(new SubstrateHeightMeasureDisplayItem
{
PointName = pointResult.PointName,
RowIndex = pointResult.RowIndex,
ColumnIndex = pointResult.ColumnIndex,
PositionX = pointResult.PositionX,
PositionY = pointResult.PositionY,
HeightValue = pointResult.HeightValue
});
}
MeasuredPointCount = PointResults.Count;
}
}
}

View File

@@ -0,0 +1,269 @@
using MainShell.Common;
using MainShell.EventArgsFolder;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.Models;
using MainShell.Process;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using MainShell.Resources.CustomControl;
using Stylet;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace MainShell.Manual.ViewModel
{
public class MarkResultModel : PropertyChangedBase
{
private int _id;
public int Id
{
get { return _id; }
set { SetAndNotify(ref _id, value); }
}
private MPoint _basePos;
public MPoint BasePose
{
get { return _basePos; }
set { SetAndNotify(ref _basePos, value); }
}
private MPoint _resultPose;
public MPoint ResultPose
{
get { return _resultPose; }
set { SetAndNotify(ref _resultPose, value); }
}
}
// 实现3个事件接口
public class SubstratePositionViewModel : OperateViewModelBase,
IHandle<SubstrateProcessStartedEventArgs>,
IHandle<SubstrateMarkFoundEventArgs>,
IHandle<SubstrateResultEventArgs>
{
private sealed class DelegateCommand : ICommand
{
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
}
public event EventHandler CanExecuteChanged
{
add
{
}
remove
{
}
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
// 属性定义保持不变...
private ObservableCollection<MarkResultModel> _markResults = new ObservableCollection<MarkResultModel>();
public ObservableCollection<MarkResultModel> MarkResults
{
get { return _markResults; }
set { SetAndNotify(ref _markResults, value); }
}
private int _row;
public int RowIndex
{
get { return _row; }
set { SetAndNotify(ref _row, value); }
}
private int _col;
public int ColumnIndex
{
get { return _col; }
set { SetAndNotify(ref _col, value); }
}
private int _rowMax;
public int RowMax
{
get { return _rowMax; }
set { SetAndNotify(ref _rowMax, value); }
}
private int _colMax;
public int ColMax
{
get { return _colMax; }
set { SetAndNotify(ref _colMax, value); }
}
private double _substrateAngle;
public double SubstrateAngle
{
get { return _substrateAngle; }
set { SetAndNotify(ref _substrateAngle, value); }
}
private DieMapModel _substrateMapModel = new DieMapModel();
public DieMapModel SubstrateMapModel
{
get => _substrateMapModel;
set => SetAndNotify(ref _substrateMapModel, value);
}
private bool _isClickEnabled = true;
public bool IsClickEnabled
{
get => _isClickEnabled;
set => SetAndNotify(ref _isClickEnabled, value);
}
public ICommand DieClickedCommand { get; private set; }
private readonly HardwareManager _hardwareManager;
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly SubstratePositionMotionService _substratePositionMotionService;
public SubstratePositionViewModel(HardwareManager hardwareManager, RecipeManager recipeManager, IEventAggregator eventAggregator, ProcessResultManager processResultManager, SubstratePositionMotionService substratePositionMotionService)
{
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_substratePositionMotionService = substratePositionMotionService ?? throw new ArgumentNullException(nameof(substratePositionMotionService));
_eventAggregator.Subscribe(this);
DieClickedCommand = new DelegateCommand(ExecuteDieClicked);
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
private void ExecuteDieClicked(object parameter)
{
if (!(parameter is ValueTuple<int, int, DieState>))
{
return;
}
ValueTuple<int, int, DieState> dieInfo = (ValueTuple<int, int, DieState>)parameter;
OnDieClicked(this, new DieMapControl.DieClickedEventArgs(dieInfo.Item1, dieInfo.Item2, dieInfo.Item3));
}
public void Handle(SubstrateProcessStartedEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
MarkResults.Clear();
SubstrateAngle = 0;
});
}
// 2. 找到 Mark 点:更新列表
public void Handle(SubstrateMarkFoundEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
var existing = MarkResults.FirstOrDefault(x => x.Id == message.Id);
if (existing != null)
{
existing.BasePose = message.BasePose;
existing.ResultPose = message.ResultPose;
}
else
{
MarkResults.Add(new MarkResultModel
{
Id = message.Id,
BasePose = message.BasePose,
ResultPose = message.ResultPose
});
}
});
}
// 3. 计算完成:更新角度
public void Handle(SubstrateResultEventArgs message)
{
Stylet.Execute.OnUIThread(() =>
{
SubstrateAngle = message.Angle;
});
}
// 手动启动流程
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = ProcessFlowName.SubstratePositionFlow;
await RunManualActivityAsync(new MainShell.Process.SubstratePositionActivity(ProcessFlowName.SubstratePositionFlow, _substratePositionMotionService), context);
}
protected override void OnViewLoaded()
{
base.OnViewLoaded();
if (_recipeManager.CurrentSubstrateRecipe != null && _recipeManager.CurrentSubstrateRecipe.SubstrateInfo != null)
{
RowMax = _recipeManager.CurrentSubstrateRecipe.SubstrateInfo.RowNumber;
ColMax = _recipeManager.CurrentSubstrateRecipe.SubstrateInfo.ColNumber;
// 初始化基板Map
if (RowMax > 0 && ColMax > 0)
{
SubstrateMapModel.Initialize(RowMax, ColMax);
for (int r = 0; r < RowMax; r++)
{
for (int c = 0; c < ColMax; c++)
{
SubstrateMapModel.SetDieState(r, c, DieState.Available);
}
}
}
}
}
public void OnDieClicked(object sender, DieMapControl.DieClickedEventArgs e)
{
if (!IsClickEnabled) return;
// 更新选中的行列
RowIndex = e.Row + 1; // 界面显示通常从1开始
ColumnIndex = e.Col + 1;
// 提示用户
MessageBox.Show($"已选择基板位置: 行 {RowIndex}, 列 {ColumnIndex}", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
}
public void MoveToSelectPad()
{
}
}
}

View File

@@ -0,0 +1,43 @@
using MainShell.Common;
using MainShell.Hardware;
using MainShell.Manual.Model;
using MainShell.ProcessResult;
using MainShell.Recipe.Models;
using Stylet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MainShell.Manual.ViewModel
{
public class WaferAngleAdjustmentViewModel : OperateViewModelBase
{
private readonly RecipeManager _recipeManager;
private readonly IEventAggregator _eventAggregator; // 引用 EventAggregator
private readonly ProcessResultManager _processResultManager;
private readonly HardwareManager _hardwareManager;
public WaferAngleAdjustmentViewModel(IEventAggregator eventAggregator,RecipeManager recipeManager,
ProcessResultManager processResultManager, HardwareManager hardwareManager)
{
_eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
_recipeManager = recipeManager ?? throw new ArgumentNullException(nameof(recipeManager));
_processResultManager = processResultManager ?? throw new ArgumentNullException(nameof(processResultManager));
_hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
_cameraAxisViewModel = IoC.Get<Common.Display.ViewModel.CameraAxisViewModel>();
_cameraAxisViewModel.CameraAxisDevices.HardwareDeviceList = hardwareManager.CameraAxisManager.TopCameraAxisDevices;
}
public async Task StartProcess()
{
var context = new MW.WorkFlow.WorkflowContext();
context[WorkflowContextKeys.EventAggregator] = _eventAggregator;
context[WorkflowContextKeys.RecipeManager] = _recipeManager;
context[WorkflowContextKeys.ProcessResultManager] = _processResultManager;
context[WorkflowContextKeys.WorkflowName] = MainShell.Process.ProcessFlowName.ChipStraighteningFlow;
await RunManualActivityAsync(new MainShell.Process.WaferAngleAdjustmentActivity(MainShell.Process.ProcessFlowName.ChipStraighteningFlow), context);
}
}
}