UCDeviceFinder.xaml.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. using MV485.Dlg;
  2. using MV485.helper;
  3. using MV485.model;
  4. using MV485.util;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Collections.ObjectModel;
  8. using System.IO;
  9. using System.Linq;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. using System.Windows;
  13. using System.Windows.Controls;
  14. using System.Windows.Data;
  15. using System.Windows.Documents;
  16. using System.Windows.Input;
  17. using System.Windows.Media;
  18. using System.Windows.Media.Imaging;
  19. using System.Windows.Navigation;
  20. using System.Windows.Shapes;
  21. using Path = System.IO.Path;
  22. namespace MV485.uc
  23. {
  24. /// <summary>
  25. /// UCDeviceFinder.xaml 的交互逻辑
  26. /// </summary>
  27. public partial class UCDeviceFinder : UserControl
  28. {
  29. private static readonly string LogFilePath =
  30. Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Documents", "search_log.txt");
  31. private FindDeviceHelper _deviceFinder;
  32. private List<LogEntry> _logEntries = new List<LogEntry>();
  33. private bool _isSearching = false; // 记录搜索状态
  34. //已找到的结果
  35. //private List<DeviceAddressItem> _findDevices = new List<DeviceAddressItem>();
  36. private ObservableCollection<DeviceAddressItem> _findDevices { get; set; }
  37. private DlgSearchDevices _dlgSearch;
  38. public UCDeviceFinder()
  39. {
  40. InitializeComponent();
  41. LoadSerialPorts();
  42. LoadBaudrates();
  43. LoadDevIdRange();
  44. LoadTimeoutPer(); //每设备超时时间
  45. LoadConfigLog(); //加载原来的日志
  46. //指定搜索结果的数据源
  47. _findDevices = new ObservableCollection<DeviceAddressItem>();
  48. lvResult.ItemsSource = _findDevices;
  49. _deviceFinder = new FindDeviceHelper();
  50. _deviceFinder.SearchFinished += OnSearchFinished;
  51. _deviceFinder.SearchDevice += _deviceFinder_SearchDevice;
  52. _deviceFinder.SearchLog += _deviceFinder_SearchLog;
  53. _deviceFinder.SerialConnected += _deviceFinder_SerialConnected;
  54. _deviceFinder.SerialStatusChanged += _deviceFinder_SerialStatusChanged;
  55. if(bool.TryParse(ConfigManager.Instance.GetConfigValue(ConfigKey.IsSearchOne, "True"),out bool blSearchOne))
  56. {
  57. chkSearchSingle.IsChecked = blSearchOne;
  58. }
  59. chkSearchSingle.Checked += (sender, e) =>
  60. {
  61. ConfigManager.Instance.UpdateConfig(ConfigKey.IsSearchOne, chkSearchSingle.IsChecked.GetValueOrDefault().ToString());
  62. };
  63. chkSearchSingle.Unchecked += (sender, e) =>
  64. {
  65. ConfigManager.Instance.UpdateConfig(ConfigKey.IsSearchOne, chkSearchSingle.IsChecked.GetValueOrDefault().ToString());
  66. };
  67. }
  68. private void LoadSerialPorts()
  69. {
  70. Dispatcher.BeginInvoke(new Action(() => {
  71. cmbPortNames.ItemsSource = SerialHelper.GetSerialPortsWithDetails();
  72. if (cmbPortNames.Items.Count > 0)
  73. cmbPortNames.SelectedIndex = 0;
  74. var portName = ConfigManager.Instance.GetConfigValue(ConfigKey.ConfigPortName, "");
  75. cmbPortNames.SelectedValue = portName;
  76. }));
  77. }
  78. //加载备选的波特率
  79. private void LoadBaudrates()
  80. {
  81. //mcmbBaudrate.SetItems(new string[] {
  82. // "115200", "57600", "38400", "19200", "9600", "14400",
  83. // "56000", "4800", "2400", "1200", "600", "300", "110"
  84. //});
  85. mcmbBaudrate.SetItems(SerialHelper.BaudRates);
  86. //mcmbBaudrate.SetSelectedValues("115200,9600");
  87. var baudrates = ConfigManager.Instance.GetConfigValue(ConfigKey.SearchBaudrate, "");
  88. mcmbBaudrate.SetSelectedValues(baudrates);
  89. }
  90. //加载设备地址范围
  91. private void LoadDevIdRange()
  92. {
  93. txtMinId.Text = "";
  94. txtMaxId.Text = "";
  95. var idRange = ConfigManager.Instance.GetConfigValue(ConfigKey.DevIdRange, null);
  96. if (idRange != null)
  97. {
  98. // 使用 Split 分割字符串
  99. string[] parts = idRange.Split(',');
  100. // 确保分割后有两个元素
  101. if (parts.Length == 2 && int.TryParse(parts[0], out int minId) && int.TryParse(parts[1], out int maxId))
  102. {
  103. // 成功转换为整数
  104. //Console.WriteLine($"Min ID: {minId}, Max ID: {maxId}");
  105. txtMinId.Text = minId.ToString();
  106. txtMaxId.Text = maxId.ToString();
  107. }
  108. }//if
  109. }
  110. private void LoadTimeoutPer()
  111. {
  112. txtIdTimeout.Text = 100.ToString();
  113. var timeout = ConfigManager.Instance.GetConfigValue(ConfigKey.SerachTimeoutPer, "100");
  114. if(int.TryParse(timeout,out int iTimeout))
  115. {
  116. txtIdTimeout.Text = iTimeout.ToString();
  117. }
  118. }
  119. private void btnSearch_Click(object sender, RoutedEventArgs e)
  120. {
  121. if (_isSearching)
  122. {
  123. StopSearch();
  124. }
  125. else
  126. {
  127. _findDevices.Clear();
  128. StartSearch();
  129. }
  130. }
  131. private async void StartSearch()
  132. {
  133. // MessageBox.Show(this, "这是一个消息框", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
  134. if (cmbPortNames.SelectedItem == null)
  135. {
  136. MessageBox.Show(Application.Current.MainWindow,"请选择串口", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
  137. return;
  138. }
  139. var portName = cmbPortNames.SelectedValue.ToString();
  140. ConfigManager.Instance.UpdateConfig(ConfigKey.ConfigPortName, portName);
  141. //获取选中的波特率数据
  142. string sBaudrates = mcmbBaudrate.GetSelectedItems();
  143. if (string.IsNullOrEmpty(sBaudrates))
  144. {
  145. MessageBox.Show(Application.Current.MainWindow,"请选择要搜索的波特率","提示",MessageBoxButton.OK,MessageBoxImage.Warning);
  146. return;
  147. }
  148. //保存选择的值
  149. ConfigManager.Instance.UpdateConfig(ConfigKey.SearchBaudrate, sBaudrates);
  150. if (!byte.TryParse(txtMinId.Text, out byte minId) ||
  151. !byte.TryParse(txtMaxId.Text, out byte maxId) ||
  152. minId < 0 || minId > 255 || maxId < 0 || maxId > 255 || minId > maxId)
  153. {
  154. MessageBox.Show(Application.Current.MainWindow, "请填入正确的地址范围","提示",MessageBoxButton.OK,MessageBoxImage.Warning);
  155. return;
  156. }
  157. string idRanges = $"{minId},{maxId}";
  158. //保存用户的选择
  159. ConfigManager.Instance.UpdateConfig(ConfigKey.DevIdRange, idRanges);
  160. if(!int.TryParse(txtIdTimeout.Text,out int iTimeout) || iTimeout < 10 || iTimeout > 1000)
  161. {
  162. MessageBox.Show(Application.Current.MainWindow, "请输入正确的超时时间(10-1000)ms", "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
  163. return;
  164. }
  165. ConfigManager.Instance.UpdateConfig(ConfigKey.SerachTimeoutPer, iTimeout.ToString());
  166. //string portName = cmbPortNames.SelectedValue.ToString();
  167. //_deviceFinder = new FindDeviceHelper(portName);
  168. //_deviceFinder.SearchFinished += OnSearchFinished;
  169. //_deviceFinder.SearchDevice += _deviceFinder_SearchDevice;
  170. //_deviceFinder.SearchLog += _deviceFinder_SearchLog;
  171. //_deviceFinder.SerialConnected += _deviceFinder_SerialConnected;
  172. //_deviceFinder.SerialStatusChanged += _deviceFinder_SerialStatusChanged;
  173. _isSearching = true;
  174. ChangeBtnSearchStatus(_isSearching);
  175. //AppendLog("开始搜索设备,串口:" + portName);
  176. //string titleInfo = "正在搜索设备...";
  177. //WaitWindow waitWindow = new WaitWindow(titleInfo)
  178. //{
  179. // Owner = Application.Current.MainWindow,
  180. // WindowStartupLocation = WindowStartupLocation.CenterOwner
  181. //};
  182. //waitWindow.Show();
  183. _dlgSearch = new DlgSearchDevices()
  184. {
  185. Owner = Application.Current.MainWindow,
  186. WindowStartupLocation = WindowStartupLocation.CenterOwner
  187. };
  188. _dlgSearch.OnStopTask += () =>
  189. {
  190. StopSearch();
  191. };
  192. _dlgSearch.Show();
  193. Application.Current.MainWindow.IsEnabled = false;
  194. try
  195. {
  196. bool blSearchOne = chkSearchSingle.IsChecked == true ? true : false;
  197. await Task.Run(() => _deviceFinder.StartSearch(portName,sBaudrates, minId, maxId,blSearchOne));
  198. }
  199. catch { }
  200. finally
  201. {
  202. //waitWindow.Close();
  203. _dlgSearch?.Close();
  204. Application.Current.MainWindow.IsEnabled = true;
  205. string findCounMsg = _deviceFinder._findCount == 0 ?
  206. "未搜索到设备" : $"已搜索到{_deviceFinder._findCount}台设备";
  207. MessageBox.Show(Application.Current.MainWindow, findCounMsg, "提示",
  208. MessageBoxButton.OK, MessageBoxImage.Information);
  209. //StopSearch();
  210. }
  211. }
  212. private void _deviceFinder_SerialStatusChanged(bool blStatus)
  213. {
  214. //throw new NotImplementedException();
  215. }
  216. private void _deviceFinder_SerialConnected(bool blConnected)
  217. {
  218. //throw new NotImplementedException();
  219. }
  220. private void StopSearch()
  221. {
  222. _deviceFinder?.StopSearch();
  223. _isSearching = false;
  224. ChangeBtnSearchStatus(_isSearching);
  225. //AppendLog("搜索已停止");
  226. }
  227. private void btnRefreshPorts_Click(object sender, RoutedEventArgs e)
  228. {
  229. LoadSerialPorts();
  230. AppendLog("串口列表已刷新");
  231. }
  232. private void btnClearLog_Click(object sender, RoutedEventArgs e)
  233. {
  234. _logEntries.Clear();
  235. lvLogs.ItemsSource = null;
  236. File.WriteAllText(LogFilePath, "");
  237. AppendLog("日志已清空");
  238. }
  239. private void _deviceFinder_SearchDevice(string portName, int baudRate, byte deviceId)
  240. {
  241. Dispatcher.Invoke(() =>
  242. {
  243. AppendLog($"成功找到设备!波特率: {baudRate}, 设备ID: 0x{deviceId:X2}");
  244. DeviceAddressItem device = new DeviceAddressItem()
  245. {
  246. PortName = portName,
  247. BaudRate = baudRate,
  248. DeviceAddress = deviceId
  249. };
  250. _findDevices.Add(device);
  251. });
  252. }
  253. //private void OnSearchFinished(bool success, string portName, int baudRate, byte deviceId)
  254. //{
  255. // _deviceFinder?.Disponse();
  256. // Dispatcher.Invoke(() =>
  257. // {
  258. // _isSearching = false;
  259. // ChangeBtnSearchStatus(_isSearching);
  260. // if (success)
  261. // {
  262. // txtResult.Text = $"设备找到!串口: {portName}, 波特率: {baudRate}, 设备ID: 0x{deviceId:X2}";
  263. // txtResult.Foreground = System.Windows.Media.Brushes.Green;
  264. // AppendLog($"成功找到设备!波特率: {baudRate}, 设备ID: 0x{deviceId:X2}");
  265. // DeviceAddressItem device = new DeviceAddressItem()
  266. // {
  267. // PortName = portName,
  268. // BaudRate = baudRate,
  269. // DeviceAddress = deviceId
  270. // };
  271. // _findDevices.Add(device);
  272. // //lvResult.ItemsSource = null;
  273. // //lvResult.ItemsSource = _findDevices;
  274. // }
  275. // else
  276. // {
  277. // txtResult.Text = "未找到设备";
  278. // txtResult.Foreground = System.Windows.Media.Brushes.Red;
  279. // AppendLog("搜索失败,未找到设备");
  280. // }
  281. // });
  282. //}
  283. private void OnSearchFinished(int findCount)
  284. {
  285. _deviceFinder?.Disponse();
  286. Dispatcher.Invoke(() =>
  287. {
  288. _isSearching = false;
  289. ChangeBtnSearchStatus(_isSearching);
  290. AppendLog($"本次搜索找到{findCount}台设备");
  291. });
  292. }
  293. private void ChangeBtnSearchStatus(bool iSsearching)
  294. {
  295. if (!iSsearching)
  296. {
  297. btnSearch.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4CAF50"));
  298. btnSearch.Content = "开始搜索";
  299. cmbPortNames.IsEnabled = true;
  300. mcmbBaudrate.IsEnabled = true;
  301. txtMinId.IsEnabled = txtMaxId.IsEnabled = true;
  302. txtIdTimeout.IsEnabled = true;
  303. }
  304. else
  305. {
  306. btnSearch.Content = "停止搜索";
  307. btnSearch.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF4C4C"));
  308. cmbPortNames.IsEnabled = false;
  309. mcmbBaudrate.IsEnabled = false;
  310. txtMinId.IsEnabled = txtMaxId.IsEnabled = false;
  311. txtIdTimeout.IsEnabled = false;
  312. }
  313. }
  314. private void _deviceFinder_SearchLog(string message)
  315. {
  316. AppendLog(message);
  317. }
  318. private void AppendLog(string message)
  319. {
  320. //Dispatcher.Invoke(() =>
  321. Dispatcher.BeginInvoke(new Action(()=>
  322. {
  323. var logEntry = new LogEntry { Time = DateTime.Now.ToString("HH:mm:ss.fff"), Message = message };
  324. _logEntries.Add(logEntry);
  325. lvLogs.ItemsSource = null;
  326. lvLogs.ItemsSource = _logEntries;
  327. lvLogs.ScrollIntoView(logEntry);
  328. //File.AppendAllText("search_log.txt", $"{logEntry.Time} - {logEntry.Message}\n");
  329. // 确保 Logs 目录存在
  330. Directory.CreateDirectory(Path.GetDirectoryName(LogFilePath));
  331. File.AppendAllText(LogFilePath, $"{logEntry.Time} - {logEntry.Message}\n");
  332. }));
  333. }
  334. //读取并加载日志
  335. private void LoadConfigLog()
  336. {
  337. Dispatcher.BeginInvoke(new Action(() =>
  338. {
  339. LoadLogs();
  340. }));
  341. }
  342. private void LoadLogs()
  343. {
  344. if (!File.Exists(LogFilePath))
  345. return;
  346. _logEntries.Clear(); // 先清空已有的日志记录
  347. //var lines = File.ReadAllLines("config_log.txt");
  348. var lines = File.ReadAllLines(LogFilePath);
  349. foreach (var line in lines)
  350. {
  351. var parts = line.Split(new[] { " - " }, 2, StringSplitOptions.None);
  352. if (parts.Length == 2)
  353. {
  354. _logEntries.Add(new LogEntry { Time = parts[0], Message = parts[1] });
  355. }
  356. }
  357. // 更新 ListView
  358. lvLogs.ItemsSource = null;
  359. lvLogs.ItemsSource = _logEntries;
  360. // 滚动到最后一条
  361. if (_logEntries.Count > 0)
  362. {
  363. lvLogs.ScrollIntoView(_logEntries[_logEntries.Count - 1]);
  364. }
  365. }
  366. private void TextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
  367. {
  368. if (e.Text.All(char.IsDigit))
  369. {
  370. e.Handled = false; // 如果是数字,允许输入
  371. }
  372. else
  373. {
  374. // 如果当前输入的字符不是数字,并且是中文输入法的拼音或候选字,禁止输入
  375. e.Handled = true;
  376. }
  377. }
  378. private void MenuItem_CopyMessage_Click(object sender, RoutedEventArgs e)
  379. {
  380. if (lvLogs.SelectedItem is LogEntry logEntry)
  381. {
  382. if (!LicenseMana.mIsLicensed)
  383. {
  384. MessageBox.Show(Application.Current.MainWindow, "软件未注册,不能使用此功能。", "提示",
  385. MessageBoxButton.OK, MessageBoxImage.Information);
  386. return;
  387. }
  388. Clipboard.SetText(logEntry.Message);
  389. }
  390. }
  391. private void UserControl_Unloaded(object sender, RoutedEventArgs e)
  392. {
  393. if (_isSearching)
  394. {
  395. StopSearch();
  396. }
  397. }
  398. //------------------------------------------------------------------
  399. }
  400. //----------------------------------------------------
  401. }