SettingViewModel.FeederExt.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using System.Windows;
  6. using Prism.Commands;
  7. using TeamAAS.Feeder.Interfaces;
  8. using TeamAAS.Feeder.Models;
  9. using TeamAAS.Views;
  10. namespace TeamAAS.ViewModels
  11. {
  12. /// <summary>
  13. /// 供料器扩展功能(partial SettingViewModel):
  14. /// 系统参数(超时)、料斗输入输出、输入触发、收发通讯日志、Feeder IP 自动搜索。
  15. /// 命令均采用懒加载,避免改动主构造函数。
  16. /// </summary>
  17. public partial class SettingViewModel
  18. {
  19. private const string FeederLogSource = "Feeder";
  20. #region 收发通讯日志(SendLog/ReceiveLog)
  21. /// <summary>收发报文日志(仅界面显示,不落文件)。</summary>
  22. public ObservableCollection<FeederCommLogEntry> CommLogs { get; }
  23. = new ObservableCollection<FeederCommLogEntry>();
  24. private const int MaxCommLogEntries = 500;
  25. /// <summary>订阅自 IFeeder.MessageExchanged,可能在非 UI 线程触发。</summary>
  26. private void OnFeederMessageExchanged(IFeeder dev, string content, bool isSend)
  27. {
  28. var entry = new FeederCommLogEntry
  29. {
  30. Time = DateTime.Now,
  31. IsSend = isSend,
  32. Content = content
  33. };
  34. var dispatcher = Application.Current?.Dispatcher;
  35. if (dispatcher != null && !dispatcher.CheckAccess())
  36. dispatcher.BeginInvoke(new Action(() => AppendCommLog(entry)));
  37. else
  38. AppendCommLog(entry);
  39. }
  40. private void AppendCommLog(FeederCommLogEntry entry)
  41. {
  42. CommLogs.Add(entry);
  43. while (CommLogs.Count > MaxCommLogEntries)
  44. CommLogs.RemoveAt(0);
  45. }
  46. private DelegateCommand _clearCommLogCommand;
  47. public DelegateCommand ClearCommLogCommand =>
  48. _clearCommLogCommand ?? (_clearCommLogCommand = new DelegateCommand(() => CommLogs.Clear()));
  49. #endregion
  50. #region 系统参数(超时/背光)
  51. private FeederSystemParam _systemParam = new FeederSystemParam();
  52. public FeederSystemParam SystemParam
  53. {
  54. get { return _systemParam; }
  55. set { SetProperty(ref _systemParam, value); }
  56. }
  57. private DelegateCommand _readSystemParamCommand;
  58. public DelegateCommand ReadSystemParamCommand =>
  59. _readSystemParamCommand ?? (_readSystemParamCommand = new DelegateCommand(async () => await ReadSystemParamAsync()));
  60. private DelegateCommand _writeSystemParamCommand;
  61. public DelegateCommand WriteSystemParamCommand =>
  62. _writeSystemParamCommand ?? (_writeSystemParamCommand = new DelegateCommand(async () => await WriteSystemParamAsync()));
  63. private async Task ReadSystemParamAsync()
  64. {
  65. if (!EnsureFeederConnected()) return;
  66. try
  67. {
  68. FeederStatusText = "读取系统参数...";
  69. var p = await _activeDevice.GetSystemParamAsync();
  70. SystemParam = p;
  71. FeederStatusText = "系统参数已读取";
  72. AppLogger.Info($"读取系统参数: 背光超时={p.LightTimeout} 亮度={p.LightBrightness} 闪烁={p.LightFlashTime} 平台振动超时={p.PlatShakeTimeout} 料斗振动超时={p.HopperShakeTimeout}", FeederLogSource);
  73. }
  74. catch (Exception ex)
  75. {
  76. FeederStatusText = "读取系统参数失败";
  77. AppLogger.Error("读取供料器系统参数失败", ex, FeederLogSource);
  78. DialogHelper.ShowError("读取系统参数失败", ex);
  79. }
  80. }
  81. private async Task WriteSystemParamAsync()
  82. {
  83. if (!EnsureFeederConnected()) return;
  84. try
  85. {
  86. FeederStatusText = "写入系统参数...";
  87. var p = SystemParam;
  88. await _activeDevice.SetSystemParamAsync(p);
  89. FeederStatusText = "系统参数已写入并保存";
  90. AppLogger.Info($"写入系统参数: 背光超时={p.LightTimeout} 亮度={p.LightBrightness} 闪烁={p.LightFlashTime} 平台振动超时={p.PlatShakeTimeout} 料斗振动超时={p.HopperShakeTimeout}", FeederLogSource);
  91. DialogHelper.Success("系统参数已写入设备");
  92. }
  93. catch (Exception ex)
  94. {
  95. FeederStatusText = "写入系统参数失败";
  96. AppLogger.Error("写入供料器系统参数失败", ex, FeederLogSource);
  97. DialogHelper.ShowError("写入系统参数失败", ex);
  98. }
  99. }
  100. #endregion
  101. #region 料斗输入输出
  102. /// <summary>料斗输出参数组序号(0~25)。</summary>
  103. public int[] HopperGroupList { get; } = Enumerable.Range(0, 26).ToArray();
  104. private int _selectedHopperGroup;
  105. public int SelectedHopperGroup
  106. {
  107. get { return _selectedHopperGroup; }
  108. set { SetProperty(ref _selectedHopperGroup, value); }
  109. }
  110. private HopperOutputParam _hopperParam = new HopperOutputParam();
  111. public HopperOutputParam HopperParam
  112. {
  113. get { return _hopperParam; }
  114. set { SetProperty(ref _hopperParam, value); }
  115. }
  116. private DelegateCommand _readHopperParamCommand;
  117. public DelegateCommand ReadHopperParamCommand =>
  118. _readHopperParamCommand ?? (_readHopperParamCommand = new DelegateCommand(async () => await ReadHopperParamAsync()));
  119. private DelegateCommand _writeHopperParamCommand;
  120. public DelegateCommand WriteHopperParamCommand =>
  121. _writeHopperParamCommand ?? (_writeHopperParamCommand = new DelegateCommand(async () => await WriteHopperParamAsync()));
  122. private DelegateCommand _runHopperOutputCommand;
  123. public DelegateCommand RunHopperOutputCommand =>
  124. _runHopperOutputCommand ?? (_runHopperOutputCommand = new DelegateCommand(async () => await RunHopperOutputAsync()));
  125. private DelegateCommand _stopHopperOutputCommand;
  126. public DelegateCommand StopHopperOutputCommand =>
  127. _stopHopperOutputCommand ?? (_stopHopperOutputCommand = new DelegateCommand(async () => await StopHopperOutputAsync()));
  128. private async Task ReadHopperParamAsync()
  129. {
  130. if (!EnsureFeederConnected()) return;
  131. try
  132. {
  133. FeederStatusText = $"读取料斗输出参数(组 {SelectedHopperGroup})...";
  134. HopperParam = await _activeDevice.GetHopperParamAsync(SelectedHopperGroup);
  135. FeederStatusText = "料斗输出参数已读取";
  136. AppLogger.Info($"读取料斗输出参数 组={SelectedHopperGroup}", FeederLogSource);
  137. }
  138. catch (Exception ex)
  139. {
  140. FeederStatusText = "读取料斗参数失败";
  141. AppLogger.Error("读取料斗输出参数失败", ex, FeederLogSource);
  142. DialogHelper.ShowError("读取料斗参数失败", ex);
  143. }
  144. }
  145. private async Task WriteHopperParamAsync()
  146. {
  147. if (!EnsureFeederConnected()) return;
  148. try
  149. {
  150. FeederStatusText = $"下载料斗输出参数(组 {SelectedHopperGroup})...";
  151. await _activeDevice.SetHopperParamAsync(SelectedHopperGroup, HopperParam);
  152. FeederStatusText = "料斗输出参数已下载";
  153. AppLogger.Info($"下载料斗输出参数 组={SelectedHopperGroup} 数字1={HopperParam.HopperDigitalOutput1} 模拟1={HopperParam.HopperAnalogOutput1} 数字2={HopperParam.HopperDigitalOutput2} 模拟2={HopperParam.HopperAnalogOutput2} 持续={HopperParam.HopperDuration}ms", FeederLogSource);
  154. DialogHelper.Success("料斗输出参数已下载到设备");
  155. }
  156. catch (Exception ex)
  157. {
  158. FeederStatusText = "下载料斗参数失败";
  159. AppLogger.Error("下载料斗输出参数失败", ex, FeederLogSource);
  160. DialogHelper.ShowError("下载料斗参数失败", ex);
  161. }
  162. }
  163. private async Task RunHopperOutputAsync()
  164. {
  165. if (!EnsureFeederConnected()) return;
  166. try
  167. {
  168. int id = SelectedHopperGroup + 1;
  169. FeederStatusText = $"执行料斗动作 {id}...";
  170. await _activeDevice.RunHopperOutputAsync(id, HopperParam.HopperDuration > 0 ? HopperParam.HopperDuration : (int?)null);
  171. FeederStatusText = $"料斗动作 {id} 执行完成";
  172. AppLogger.Info($"执行料斗动作 id={id} 持续={HopperParam.HopperDuration}ms", FeederLogSource);
  173. }
  174. catch (Exception ex)
  175. {
  176. FeederStatusText = "料斗动作执行失败";
  177. AppLogger.Error("执行料斗动作失败", ex, FeederLogSource);
  178. DialogHelper.ShowError("料斗动作执行失败", ex);
  179. }
  180. }
  181. private async Task StopHopperOutputAsync()
  182. {
  183. if (_activeDevice == null) return;
  184. try
  185. {
  186. await _activeDevice.StopHopperOutputAsync();
  187. FeederStatusText = "料斗振动已停止";
  188. AppLogger.Info("停止料斗振动", FeederLogSource);
  189. }
  190. catch (Exception ex)
  191. {
  192. AppLogger.Error("停止料斗振动失败", ex, FeederLogSource);
  193. DialogHelper.ShowError("停止料斗振动失败", ex);
  194. }
  195. }
  196. #endregion
  197. #region 输入触发序列ID
  198. private int _inputTrigger1;
  199. public int InputTrigger1
  200. {
  201. get { return _inputTrigger1; }
  202. set { SetProperty(ref _inputTrigger1, value); }
  203. }
  204. private int _inputTrigger2;
  205. public int InputTrigger2
  206. {
  207. get { return _inputTrigger2; }
  208. set { SetProperty(ref _inputTrigger2, value); }
  209. }
  210. private DelegateCommand _readInputTriggerCommand;
  211. public DelegateCommand ReadInputTriggerCommand =>
  212. _readInputTriggerCommand ?? (_readInputTriggerCommand = new DelegateCommand(async () => await ReadInputTriggerAsync()));
  213. private DelegateCommand _writeInputTriggerCommand;
  214. public DelegateCommand WriteInputTriggerCommand =>
  215. _writeInputTriggerCommand ?? (_writeInputTriggerCommand = new DelegateCommand(async () => await WriteInputTriggerAsync()));
  216. private async Task ReadInputTriggerAsync()
  217. {
  218. if (!EnsureFeederConnected()) return;
  219. try
  220. {
  221. InputTrigger1 = await _activeDevice.GetInputTriggerAsync(0);
  222. InputTrigger2 = await _activeDevice.GetInputTriggerAsync(1);
  223. FeederStatusText = $"输入触发已读取: 输入1={InputTrigger1} 输入2={InputTrigger2}";
  224. AppLogger.Info($"读取输入触发序列ID: 输入1={InputTrigger1} 输入2={InputTrigger2}", FeederLogSource);
  225. }
  226. catch (Exception ex)
  227. {
  228. FeederStatusText = "读取输入触发失败";
  229. AppLogger.Error("读取输入触发序列ID失败", ex, FeederLogSource);
  230. DialogHelper.ShowError("读取输入触发失败", ex);
  231. }
  232. }
  233. private async Task WriteInputTriggerAsync()
  234. {
  235. if (!EnsureFeederConnected()) return;
  236. try
  237. {
  238. await _activeDevice.SetInputTriggerAsync(0, InputTrigger1);
  239. await _activeDevice.SetInputTriggerAsync(1, InputTrigger2);
  240. FeederStatusText = "输入触发已写入";
  241. AppLogger.Info($"写入输入触发序列ID: 输入1={InputTrigger1} 输入2={InputTrigger2}", FeederLogSource);
  242. DialogHelper.Success("输入触发参数已写入设备");
  243. }
  244. catch (Exception ex)
  245. {
  246. FeederStatusText = "写入输入触发失败";
  247. AppLogger.Error("写入输入触发序列ID失败", ex, FeederLogSource);
  248. DialogHelper.ShowError("写入输入触发失败", ex);
  249. }
  250. }
  251. #endregion
  252. #region Feeder IP 自动搜索
  253. private DelegateCommand _searchFeederIpCommand;
  254. public DelegateCommand SearchFeederIpCommand =>
  255. _searchFeederIpCommand ?? (_searchFeederIpCommand = new DelegateCommand(OpenFeederSearchDialog));
  256. private void OpenFeederSearchDialog()
  257. {
  258. if (SelectedFeeder == null)
  259. {
  260. DialogHelper.Warning("请先选择或添加一个供料器");
  261. return;
  262. }
  263. var dialog = new SearchFeederWindow { Owner = Application.Current.MainWindow };
  264. if (dialog.ShowDialog() == true && dialog.SelectedResult != null)
  265. {
  266. var r = dialog.SelectedResult;
  267. SelectedFeeder.IP = r.IP;
  268. SelectedFeeder.Port = r.Port;
  269. FeederStatusText = $"已回填 {r.IP}:{r.Port}";
  270. AppLogger.Info($"搜索回填 Feeder IP: {r.FeederType} {r.IP}:{r.Port} MAC={r.MAC}", FeederLogSource);
  271. }
  272. }
  273. #endregion
  274. /// <summary>确保已连接供料器;未连接则提示并返回 false。</summary>
  275. private bool EnsureFeederConnected()
  276. {
  277. if (_activeDevice == null || !_activeDevice.IsConnected)
  278. {
  279. DialogHelper.Warning("请先连接供料器");
  280. return false;
  281. }
  282. return true;
  283. }
  284. }
  285. /// <summary>供料器收发报文日志条目(界面 SendLog/ReceiveLog 显示用)。</summary>
  286. public class FeederCommLogEntry
  287. {
  288. public DateTime Time { get; set; }
  289. public bool IsSend { get; set; }
  290. public string Content { get; set; }
  291. public string Direction => IsSend ? "TX →" : "RX ←";
  292. public string TimeText => Time.ToString("HH:mm:ss.fff");
  293. public string Display => $"{TimeText} {Direction} {Content}";
  294. }
  295. }