Files
test_demo/MX-PD-盘古 - new/PanGu.DieBonderApp/MainShell/Manual/ViewModel/LogisticsOperationViewModel.cs
Shi.Ji e31d3560bb 添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
2026-05-18 11:43:09 +08:00

295 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}
}