270 lines
8.9 KiB
C#
270 lines
8.9 KiB
C#
|
|
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()
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|