| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546 |
- using System;
- using System.Collections.ObjectModel;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using Prism.Commands;
- using Prism.Mvvm;
- using TeamAAS.Feeder.Enums;
- using TeamAAS.Feeder.Interfaces;
- using TeamAAS.Feeder.Models;
- namespace TeamAAS.Feeder.UI
- {
- /// <summary>
- /// 通用(Team 品牌)供料器调试页的视图模型:从宿主 SettingViewModel 下沉而来(Phase 2 迁移)。
- /// 绑定一个 <see cref="IFeeder"/> 设备实例 + 其 <see cref="FeederInfo"/> 配置,直接调用设备 API。
- /// IFeederManager 经 TeamAAS.Global.ServiceRegistry 拉取(避免 Feeder 库反向依赖 TeamAAS.Core)。
- /// 方向/振动集/序列集/料斗IO/输入触发/系统参数/背光 全部在此,与硬件库调试页范式一致。
- /// </summary>
- public class FeederDebugViewModel : BindableBase
- {
- private const string LogSource = "Feeder";
- private IFeeder _feeder;
- private FeederInfo _info;
- private IFeederManager _manager;
- private CancellationTokenSource _sequenceCts;
- public FeederDebugViewModel()
- {
- FeederDirectionCommand = new DelegateCommand<object>(async p => await ExecuteDirectionAsync(Convert.ToInt32(p)));
- StopFeederActionCommand = new DelegateCommand(async () => await StopFeederAsync());
- ToggleBacklightCommand = new DelegateCommand(async () => await ToggleBacklightAsync());
- ExecuteActionCommand = new DelegateCommand(async () => await ExecuteCurrentActionAsync());
- DownloadParamCommand = new DelegateCommand(async () => await DownloadFeederParamsAsync());
- AddSequenceCommand = new DelegateCommand(AddSequence);
- DeleteSequenceCommand = new DelegateCommand(DeleteSequence);
- RenameSequenceCommand = new DelegateCommand(RenameSequence);
- AddSequenceStepCommand = new DelegateCommand<string>(AddSequenceStep);
- MoveUpStepCommand = new DelegateCommand(MoveUpStep);
- MoveDownStepCommand = new DelegateCommand(MoveDownStep);
- DeleteStepCommand = new DelegateCommand(DeleteStep);
- ExecuteSequenceCommand = new DelegateCommand(async () => await ExecuteSequenceAsync());
- StopSequenceCommand = new DelegateCommand(StopSequence);
- ReadHopperParamCommand = new DelegateCommand(async () => await ReadHopperParamAsync());
- WriteHopperParamCommand = new DelegateCommand(async () => await WriteHopperParamAsync());
- RunHopperOutputCommand = new DelegateCommand(async () => await RunHopperOutputAsync());
- StopHopperOutputCommand = new DelegateCommand(async () => await StopHopperOutputAsync());
- ReadInputTriggerCommand = new DelegateCommand(async () => await ReadInputTriggerAsync());
- WriteInputTriggerCommand = new DelegateCommand(async () => await WriteInputTriggerAsync());
- ReadSystemParamCommand = new DelegateCommand(async () => await ReadSystemParamAsync());
- WriteSystemParamCommand = new DelegateCommand(async () => await WriteSystemParamAsync());
- }
- /// <summary>绑定供料器设备(页面装载/选中时由 IFeederDebugPage.BindFeeder 调用)。</summary>
- public void Bind(IFeeder feeder)
- {
- _feeder = feeder;
- try { _manager = TeamAAS.Global.ServiceRegistry.Default.GetService<IFeederManager>(); }
- catch { _manager = null; }
- _info = null;
- if (_feeder != null && _manager != null)
- {
- try { _info = _manager.GetAll().FirstOrDefault(f => f.Id == _feeder.Id); }
- catch { _info = null; }
- }
- SelectedSequence = _info?.Sequences?.FirstOrDefault();
- StatusText = _feeder == null ? "未连接" : (_feeder.IsConnected ? $"{_feeder.Name} 已就绪" : "未连接");
- RaisePropertyChanged(nameof(Info));
- RaisePropertyChanged(nameof(CurrentAction));
- RaisePropertyChanged(nameof(CurrentSequences));
- RaisePropertyChanged(nameof(HasSelectedStep));
- }
- /// <summary>解绑(切换供料器/页面卸载)。</summary>
- public void Unbind()
- {
- try { _sequenceCts?.Cancel(); } catch { }
- _feeder = null;
- _info = null;
- }
- private string _statusText = "未连接";
- public string StatusText { get { return _statusText; } set { SetProperty(ref _statusText, value); } }
- /// <summary>当前绑定供料器的配置模型(方向/振动集/序列/背光状态等的数据源);未绑定时为 null。</summary>
- public FeederInfo Info { get { return _info; } }
- private bool EnsureConnected()
- {
- if (_feeder == null || !_feeder.IsConnected)
- {
- TeamAAS.DialogHelper.Warning("请先连接供料器");
- return false;
- }
- return true;
- }
- private void SaveSilent()
- {
- if (_info == null || _manager == null) return;
- try { _manager.SaveConfig(); } catch { }
- }
- #region 振动集 / 方向
- public int[] VibrationSetList { get; } = Enumerable.Range(1, 11).ToArray();
- public string[] ActionLetterList { get; } = { "← A 左", "↙ B 左下", "↖ C 左上", "↓ D 下", "↑ E 上", "→ F 右", "↘ G 右下", "↗ H 右上", "⤨ I 散开", "⇔ J 水平聚集", "⇕ K 垂直聚集" };
- private int _selectedVibrationSetIndex;
- public int SelectedVibrationSetIndex
- {
- get { return _selectedVibrationSetIndex; }
- set { if (SetProperty(ref _selectedVibrationSetIndex, value)) RaisePropertyChanged(nameof(CurrentAction)); }
- }
- private int _selectedActionIndex;
- public int SelectedActionIndex
- {
- get { return _selectedActionIndex; }
- set { if (SetProperty(ref _selectedActionIndex, value)) RaisePropertyChanged(nameof(CurrentAction)); }
- }
- private int _currentDuration = 500;
- public int CurrentDuration { get { return _currentDuration; } set { SetProperty(ref _currentDuration, value); } }
- public SingleActionParam CurrentAction
- {
- get
- {
- if (_info == null || _info.ActionGroups == null) return null;
- int idx = SelectedVibrationSetIndex < 0 ? 0 : SelectedVibrationSetIndex > 10 ? 10 : SelectedVibrationSetIndex;
- return _info.ActionGroups[idx];
- }
- }
- private bool _isSyncChanging;
- public bool IsSyncChanging { get { return _isSyncChanging; } set { SetProperty(ref _isSyncChanging, value); } }
- private int _globalAmplitude = 50;
- public int GlobalAmplitude
- {
- get { return _globalAmplitude; }
- set
- {
- if (SetProperty(ref _globalAmplitude, value) && IsSyncChanging && CurrentAction != null)
- {
- CurrentAction.Motor1.Amplitude = (ushort)value;
- CurrentAction.Motor2.Amplitude = (ushort)value;
- CurrentAction.Motor3.Amplitude = (ushort)value;
- CurrentAction.Motor4.Amplitude = (ushort)value;
- }
- }
- }
- private int _globalFrequency = 100;
- public int GlobalFrequency
- {
- get { return _globalFrequency; }
- set
- {
- if (SetProperty(ref _globalFrequency, value) && IsSyncChanging && CurrentAction != null)
- {
- CurrentAction.Motor1.Frequency = (ushort)value;
- CurrentAction.Motor2.Frequency = (ushort)value;
- CurrentAction.Motor3.Frequency = (ushort)value;
- CurrentAction.Motor4.Frequency = (ushort)value;
- }
- }
- }
- public DelegateCommand<object> FeederDirectionCommand { get; }
- public DelegateCommand StopFeederActionCommand { get; }
- public DelegateCommand ToggleBacklightCommand { get; }
- public DelegateCommand ExecuteActionCommand { get; }
- public DelegateCommand DownloadParamCommand { get; }
- private async Task ExecuteDirectionAsync(int dir)
- {
- if (!EnsureConnected()) return;
- try
- {
- var action = (FeederAction)dir;
- StatusText = $"执行方向: {action}";
- await _feeder.RunDirectionAsync(action, CurrentDuration);
- TeamAAS.AppLogger.Info($"供料器方向执行: {action} 持续 {CurrentDuration}ms", LogSource);
- StatusText = $"方向 {action} 执行完成";
- }
- catch (Exception ex) { StatusText = "执行失败"; TeamAAS.DialogHelper.ShowError("方向执行失败", ex); }
- }
- private async Task StopFeederAsync()
- {
- if (_feeder == null) return;
- try { await _feeder.StopAsync(); StatusText = "停止振动"; }
- catch (Exception ex) { TeamAAS.DialogHelper.ShowError("停止失败", ex); }
- }
- private async Task ToggleBacklightAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- bool on = await _feeder.QueryBacklightAsync();
- if (on) { await _feeder.CloseBacklightAsync(); if (_info != null) _info.IsBacklightOn = false; StatusText = "背光关"; }
- else { await _feeder.OpenBacklightAsync(); if (_info != null) _info.IsBacklightOn = true; StatusText = "背光开"; }
- RaisePropertyChanged(nameof(Info));
- }
- catch (Exception ex) { TeamAAS.DialogHelper.ShowError("背光控制失败", ex); }
- }
- private async Task ExecuteCurrentActionAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- await _feeder.SetActionParamAsync(SelectedVibrationSetIndex, CurrentAction);
- await _feeder.RunDirectionAsync((FeederAction)SelectedActionIndex, CurrentDuration);
- StatusText = $"执行振动集 {SelectedVibrationSetIndex + 1} ({ActionLetterList[SelectedActionIndex]})";
- }
- catch (Exception ex) { StatusText = "执行失败"; TeamAAS.DialogHelper.ShowError("执行动作失败", ex); }
- }
- private async Task DownloadFeederParamsAsync()
- {
- if (!EnsureConnected() || _info == null) return;
- try
- {
- StatusText = "下载参数中...";
- await _feeder.DownloadAllParamsAsync(_info);
- TeamAAS.AppLogger.Info($"供料器下载振动集参数: {_info.FeederName}", LogSource);
- await _feeder.SaveToDeviceAsync();
- StatusText = "参数已下载并保存到设备";
- TeamAAS.DialogHelper.Success("参数已下载到设备");
- }
- catch (Exception ex) { StatusText = "下载失败"; TeamAAS.DialogHelper.ShowError("下载参数失败", ex); }
- }
- #endregion
- #region 序列集(送料流程)
- public ObservableCollection<SequenceSet> CurrentSequences => _info?.Sequences;
- private SequenceSet _selectedSequence;
- public SequenceSet SelectedSequence
- {
- get { return _selectedSequence; }
- set { if (SetProperty(ref _selectedSequence, value)) RaisePropertyChanged(nameof(CurrentSequenceSteps)); }
- }
- public ObservableCollection<SequenceStep> CurrentSequenceSteps => _selectedSequence?.Steps;
- private SequenceStep _selectedStep;
- public SequenceStep SelectedStep
- {
- get { return _selectedStep; }
- set { if (SetProperty(ref _selectedStep, value)) RaisePropertyChanged(nameof(HasSelectedStep)); }
- }
- public bool HasSelectedStep => _selectedStep != null;
- private string _sequenceStatusText = "";
- public string SequenceStatusText { get { return _sequenceStatusText; } set { SetProperty(ref _sequenceStatusText, value); } }
- public DelegateCommand AddSequenceCommand { get; }
- public DelegateCommand DeleteSequenceCommand { get; }
- public DelegateCommand RenameSequenceCommand { get; }
- public DelegateCommand<string> AddSequenceStepCommand { get; }
- public DelegateCommand MoveUpStepCommand { get; }
- public DelegateCommand MoveDownStepCommand { get; }
- public DelegateCommand DeleteStepCommand { get; }
- public DelegateCommand ExecuteSequenceCommand { get; }
- public DelegateCommand StopSequenceCommand { get; }
- private void AddSequence()
- {
- if (_info == null) { TeamAAS.DialogHelper.Warning("请先选择供料器"); return; }
- string name = TeamAAS.DialogHelper.Input("流程名称", "新建流程", "新流程");
- if (string.IsNullOrWhiteSpace(name)) return;
- if (_info.Sequences.Any(s => string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase)))
- { TeamAAS.DialogHelper.Warning($"流程 \"{name}\" 已存在,请换一个名称"); return; }
- var seq = new SequenceSet { Name = name };
- _info.Sequences.Add(seq);
- SelectedSequence = seq;
- RenumberSteps();
- SaveSilent();
- }
- private void DeleteSequence()
- {
- if (SelectedSequence == null) return;
- if (!TeamAAS.DialogHelper.ConfirmYesNo($"确认删除流程 \"{SelectedSequence.Name}\" 吗?", "确认")) return;
- var list = CurrentSequences;
- int idx = list.IndexOf(SelectedSequence);
- list.Remove(SelectedSequence);
- SelectedSequence = list.Count > 0 ? list[Math.Min(idx, list.Count - 1)] : null;
- RaisePropertyChanged(nameof(CurrentSequenceSteps));
- RenumberSteps();
- SaveSilent();
- }
- private void RenameSequence()
- {
- if (SelectedSequence == null || _info == null) return;
- string name = TeamAAS.DialogHelper.Input("流程名称", "重命名", SelectedSequence.Name);
- if (string.IsNullOrWhiteSpace(name)) return;
- if (!string.Equals(name, SelectedSequence.Name, StringComparison.OrdinalIgnoreCase) &&
- _info.Sequences.Any(s => string.Equals(s.Name, name, StringComparison.OrdinalIgnoreCase)))
- { TeamAAS.DialogHelper.Warning($"流程 \"{name}\" 已存在,请换一个名称"); return; }
- SelectedSequence.Name = name;
- SaveSilent();
- }
- private void AddSequenceStep(string type)
- {
- if (SelectedSequence == null) { TeamAAS.DialogHelper.Warning("请先选择或创建一个流程"); return; }
- var step = new SequenceStep
- {
- StepType = type == "Delay" ? SequenceStepType.Delay : SequenceStepType.Direction,
- TimeMs = type == "Delay" ? 300 : 500,
- Direction = FeederAction.Right,
- Enabled = true,
- };
- SelectedSequence.Steps.Add(step);
- RenumberSteps();
- SaveSilent();
- }
- private void MoveUpStep()
- {
- if (SelectedStep == null || CurrentSequenceSteps == null) return;
- int idx = CurrentSequenceSteps.IndexOf(SelectedStep);
- if (idx <= 0) return;
- CurrentSequenceSteps.Move(idx, idx - 1);
- SelectedStep = CurrentSequenceSteps[idx - 1];
- RenumberSteps();
- SaveSilent();
- }
- private void MoveDownStep()
- {
- if (SelectedStep == null || CurrentSequenceSteps == null) return;
- int idx = CurrentSequenceSteps.IndexOf(SelectedStep);
- if (idx < 0 || idx >= CurrentSequenceSteps.Count - 1) return;
- CurrentSequenceSteps.Move(idx, idx + 1);
- SelectedStep = CurrentSequenceSteps[idx + 1];
- RenumberSteps();
- SaveSilent();
- }
- private void DeleteStep()
- {
- if (SelectedStep == null || CurrentSequenceSteps == null) return;
- CurrentSequenceSteps.Remove(SelectedStep);
- SelectedStep = null;
- RenumberSteps();
- SaveSilent();
- }
- private void RenumberSteps()
- {
- if (CurrentSequenceSteps == null) return;
- for (int i = 0; i < CurrentSequenceSteps.Count; i++)
- CurrentSequenceSteps[i].Index = i + 1;
- }
- private async Task ExecuteSequenceAsync()
- {
- if (SelectedSequence == null) { TeamAAS.DialogHelper.Warning("请先选择流程"); return; }
- if (!EnsureConnected()) return;
- _sequenceCts = new CancellationTokenSource();
- var token = _sequenceCts.Token;
- try
- {
- int stepIdx = 0;
- foreach (var step in SelectedSequence.Steps)
- {
- stepIdx++;
- if (!step.Enabled) continue;
- if (token.IsCancellationRequested) break;
- if (step.StepType == SequenceStepType.Delay)
- {
- SequenceStatusText = $"等待 {step.TimeMs}ms... ({stepIdx}/{SelectedSequence.Steps.Count})";
- await Task.Delay(step.TimeMs, token);
- }
- else
- {
- SequenceStatusText = $"{step.Direction} 振动 {step.TimeMs}ms ({stepIdx}/{SelectedSequence.Steps.Count})";
- await _feeder.RunDirectionAsync(step.Direction, step.TimeMs > 0 ? step.TimeMs : (int?)null);
- }
- }
- SequenceStatusText = token.IsCancellationRequested ? "流程已停止" : "流程执行完成";
- }
- catch (TaskCanceledException) { SequenceStatusText = "流程已停止"; }
- catch (Exception ex) { SequenceStatusText = "流程执行异常"; TeamAAS.DialogHelper.ShowError("流程执行异常", ex); }
- }
- private void StopSequence()
- {
- try { _sequenceCts?.Cancel(); } catch { }
- try { var t = _feeder?.StopAsync(); } catch { }
- }
- #endregion
- #region 料斗输入输出
- public int[] HopperGroupList { get; } = Enumerable.Range(0, 26).ToArray();
- private int _selectedHopperGroup;
- public int SelectedHopperGroup { get { return _selectedHopperGroup; } set { SetProperty(ref _selectedHopperGroup, value); } }
- private HopperOutputParam _hopperParam = new HopperOutputParam();
- public HopperOutputParam HopperParam { get { return _hopperParam; } set { SetProperty(ref _hopperParam, value); } }
- public DelegateCommand ReadHopperParamCommand { get; }
- public DelegateCommand WriteHopperParamCommand { get; }
- public DelegateCommand RunHopperOutputCommand { get; }
- public DelegateCommand StopHopperOutputCommand { get; }
- private async Task ReadHopperParamAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- StatusText = $"读取料斗输出参数(组 {SelectedHopperGroup})...";
- HopperParam = await _feeder.GetHopperParamAsync(SelectedHopperGroup);
- StatusText = "料斗输出参数已读取";
- }
- catch (Exception ex) { StatusText = "读取料斗参数失败"; TeamAAS.AppLogger.Error("读取料斗输出参数失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("读取料斗参数失败", ex); }
- }
- private async Task WriteHopperParamAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- StatusText = $"下载料斗输出参数(组 {SelectedHopperGroup})...";
- await _feeder.SetHopperParamAsync(SelectedHopperGroup, HopperParam);
- StatusText = "料斗输出参数已下载";
- TeamAAS.DialogHelper.Success("料斗输出参数已下载到设备");
- }
- catch (Exception ex) { StatusText = "下载料斗参数失败"; TeamAAS.AppLogger.Error("下载料斗输出参数失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("下载料斗参数失败", ex); }
- }
- private async Task RunHopperOutputAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- int id = SelectedHopperGroup + 1;
- StatusText = $"执行料斗动作 {id}...";
- await _feeder.RunHopperOutputAsync(id, HopperParam.HopperDuration > 0 ? HopperParam.HopperDuration : (int?)null);
- StatusText = $"料斗动作 {id} 执行完成";
- }
- catch (Exception ex) { StatusText = "料斗动作执行失败"; TeamAAS.AppLogger.Error("执行料斗动作失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("料斗动作执行失败", ex); }
- }
- private async Task StopHopperOutputAsync()
- {
- if (_feeder == null) return;
- try { await _feeder.StopHopperOutputAsync(); StatusText = "料斗振动已停止"; }
- catch (Exception ex) { TeamAAS.AppLogger.Error("停止料斗振动失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("停止料斗振动失败", ex); }
- }
- #endregion
- #region 输入触发序列ID
- private int _inputTrigger1;
- public int InputTrigger1 { get { return _inputTrigger1; } set { SetProperty(ref _inputTrigger1, value); } }
- private int _inputTrigger2;
- public int InputTrigger2 { get { return _inputTrigger2; } set { SetProperty(ref _inputTrigger2, value); } }
- public DelegateCommand ReadInputTriggerCommand { get; }
- public DelegateCommand WriteInputTriggerCommand { get; }
- private async Task ReadInputTriggerAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- InputTrigger1 = await _feeder.GetInputTriggerAsync(0);
- InputTrigger2 = await _feeder.GetInputTriggerAsync(1);
- StatusText = $"输入触发已读取: 输入1={InputTrigger1} 输入2={InputTrigger2}";
- }
- catch (Exception ex) { StatusText = "读取输入触发失败"; TeamAAS.AppLogger.Error("读取输入触发序列ID失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("读取输入触发失败", ex); }
- }
- private async Task WriteInputTriggerAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- await _feeder.SetInputTriggerAsync(0, InputTrigger1);
- await _feeder.SetInputTriggerAsync(1, InputTrigger2);
- StatusText = "输入触发已写入";
- TeamAAS.DialogHelper.Success("输入触发参数已写入设备");
- }
- catch (Exception ex) { StatusText = "写入输入触发失败"; TeamAAS.AppLogger.Error("写入输入触发序列ID失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("写入输入触发失败", ex); }
- }
- #endregion
- #region 系统参数(超时/背光)
- private FeederSystemParam _systemParam = new FeederSystemParam();
- public FeederSystemParam SystemParam { get { return _systemParam; } set { SetProperty(ref _systemParam, value); } }
- public DelegateCommand ReadSystemParamCommand { get; }
- public DelegateCommand WriteSystemParamCommand { get; }
- private async Task ReadSystemParamAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- StatusText = "读取系统参数...";
- SystemParam = await _feeder.GetSystemParamAsync();
- StatusText = "系统参数已读取";
- }
- catch (Exception ex) { StatusText = "读取系统参数失败"; TeamAAS.AppLogger.Error("读取供料器系统参数失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("读取系统参数失败", ex); }
- }
- private async Task WriteSystemParamAsync()
- {
- if (!EnsureConnected()) return;
- try
- {
- StatusText = "写入系统参数...";
- await _feeder.SetSystemParamAsync(SystemParam);
- StatusText = "系统参数已写入并保存";
- TeamAAS.DialogHelper.Success("系统参数已写入设备");
- }
- catch (Exception ex) { StatusText = "写入系统参数失败"; TeamAAS.AppLogger.Error("写入供料器系统参数失败", ex, LogSource); TeamAAS.DialogHelper.ShowError("写入系统参数失败", ex); }
- }
- #endregion
- }
- }
|