using System; using System.Collections.ObjectModel; using System.Linq; using System.Threading.Tasks; using System.Windows; using Prism.Commands; using TeamAAS.Feeder.Interfaces; using TeamAAS.Feeder.Models; using TeamAAS.Views; namespace TeamAAS.ViewModels { /// /// 供料器扩展功能(partial SettingViewModel): /// 系统参数(超时)、料斗输入输出、输入触发、收发通讯日志、Feeder IP 自动搜索。 /// 命令均采用懒加载,避免改动主构造函数。 /// public partial class SettingViewModel { private const string FeederLogSource = "Feeder"; #region 收发通讯日志(SendLog/ReceiveLog) /// 收发报文日志(仅界面显示,不落文件)。 public ObservableCollection CommLogs { get; } = new ObservableCollection(); private const int MaxCommLogEntries = 500; /// 订阅自 IFeeder.MessageExchanged,可能在非 UI 线程触发。 private void OnFeederMessageExchanged(IFeeder dev, string content, bool isSend) { var entry = new FeederCommLogEntry { Time = DateTime.Now, IsSend = isSend, Content = content }; var dispatcher = Application.Current?.Dispatcher; if (dispatcher != null && !dispatcher.CheckAccess()) dispatcher.BeginInvoke(new Action(() => AppendCommLog(entry))); else AppendCommLog(entry); } private void AppendCommLog(FeederCommLogEntry entry) { CommLogs.Add(entry); while (CommLogs.Count > MaxCommLogEntries) CommLogs.RemoveAt(0); } private DelegateCommand _clearCommLogCommand; public DelegateCommand ClearCommLogCommand => _clearCommLogCommand ?? (_clearCommLogCommand = new DelegateCommand(() => CommLogs.Clear())); #endregion #region 系统参数(超时/背光) private FeederSystemParam _systemParam = new FeederSystemParam(); public FeederSystemParam SystemParam { get { return _systemParam; } set { SetProperty(ref _systemParam, value); } } private DelegateCommand _readSystemParamCommand; public DelegateCommand ReadSystemParamCommand => _readSystemParamCommand ?? (_readSystemParamCommand = new DelegateCommand(async () => await ReadSystemParamAsync())); private DelegateCommand _writeSystemParamCommand; public DelegateCommand WriteSystemParamCommand => _writeSystemParamCommand ?? (_writeSystemParamCommand = new DelegateCommand(async () => await WriteSystemParamAsync())); private async Task ReadSystemParamAsync() { if (!EnsureFeederConnected()) return; try { FeederStatusText = "读取系统参数..."; var p = await _activeDevice.GetSystemParamAsync(); SystemParam = p; FeederStatusText = "系统参数已读取"; AppLogger.Info($"读取系统参数: 背光超时={p.LightTimeout} 亮度={p.LightBrightness} 闪烁={p.LightFlashTime} 平台振动超时={p.PlatShakeTimeout} 料斗振动超时={p.HopperShakeTimeout}", FeederLogSource); } catch (Exception ex) { FeederStatusText = "读取系统参数失败"; AppLogger.Error("读取供料器系统参数失败", ex, FeederLogSource); DialogHelper.ShowError("读取系统参数失败", ex); } } private async Task WriteSystemParamAsync() { if (!EnsureFeederConnected()) return; try { FeederStatusText = "写入系统参数..."; var p = SystemParam; await _activeDevice.SetSystemParamAsync(p); FeederStatusText = "系统参数已写入并保存"; AppLogger.Info($"写入系统参数: 背光超时={p.LightTimeout} 亮度={p.LightBrightness} 闪烁={p.LightFlashTime} 平台振动超时={p.PlatShakeTimeout} 料斗振动超时={p.HopperShakeTimeout}", FeederLogSource); DialogHelper.Success("系统参数已写入设备"); } catch (Exception ex) { FeederStatusText = "写入系统参数失败"; AppLogger.Error("写入供料器系统参数失败", ex, FeederLogSource); DialogHelper.ShowError("写入系统参数失败", ex); } } #endregion #region 料斗输入输出 /// 料斗输出参数组序号(0~25)。 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); } } private DelegateCommand _readHopperParamCommand; public DelegateCommand ReadHopperParamCommand => _readHopperParamCommand ?? (_readHopperParamCommand = new DelegateCommand(async () => await ReadHopperParamAsync())); private DelegateCommand _writeHopperParamCommand; public DelegateCommand WriteHopperParamCommand => _writeHopperParamCommand ?? (_writeHopperParamCommand = new DelegateCommand(async () => await WriteHopperParamAsync())); private DelegateCommand _runHopperOutputCommand; public DelegateCommand RunHopperOutputCommand => _runHopperOutputCommand ?? (_runHopperOutputCommand = new DelegateCommand(async () => await RunHopperOutputAsync())); private DelegateCommand _stopHopperOutputCommand; public DelegateCommand StopHopperOutputCommand => _stopHopperOutputCommand ?? (_stopHopperOutputCommand = new DelegateCommand(async () => await StopHopperOutputAsync())); private async Task ReadHopperParamAsync() { if (!EnsureFeederConnected()) return; try { FeederStatusText = $"读取料斗输出参数(组 {SelectedHopperGroup})..."; HopperParam = await _activeDevice.GetHopperParamAsync(SelectedHopperGroup); FeederStatusText = "料斗输出参数已读取"; AppLogger.Info($"读取料斗输出参数 组={SelectedHopperGroup}", FeederLogSource); } catch (Exception ex) { FeederStatusText = "读取料斗参数失败"; AppLogger.Error("读取料斗输出参数失败", ex, FeederLogSource); DialogHelper.ShowError("读取料斗参数失败", ex); } } private async Task WriteHopperParamAsync() { if (!EnsureFeederConnected()) return; try { FeederStatusText = $"下载料斗输出参数(组 {SelectedHopperGroup})..."; await _activeDevice.SetHopperParamAsync(SelectedHopperGroup, HopperParam); FeederStatusText = "料斗输出参数已下载"; AppLogger.Info($"下载料斗输出参数 组={SelectedHopperGroup} 数字1={HopperParam.HopperDigitalOutput1} 模拟1={HopperParam.HopperAnalogOutput1} 数字2={HopperParam.HopperDigitalOutput2} 模拟2={HopperParam.HopperAnalogOutput2} 持续={HopperParam.HopperDuration}ms", FeederLogSource); DialogHelper.Success("料斗输出参数已下载到设备"); } catch (Exception ex) { FeederStatusText = "下载料斗参数失败"; AppLogger.Error("下载料斗输出参数失败", ex, FeederLogSource); DialogHelper.ShowError("下载料斗参数失败", ex); } } private async Task RunHopperOutputAsync() { if (!EnsureFeederConnected()) return; try { int id = SelectedHopperGroup + 1; FeederStatusText = $"执行料斗动作 {id}..."; await _activeDevice.RunHopperOutputAsync(id, HopperParam.HopperDuration > 0 ? HopperParam.HopperDuration : (int?)null); FeederStatusText = $"料斗动作 {id} 执行完成"; AppLogger.Info($"执行料斗动作 id={id} 持续={HopperParam.HopperDuration}ms", FeederLogSource); } catch (Exception ex) { FeederStatusText = "料斗动作执行失败"; AppLogger.Error("执行料斗动作失败", ex, FeederLogSource); DialogHelper.ShowError("料斗动作执行失败", ex); } } private async Task StopHopperOutputAsync() { if (_activeDevice == null) return; try { await _activeDevice.StopHopperOutputAsync(); FeederStatusText = "料斗振动已停止"; AppLogger.Info("停止料斗振动", FeederLogSource); } catch (Exception ex) { AppLogger.Error("停止料斗振动失败", ex, FeederLogSource); 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); } } private DelegateCommand _readInputTriggerCommand; public DelegateCommand ReadInputTriggerCommand => _readInputTriggerCommand ?? (_readInputTriggerCommand = new DelegateCommand(async () => await ReadInputTriggerAsync())); private DelegateCommand _writeInputTriggerCommand; public DelegateCommand WriteInputTriggerCommand => _writeInputTriggerCommand ?? (_writeInputTriggerCommand = new DelegateCommand(async () => await WriteInputTriggerAsync())); private async Task ReadInputTriggerAsync() { if (!EnsureFeederConnected()) return; try { InputTrigger1 = await _activeDevice.GetInputTriggerAsync(0); InputTrigger2 = await _activeDevice.GetInputTriggerAsync(1); FeederStatusText = $"输入触发已读取: 输入1={InputTrigger1} 输入2={InputTrigger2}"; AppLogger.Info($"读取输入触发序列ID: 输入1={InputTrigger1} 输入2={InputTrigger2}", FeederLogSource); } catch (Exception ex) { FeederStatusText = "读取输入触发失败"; AppLogger.Error("读取输入触发序列ID失败", ex, FeederLogSource); DialogHelper.ShowError("读取输入触发失败", ex); } } private async Task WriteInputTriggerAsync() { if (!EnsureFeederConnected()) return; try { await _activeDevice.SetInputTriggerAsync(0, InputTrigger1); await _activeDevice.SetInputTriggerAsync(1, InputTrigger2); FeederStatusText = "输入触发已写入"; AppLogger.Info($"写入输入触发序列ID: 输入1={InputTrigger1} 输入2={InputTrigger2}", FeederLogSource); DialogHelper.Success("输入触发参数已写入设备"); } catch (Exception ex) { FeederStatusText = "写入输入触发失败"; AppLogger.Error("写入输入触发序列ID失败", ex, FeederLogSource); DialogHelper.ShowError("写入输入触发失败", ex); } } #endregion #region Feeder IP 自动搜索 private DelegateCommand _searchFeederIpCommand; public DelegateCommand SearchFeederIpCommand => _searchFeederIpCommand ?? (_searchFeederIpCommand = new DelegateCommand(OpenFeederSearchDialog)); private void OpenFeederSearchDialog() { if (SelectedFeeder == null) { DialogHelper.Warning("请先选择或添加一个供料器"); return; } var dialog = new SearchFeederWindow { Owner = Application.Current.MainWindow }; if (dialog.ShowDialog() == true && dialog.SelectedResult != null) { var r = dialog.SelectedResult; SelectedFeeder.IP = r.IP; SelectedFeeder.Port = r.Port; FeederStatusText = $"已回填 {r.IP}:{r.Port}"; AppLogger.Info($"搜索回填 Feeder IP: {r.FeederType} {r.IP}:{r.Port} MAC={r.MAC}", FeederLogSource); } } #endregion /// 确保已连接供料器;未连接则提示并返回 false。 private bool EnsureFeederConnected() { if (_activeDevice == null || !_activeDevice.IsConnected) { DialogHelper.Warning("请先连接供料器"); return false; } return true; } } /// 供料器收发报文日志条目(界面 SendLog/ReceiveLog 显示用)。 public class FeederCommLogEntry { public DateTime Time { get; set; } public bool IsSend { get; set; } public string Content { get; set; } public string Direction => IsSend ? "TX →" : "RX ←"; public string TimeText => Time.ToString("HH:mm:ss.fff"); public string Display => $"{TimeText} {Direction} {Content}"; } }