Files

295 lines
11 KiB
C#
Raw Permalink Normal View History

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;
}
}
}