HouseDebugPageView.xaml.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. using ivf_tl_CustomControls;
  2. using ivf_tl_Entity.CameraEntitys;
  3. using ivf_tl_Entity.GlobalEnums;
  4. using ivf_tl_Operate.Debug;
  5. using ivf_tl_Operate.ViewModel;
  6. using ivf_tl_Operate.Windows;
  7. using Newtonsoft.Json.Linq;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Net.Sockets;
  13. using System.Runtime.ConstrainedExecution;
  14. using System.Security.Cryptography;
  15. using System.Security.Policy;
  16. using System.Text;
  17. using System.Threading.Tasks;
  18. using System.Windows;
  19. using System.Windows.Controls;
  20. using System.Windows.Data;
  21. using System.Windows.Documents;
  22. using System.Windows.Input;
  23. using System.Windows.Media;
  24. using System.Windows.Media.Imaging;
  25. using System.Windows.Media.Media3D;
  26. using System.Windows.Navigation;
  27. using System.Windows.Shapes;
  28. namespace ivf_tl_Operate.View
  29. {
  30. /// <summary>
  31. /// HouseDebugPageView.xaml 的交互逻辑
  32. /// </summary>
  33. public partial class HouseDebugPageView : UserControl
  34. {
  35. //private HouseDebugPageViewModel vm { get { return (HouseDebugPageViewModel)DataContext; } }
  36. private HouseDebugPageViewModel vm = null;
  37. /// <summary>
  38. /// 实时图像打开状态
  39. /// </summary>
  40. private bool isOpen = false;
  41. /// <summary>D2-02 第二阶段:MJPEG 预览客户端(一次性,每次开预览 new 新实例)。</summary>
  42. private MjpegStreamClient _mjpeg;
  43. private object locker = new object();
  44. //#bdbdbd #bfd87d
  45. public HouseDebugPageView(HouseDebugPageViewModel houseDebugPageViewModel)
  46. {
  47. try
  48. {
  49. InitializeComponent();
  50. vm = houseDebugPageViewModel;
  51. this.DataContext = vm;
  52. Loaded += HouseDebugPageView_Loaded;
  53. }
  54. catch (Exception ex)
  55. {
  56. ExLog(ex, "HouseDebugPageView");
  57. }
  58. }
  59. #region 通用设置
  60. private void HouseDebugPageView_Loaded(object sender, RoutedEventArgs e)
  61. {
  62. vm.MessageEvent += AddMessageInfo;
  63. vm.OpenVideoEvent += OpenVideo;
  64. foreach (var item in vm.HouseList)
  65. {
  66. switch (item.houseSn)
  67. {
  68. case 1:
  69. this._houseCCD1.Text = item.ccdId.ToString();
  70. break;
  71. case 2:
  72. this._houseCCD2.Text = item.ccdId.ToString();
  73. break;
  74. case 3:
  75. this._houseCCD3.Text = item.ccdId.ToString();
  76. break;
  77. case 4:
  78. this._houseCCD4.Text = item.ccdId.ToString();
  79. break;
  80. case 5:
  81. this._houseCCD5.Text = item.ccdId.ToString();
  82. break;
  83. case 6:
  84. this._houseCCD6.Text = item.ccdId.ToString();
  85. break;
  86. case 7:
  87. this._houseCCD7.Text = item.ccdId.ToString();
  88. break;
  89. case 8:
  90. this._houseCCD8.Text = item.ccdId.ToString();
  91. break;
  92. case 9:
  93. this._houseCCD9.Text = item.ccdId.ToString();
  94. break;
  95. case 10:
  96. this._houseCCD10.Text = item.ccdId.ToString();
  97. break;
  98. }
  99. }
  100. }
  101. private void RefshInfo()
  102. {
  103. Dispatcher.Invoke(() =>
  104. {
  105. var aa = vm.houseWellSettingList.FirstOrDefault(x => x.houseSn == vm.CurrentHouseId && x.wellSn == vm.CurrentWell);
  106. if (aa == null)
  107. {
  108. this._well_TextBlock.Text = "0";
  109. this._eepromPosition.Text = "0";
  110. }
  111. else
  112. {
  113. this._well_TextBlock.Text = aa.horizontalMotorPosition.ToString();
  114. this._eepromPosition.Text = aa.eepromClearPosition.ToString();
  115. }
  116. var bb = vm.ccdPhoto.FirstOrDefault(x => x.houseSn == vm.CurrentHouseId && x.wellSn == vm.CurrentWell);
  117. if (bb == null)
  118. {
  119. this._autoFocus.Text = "0";
  120. this._ccdOne.Text = "0";
  121. }
  122. else
  123. {
  124. this._autoFocus.Text = bb.autoFocusPosition.ToString();
  125. this._ccdOne.Text = bb.clearestPosition.ToString();
  126. }
  127. });
  128. }
  129. DateTime logTime = DateTime.Now;
  130. private void AddMessageInfo(string message)
  131. {
  132. lock (locker)
  133. {
  134. logTime = DateTime.Now;
  135. message = $"{logTime.ToString("yyyy-MM-dd HH:mm:ss")}.{logTime.Millisecond}{message}";
  136. Dispatcher.Invoke(() =>
  137. {
  138. vm.MessageInfoList.Add(message);
  139. _messageList.ScrollIntoView(message);
  140. });
  141. }
  142. }
  143. // 旧抓图把图片铺到 _warpPanelPictures 的 AddPic 处理已移除(画面改用 MJPEG 预览);
  144. // 面板与"清空"按钮保留(Clear_Click 仍可清空消息/残留图)。
  145. /// <summary>
  146. /// 打开图像
  147. /// </summary>
  148. private void OpenVideo()
  149. {
  150. try
  151. {
  152. if (isOpen) { AddMessageInfo($"图像已打开"); return; }
  153. string sessionId = vm.CurrentSessionId;
  154. if (string.IsNullOrEmpty(sessionId)) { AddMessageInfo("未借用会话,无法预览(请先初始化)"); return; }
  155. // 每次开预览 new 新实例(MjpegStreamClient 一次性,不可复用)
  156. var client = new MjpegStreamClient(ivf_tl_Operate.Helpers.ControlClient.BaseUrl);
  157. _mjpeg = client;
  158. client.FrameReceived += img => Dispatcher.Invoke(() =>
  159. {
  160. if (_mjpeg != client) return; // 已被 CloseVideo 换/清,丢弃残帧(避免关画面后残帧写回)
  161. _previewImage.Source = img;
  162. });
  163. client.Stopped += reason => Dispatcher.Invoke(() =>
  164. {
  165. if (_mjpeg != client) return; // 旧实例的 Stopped 不串台到新会话
  166. isOpen = false;
  167. AddMessageInfo($"⚠ {reason}"); // 明确提示操作人员手动重开(不自动重连)
  168. });
  169. client.Start(sessionId);
  170. isOpen = true;
  171. AddMessageInfo("图像已打开(MJPEG 实时预览)");
  172. }
  173. catch (Exception ex)
  174. {
  175. ExLog(ex, "OpenVideo");
  176. AddMessageInfo($"图像打开异常:{ex.Message}");
  177. }
  178. }
  179. private void CloseVideo()
  180. {
  181. try
  182. {
  183. if (!isOpen) { AddMessageInfo($"图像未打开,无需关闭"); return; }
  184. _mjpeg?.Stop();
  185. _mjpeg = null; // 丢弃实例(一次性,下次开预览 new 新的)
  186. Dispatcher.Invoke(() => { _previewImage.Source = null; });
  187. isOpen = false;
  188. AddMessageInfo("图像已关闭");
  189. }
  190. catch (Exception ex)
  191. {
  192. ExLog(ex, "CloseVideo");
  193. AddMessageInfo($"图像关闭异常:{ex.Message}");
  194. }
  195. }
  196. private void ExLog(Exception ex, string name)
  197. {
  198. AppData.Instance.LogHelper.ExceptionLog(ex, $"HouseDebugPageView.{name}", LogEnum.RunException);
  199. }
  200. private void ShowMessageDefeat(string mess)
  201. {
  202. Dispatcher.Invoke(() =>
  203. {
  204. MessageBox.Show(mess);
  205. });
  206. //AddMessageInfo(mess);
  207. //new MessagePrompt().ShowCenterOwnerDefeat(AppData.Instance.MainWindow, mess);
  208. }
  209. private void ShowMessage(string mess)
  210. {
  211. }
  212. private void ShowMessageSuccess(string mess)
  213. {
  214. new MessagePrompt().ShowCenterOwnerSuccess(AppData.Instance.MainWindow, mess);
  215. }
  216. #endregion
  217. private void Return_Click(object sender, RoutedEventArgs e)
  218. {
  219. CloseVideo();
  220. vm.ComHouseUnit();
  221. SettingPageView settingPageView = new SettingPageView();
  222. AppData.Instance.MainWindow.LoadPage(settingPageView);
  223. }
  224. private void House_Checked(object sender, RoutedEventArgs e)
  225. {
  226. if (vm == null) return;
  227. if (!(sender is RadioButtonNoFrame radioButtonNoFrame)) return;
  228. vm.CurrentHouseId = int.Parse(radioButtonNoFrame.Tag.ToString());
  229. vm.CurrentHouse = vm.HouseList.First(x => x.houseSn == vm.CurrentHouseId);
  230. }
  231. private void CCD_Checked(object sender, RoutedEventArgs e)
  232. {
  233. if (vm == null) return;
  234. if (!(sender is RadioButtonNoFrame radioButtonNoFrame)) return;
  235. string ccdidString = radioButtonNoFrame.Tag.ToString();
  236. vm.CurrentCCDId = int.Parse(ccdidString);
  237. this._ccdId_TextBlock.Text = ccdidString;
  238. }
  239. /// <summary>
  240. /// 初始化
  241. /// </summary>
  242. /// <param name="sender"></param>
  243. /// <param name="e"></param>
  244. private async void Start_Click(object sender, RoutedEventArgs e)
  245. {
  246. try
  247. {
  248. this._house_StackPanel.IsEnabled = false;
  249. this._ccd_StackPanel.IsEnabled = false;
  250. this._startButton.IsEnabled = false;
  251. //await Task.Delay(3000 * 5);
  252. //return;
  253. await vm.ComHouseInit();
  254. RefshInfo();
  255. OpenVideo();
  256. this._button1.IsEnabled = true;
  257. this._button2.IsEnabled = true;
  258. this._button3.IsEnabled = true;
  259. this._button4.IsEnabled = true;
  260. this._button5.IsEnabled = true;
  261. this._button6.IsEnabled = true;
  262. this._button7.IsEnabled = true;
  263. this._button90.IsEnabled = true;
  264. this._button100.IsEnabled = true;
  265. this._stackPanel1.IsEnabled = true;
  266. this._stackPanel2.IsEnabled = true;
  267. this._stackPanel3.IsEnabled = true;
  268. this._stackPanel4.IsEnabled = true;
  269. }
  270. catch (Exception ex)
  271. {
  272. ExLog(ex, "Start_Click");
  273. }
  274. }
  275. /// <summary>
  276. /// 读E方
  277. /// </summary>
  278. /// <param name="sender"></param>
  279. /// <param name="e"></param>
  280. private void RedE_Click(object sender, RoutedEventArgs e)
  281. {
  282. }
  283. /// <summary>
  284. /// 读温度
  285. /// </summary>
  286. /// <param name="sender"></param>
  287. /// <param name="e"></param>
  288. private void RedTem_Click(object sender, RoutedEventArgs e)
  289. {
  290. Task.Run(async () => await vm.RedTem());
  291. }
  292. /// <summary>
  293. /// 读压力
  294. /// </summary>
  295. /// <param name="sender"></param>
  296. /// <param name="e"></param>
  297. private void RedPre_Click(object sender, RoutedEventArgs e)
  298. {
  299. Task.Run(async () =>
  300. {
  301. await vm.RedPre();
  302. });
  303. }
  304. /// <summary>
  305. /// 读舱门
  306. /// </summary>
  307. /// <param name="sender"></param>
  308. /// <param name="e"></param>
  309. private void RedDoor_Click(object sender, RoutedEventArgs e)
  310. {
  311. Task.Run(async () =>
  312. {
  313. await vm.RedDoor();
  314. });
  315. }
  316. /// <summary>
  317. /// 开灯
  318. /// </summary>
  319. /// <param name="sender"></param>
  320. /// <param name="e"></param>
  321. private void OpenLed_Click(object sender, RoutedEventArgs e)
  322. {
  323. Task.Run(async () =>
  324. {
  325. await vm.OpenLed();
  326. });
  327. }
  328. /// <summary>
  329. /// 关灯
  330. /// </summary>
  331. /// <param name="sender"></param>
  332. /// <param name="e"></param>
  333. private void CloseLed_Click(object sender, RoutedEventArgs e)
  334. {
  335. Task.Run(async () =>
  336. {
  337. await vm.CloseLed();
  338. });
  339. }
  340. /// <summary>
  341. /// 卸载
  342. /// </summary>
  343. /// <param name="sender"></param>
  344. /// <param name="e"></param>
  345. private void End_Click(object sender, RoutedEventArgs e)
  346. {
  347. if (isOpen)
  348. {
  349. CloseVideo();
  350. }
  351. vm.IsStop = true;
  352. vm.ComHouseUnit();
  353. this._house_StackPanel.IsEnabled = true;
  354. this._ccd_StackPanel.IsEnabled = true;
  355. this._startButton.IsEnabled = true;
  356. this._button1.IsEnabled = false;
  357. this._button2.IsEnabled = false;
  358. this._button3.IsEnabled = false;
  359. this._button4.IsEnabled = false;
  360. this._button5.IsEnabled = false;
  361. this._button6.IsEnabled = false;
  362. this._button7.IsEnabled = false;
  363. this._button90.IsEnabled = false;
  364. this._button100.IsEnabled = false;
  365. this._stackPanel1.IsEnabled = false;
  366. this._stackPanel2.IsEnabled = false;
  367. this._stackPanel3.IsEnabled = false;
  368. this._stackPanel4.IsEnabled = false;
  369. }
  370. /// <summary>
  371. /// 打开进气阀
  372. /// </summary>
  373. /// <param name="sender"></param>
  374. /// <param name="e"></param>
  375. private void OpenIntake_Click(object sender, RoutedEventArgs e)
  376. {
  377. Task.Run(async () =>
  378. {
  379. await vm.OpenIntake();
  380. });
  381. }
  382. /// <summary>
  383. /// 关闭进气阀
  384. /// </summary>
  385. /// <param name="sender"></param>
  386. /// <param name="e"></param>
  387. private void CloseIntake_Click(object sender, RoutedEventArgs e)
  388. {
  389. Task.Run(async () =>
  390. {
  391. await vm.CloseIntake();
  392. });
  393. }
  394. /// <summary>
  395. /// 打开排气阀
  396. /// </summary>
  397. /// <param name="sender"></param>
  398. /// <param name="e"></param>
  399. private void OpenExhaust_Click(object sender, RoutedEventArgs e)
  400. {
  401. Task.Run(async () =>
  402. {
  403. await vm.OpenExhaust();
  404. });
  405. }
  406. /// <summary>
  407. /// 关闭排气阀
  408. /// </summary>
  409. /// <param name="sender"></param>
  410. /// <param name="e"></param>
  411. private void CloseExhaust_Click(object sender, RoutedEventArgs e)
  412. {
  413. Task.Run(async () =>
  414. {
  415. await vm.CloseExhaust();
  416. });
  417. }
  418. private void Aeration_Click(object sender, RoutedEventArgs e)
  419. {
  420. Task.Run(async () =>
  421. {
  422. await vm.HouseAeration();
  423. });
  424. }
  425. /// <summary>
  426. /// 写舱室进气阀时间
  427. /// </summary>
  428. /// <param name="sender"></param>
  429. /// <param name="e"></param>
  430. private void WriteOpenIntakeTime_Click(object sender, RoutedEventArgs e)
  431. {
  432. try
  433. {
  434. if (vm.CurrentHouse == null)
  435. {
  436. ShowMessageDefeat("未获取到对应的舱室信息");
  437. return;
  438. }
  439. string timeString = this._openIntakeTime_TextBox.Text.Trim();
  440. if (string.IsNullOrEmpty(timeString))
  441. {
  442. ShowMessageDefeat("请输入进气阀打开时间");
  443. return;
  444. }
  445. if (int.TryParse(timeString, out int value) && value >= 0)
  446. {
  447. Task.Run(async () =>
  448. {
  449. await vm.WriteOpenIntakeTime(value);
  450. if (vm.CurrentHouse != null)
  451. {
  452. vm.CurrentHouse.inletValveOpeningTime = value;
  453. vm.SetHouseInfo();
  454. }
  455. });
  456. }
  457. else
  458. {
  459. ShowMessageDefeat("进气阀打开时间只能输入大于等于0数字");
  460. return;
  461. }
  462. }
  463. catch (Exception ex)
  464. {
  465. ExLog(ex, "WriteOpenIntakeTime_Click");
  466. }
  467. }
  468. /// <summary>
  469. /// 写曝光时间
  470. /// </summary>
  471. /// <param name="sender"></param>
  472. /// <param name="e"></param>
  473. private async void SetExposure_Click(object sender, RoutedEventArgs e)
  474. {
  475. try
  476. {
  477. if (vm.CurrentHouse == null)
  478. {
  479. ShowMessageDefeat("未获取到对应的舱室信息");
  480. return;
  481. }
  482. if (int.TryParse(this._exposure_TextBox.Text.Trim(), out int newExposure) && newExposure >= 0)
  483. {
  484. await vm.SetExposure(newExposure);
  485. if (vm.CurrentHouse != null)
  486. {
  487. vm.CurrentHouse.ccdExposure = newExposure;
  488. vm.SetHouseInfo();
  489. }
  490. return;
  491. }
  492. ShowMessageDefeat("曝光值只能输入大于等于0数字");
  493. return;
  494. }
  495. catch (Exception ex)
  496. {
  497. ExLog(ex, "SetExposure_Click");
  498. }
  499. }
  500. /// <summary>
  501. /// 水平电机复位
  502. /// </summary>
  503. /// <param name="sender"></param>
  504. /// <param name="e"></param>
  505. private void HorizontalMotorReset_Click(object sender, RoutedEventArgs e)
  506. {
  507. Task.Run(async () =>
  508. {
  509. await vm.HorizontalMotorReset();
  510. RefshInfo();
  511. });
  512. }
  513. /// <summary>
  514. /// 保存当前well水平电机位置
  515. /// </summary>
  516. /// <param name="sender"></param>
  517. /// <param name="e"></param>
  518. private void SaveWellHorizontalMotor_Click(object sender, RoutedEventArgs e)
  519. {
  520. if (vm.CurrentHouse == null)
  521. {
  522. ShowMessageDefeat("未获取到对应的舱室信息");
  523. return;
  524. }
  525. Task.Run(async () =>
  526. {
  527. try
  528. {
  529. await vm.SaveWellHor();
  530. if (!vm.SetWellMotorPosition())
  531. {
  532. ShowMessageDefeat("数据库保存失败");
  533. return;
  534. }
  535. var aa = vm.houseWellSettingList.FirstOrDefault(x => x.houseSn == vm.CurrentHouseId && x.wellSn == vm.CurrentWell);
  536. if (aa != null)
  537. {
  538. aa.horizontalMotorPosition = vm.CurrentHor;
  539. Dispatcher.Invoke(() =>
  540. {
  541. this._well_TextBlock.Text = aa.horizontalMotorPosition.ToString();
  542. });
  543. }
  544. else
  545. {
  546. ShowMessageDefeat("未获取到well位置");
  547. return;
  548. }
  549. }
  550. catch (Exception ex)
  551. {
  552. ExLog(ex, "SaveWellHorizontalMotor_Click");
  553. }
  554. });
  555. }
  556. /// <summary>
  557. /// 水平电机正向移动
  558. /// </summary>
  559. /// <param name="sender"></param>
  560. /// <param name="e"></param>
  561. private void HorizontalMotorForward_Click(object sender, RoutedEventArgs e)
  562. {
  563. string forwardString = this._horizontalMotorForward_TextBox.Text.Trim();
  564. if (string.IsNullOrEmpty(forwardString))
  565. {
  566. MessageBox.Show("请输入正向移动脉冲");
  567. return;
  568. }
  569. if (int.TryParse(forwardString, out int value))
  570. {
  571. Task.Run(async () =>
  572. {
  573. await vm.HorizontalMotorForward(value);
  574. });
  575. }
  576. else
  577. {
  578. MessageBox.Show("正向移动脉冲只能输入数字");
  579. return;
  580. }
  581. }
  582. /// <summary>
  583. /// 水平电机反向运动
  584. /// </summary>
  585. /// <param name="sender"></param>
  586. /// <param name="e"></param>
  587. private void HorizontalMotorBackward_Click(object sender, RoutedEventArgs e)
  588. {
  589. string backwardString = this._horizontalMotorBackward_TextBox.Text.Trim();
  590. if (string.IsNullOrEmpty(backwardString))
  591. {
  592. MessageBox.Show("请输入反向移动脉冲");
  593. return;
  594. }
  595. if (int.TryParse(backwardString, out int value))
  596. {
  597. Task.Run(async () =>
  598. {
  599. await vm.HorizontalMotorBackward(value);
  600. });
  601. }
  602. else
  603. {
  604. MessageBox.Show("反向移动脉冲只能输入数字");
  605. return;
  606. }
  607. }
  608. /// <summary>
  609. /// 水平电机移动到指定Well
  610. /// </summary>
  611. /// <param name="sender"></param>
  612. /// <param name="e"></param>
  613. private void HorizontalMotorToWell_Click(object sender, RoutedEventArgs e)
  614. {
  615. string wellString = this._horizontalMotorToWell_TextBox.Text.Trim();
  616. if (string.IsNullOrEmpty(wellString))
  617. {
  618. ShowMessageDefeat("请输入目标well");
  619. return;
  620. }
  621. if (int.TryParse(wellString, out int value) && value >= 1 && value <= 16)
  622. {
  623. Task.Run(async () =>
  624. {
  625. await vm.HorizontalMotorToWell(value);
  626. RefshInfo();
  627. });
  628. }
  629. else
  630. {
  631. ShowMessageDefeat("目标well只能输入1-16的数字");
  632. return;
  633. }
  634. }
  635. /// <summary>
  636. /// 下一well
  637. /// </summary>
  638. /// <param name="sender"></param>
  639. /// <param name="e"></param>
  640. private void HorizontalMotorNextWell_Click(object sender, RoutedEventArgs e)
  641. {
  642. Task.Run(async () =>
  643. {
  644. await vm.HorizontalMotorToWell(vm.CurrentWell + 1);
  645. RefshInfo();
  646. });
  647. }
  648. /// <summary>
  649. /// 垂直电机复位
  650. /// </summary>
  651. /// <param name="sender"></param>
  652. /// <param name="e"></param>
  653. private void VerticalMotorResetWait_Click(object sender, RoutedEventArgs e)
  654. {
  655. Task.Run(async () =>
  656. {
  657. await vm.VerticalMotorReset();
  658. vm.CurrentFocal = 0;
  659. });
  660. }
  661. /// <summary>
  662. /// 垂直电机正向移动
  663. /// </summary>
  664. /// <param name="sender"></param>
  665. /// <param name="e"></param>
  666. private void VerticalMotorForward_Click(object sender, RoutedEventArgs e)
  667. {
  668. string forwardString = this._verticalMotorForward_TextBox.Text.Trim();
  669. if (string.IsNullOrEmpty(forwardString))
  670. {
  671. MessageBox.Show("请输入正向移动脉冲");
  672. return;
  673. }
  674. if (int.TryParse(forwardString, out int value))
  675. {
  676. Task.Run(async () =>
  677. {
  678. await vm.VerticalMotorForward(value);
  679. });
  680. }
  681. else
  682. {
  683. MessageBox.Show("正向移动脉冲只能输入数字");
  684. return;
  685. }
  686. }
  687. /// <summary>
  688. /// 垂直电机反向运动
  689. /// </summary>
  690. /// <param name="sender"></param>
  691. /// <param name="e"></param>
  692. private void VerticalMotorBackward_Click(object sender, RoutedEventArgs e)
  693. {
  694. string backwardString = this._verticalMotorBackward_TextBox.Text.Trim();
  695. if (string.IsNullOrEmpty(backwardString))
  696. {
  697. MessageBox.Show("请输入反向移动脉冲");
  698. return;
  699. }
  700. if (int.TryParse(backwardString, out int value))
  701. {
  702. Task.Run(async () =>
  703. {
  704. await vm.VerticalMotorBackward(value);
  705. });
  706. }
  707. else
  708. {
  709. MessageBox.Show("反向移动脉冲只能输入数字");
  710. return;
  711. }
  712. }
  713. /// <summary>
  714. /// 垂直电机绝对运动
  715. /// </summary>
  716. /// <param name="sender"></param>
  717. /// <param name="e"></param>
  718. private void VerticalMotorAbsolute_Click(object sender, RoutedEventArgs e)
  719. {
  720. string absoluteString = this._verticalMotorAbsolute_TextBox.Text.Trim();
  721. if (string.IsNullOrEmpty(absoluteString))
  722. {
  723. MessageBox.Show("请输入目标脉冲");
  724. return;
  725. }
  726. if (int.TryParse(absoluteString, out int value) && value >= 0 && value <= vm.tLSetting.verticalMotorPulseMax)
  727. {
  728. Task.Run(async () =>
  729. {
  730. await vm.VerticalMotorAbsolute(value);
  731. });
  732. }
  733. else
  734. {
  735. MessageBox.Show($"目标脉冲只能输入0-{vm.tLSetting.verticalMotorPulseMax}的数字");
  736. return;
  737. }
  738. }
  739. /// <summary>
  740. /// 保存垂直电机间隔脉冲
  741. /// </summary>
  742. /// <param name="sender"></param>
  743. /// <param name="e"></param>
  744. private void SaveVerSpacePulse_Click(object sender, RoutedEventArgs e)
  745. {
  746. try
  747. {
  748. if (vm.CurrentHouse == null)
  749. {
  750. ShowMessageDefeat("未获取到对应的舱室信息");
  751. return;
  752. }
  753. if (int.TryParse(this._verticalMotorForward_TextBox.Text.Trim(), out int newValue) && newValue > 0 && newValue <= vm.tLSetting.verticalMotorPulseMax)
  754. {
  755. Task.Run(async () =>
  756. {
  757. await vm.WriteOVerSpace(newValue);
  758. vm.CurrentHouse.verticalMotorSpacePulse = newValue;
  759. vm.SetHouseInfo();
  760. });
  761. }
  762. else
  763. {
  764. AddMessageInfo($"垂直电机间隔脉冲只能输入0-{vm.tLSetting.verticalMotorPulseMax}的数字");
  765. }
  766. }
  767. catch (Exception ex)
  768. {
  769. AddMessageInfo($"保存垂直电机间隔脉冲异常:{ex.Message}");
  770. ExLog(ex, "SaveVerSpacePulse_Click");
  771. }
  772. }
  773. /// <summary>
  774. /// 清空
  775. /// </summary>
  776. /// <param name="sender"></param>
  777. /// <param name="e"></param>
  778. private void Clear_Click(object sender, RoutedEventArgs e)
  779. {
  780. vm.MessageInfoList.Clear();
  781. this._warpPanelPictures.Children.Clear();
  782. }
  783. /// <summary>
  784. /// 一键电机准备
  785. /// </summary>
  786. /// <param name="sender"></param>
  787. /// <param name="e"></param>
  788. private void Motor_Click(object sender, RoutedEventArgs e)
  789. {
  790. Task.Run(async () =>
  791. {
  792. await vm.MototReady();
  793. });
  794. }
  795. // 旧抓图按钮的事件处理(单张抓拍 GetPic_Click / 水平抓图 ShuiPingPic_Click /
  796. // 结束抓图 EndPic_Click / 清晰图层抓图 AutoFocus_Click)已移除——调试页画面改用 MJPEG 实时预览。
  797. private void CloseVideo_Click(object sender, RoutedEventArgs e)
  798. {
  799. try
  800. {
  801. CloseVideo();
  802. }
  803. catch (Exception ex)
  804. {
  805. ExLog(ex, "CloseVideo_Click");
  806. AddMessageInfo($"图像关闭异常:{ex.Message}");
  807. }
  808. }
  809. /// <summary>
  810. /// M2-05 场景A 一键全自动标定(沙盒)。对选中 well(默认 16 well)逐个跑四步标定,
  811. /// 合格绿/伪峰红实时显示,结果作出厂基准 scene=0 存档 + 写 calibration.json。
  812. /// 硬件复用调试页已借用并打开的 comBin/camera(经适配器暴露为 HAL 接口),不二次 Open/Init。
  813. /// </summary>
  814. private async void OneClickCalibrate_Click(object sender, RoutedEventArgs e)
  815. {
  816. try
  817. {
  818. if (vm.CurrentHouse == null)
  819. {
  820. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  821. return;
  822. }
  823. if (vm.IsCalibrating)
  824. {
  825. AddMessageInfo("[一键标定]已在标定中");
  826. return;
  827. }
  828. this._oneClickCalib_Button.IsEnabled = false;
  829. try
  830. {
  831. // 沙盒默认全 16 well 逐个跑(如需勾选子集,传入选中 well 列表即可)。
  832. await vm.OneClickCalibrate(null);
  833. }
  834. finally
  835. {
  836. this._oneClickCalib_Button.IsEnabled = true;
  837. }
  838. }
  839. catch (Exception ex)
  840. {
  841. ExLog(ex, "OneClickCalibrate_Click");
  842. AddMessageInfo($"一键标定异常:{ex.Message}");
  843. }
  844. }
  845. /// <summary>M2-05 中止一键标定(当前 well 结束后停止,已完成结果保留)。</summary>
  846. private void StopCalibrate_Click(object sender, RoutedEventArgs e)
  847. {
  848. try
  849. {
  850. vm.StopCalibrate();
  851. }
  852. catch (Exception ex)
  853. {
  854. ExLog(ex, "StopCalibrate_Click");
  855. }
  856. }
  857. /// <summary>
  858. /// M2-07 载入选定 well 当前生效的拍摄层配置(well 级覆盖优先,空则显示继承的设备级值占位),
  859. /// 回填三个手调框并触发一次预览。
  860. /// </summary>
  861. private void LoadManualTune_Click(object sender, RoutedEventArgs e)
  862. {
  863. try
  864. {
  865. if (vm.CurrentHouse == null)
  866. {
  867. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  868. return;
  869. }
  870. if (vm.ManualTuneWell < 1 || vm.ManualTuneWell > 16)
  871. {
  872. ShowMessageDefeat("well 只能为 1-16");
  873. return;
  874. }
  875. vm.LoadManualLayerSetting(vm.ManualTuneWell);
  876. }
  877. catch (Exception ex)
  878. {
  879. ExLog(ex, "LoadManualTune_Click");
  880. }
  881. }
  882. /// <summary>
  883. /// M2-07 实时预览:用该 well 标定 FocusZ + 当前手调值调 M2-02 ComputeLayerPositions 算各层 Z(关联 V-045)。
  884. /// </summary>
  885. private void PreviewManualTune_Click(object sender, RoutedEventArgs e)
  886. {
  887. try
  888. {
  889. if (vm.CurrentHouse == null)
  890. {
  891. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  892. return;
  893. }
  894. vm.PreviewManualLayers();
  895. }
  896. catch (Exception ex)
  897. {
  898. ExLog(ex, "PreviewManualTune_Click");
  899. }
  900. }
  901. /// <summary>
  902. /// M2-07 存为该 well 默认:校验通过后写 house_well_setting well 级覆盖
  903. /// (focus_layer_spacing_pulse/focus_layer_count + 复用 move_down_layer),留空继承设备级。
  904. /// </summary>
  905. private void SaveManualTune_Click(object sender, RoutedEventArgs e)
  906. {
  907. try
  908. {
  909. if (vm.CurrentHouse == null)
  910. {
  911. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  912. return;
  913. }
  914. if (vm.SaveManualLayerTune())
  915. {
  916. ShowMessageSuccess("手调拍摄层已存为该 well 默认");
  917. }
  918. else
  919. {
  920. ShowMessageDefeat("手调拍摄层保存失败(见日志,可能为非法值或接口失败)");
  921. }
  922. }
  923. catch (Exception ex)
  924. {
  925. ExLog(ex, "SaveManualTune_Click");
  926. }
  927. }
  928. /// <summary>D2-02-T3.5 载入选定 well 当前 well 级对焦半幅覆盖值(空=继承设备级)。</summary>
  929. private void LoadFocusRange_Click(object sender, RoutedEventArgs e)
  930. {
  931. try
  932. {
  933. if (vm.CurrentHouse == null)
  934. {
  935. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  936. return;
  937. }
  938. if (vm.ManualTuneWell < 1 || vm.ManualTuneWell > 16)
  939. {
  940. ShowMessageDefeat("well 只能为 1-16");
  941. return;
  942. }
  943. vm.LoadWellFocusRange(vm.ManualTuneWell);
  944. }
  945. catch (Exception ex)
  946. {
  947. ExLog(ex, "LoadFocusRange_Click");
  948. }
  949. }
  950. /// <summary>D2-02-T3.5 保存该 well 对焦搜索半幅(写 house_well_setting well 级覆盖,留空继承设备级)。</summary>
  951. private void SaveFocusRange_Click(object sender, RoutedEventArgs e)
  952. {
  953. try
  954. {
  955. if (vm.CurrentHouse == null)
  956. {
  957. ShowMessageDefeat("获取舱室信息失败,请先初始化舱室");
  958. return;
  959. }
  960. if (vm.SaveWellFocusRange())
  961. {
  962. ShowMessageSuccess("对焦搜索半幅已保存为该 well 覆盖");
  963. }
  964. else
  965. {
  966. ShowMessageDefeat("对焦半幅保存失败(见日志,可能为非法值或接口失败)");
  967. }
  968. }
  969. catch (Exception ex)
  970. {
  971. ExLog(ex, "SaveFocusRange_Click");
  972. }
  973. }
  974. /// <summary>D2-02-T3.4 逐孔微调:对该格 well 经 control 协作单孔重标。</summary>
  975. private async void RecalibrateWell_Click(object sender, RoutedEventArgs e)
  976. {
  977. try
  978. {
  979. if (!(sender is FrameworkElement fe) || fe.Tag == null) return;
  980. if (!int.TryParse(fe.Tag.ToString(), out int well)) return;
  981. await vm.RecalibrateWell(well);
  982. }
  983. catch (Exception ex)
  984. {
  985. ExLog(ex, "RecalibrateWell_Click");
  986. AddMessageInfo($"单孔重标异常:{ex.Message}");
  987. }
  988. }
  989. private void OpenVideo_Click(object sender, RoutedEventArgs e)
  990. {
  991. try
  992. {
  993. OpenVideo();
  994. }
  995. catch (Exception ex)
  996. {
  997. ExLog(ex, "OpenVideo_Click");
  998. AddMessageInfo($"图像打开异常:{ex.Message}");
  999. }
  1000. }
  1001. /// <summary>
  1002. /// 保存自动对焦起点
  1003. /// </summary>
  1004. /// <param name="sender"></param>
  1005. /// <param name="e"></param>
  1006. private void SaveAutoFocus_Click(object sender, RoutedEventArgs e)
  1007. {
  1008. try
  1009. {
  1010. if (vm.SetWellAutoFocus())
  1011. {
  1012. var bb = vm.ccdPhoto.FirstOrDefault(x => x.houseSn == vm.CurrentHouseId && x.wellSn == vm.CurrentWell);
  1013. if (bb != null) bb.autoFocusPosition = vm.CurrentVer;
  1014. this._autoFocus.Text = vm.CurrentVer.ToString();
  1015. AddMessageInfo($"自动对焦起点保存成功");
  1016. }
  1017. else
  1018. {
  1019. AddMessageInfo($"自动对焦起点保存失败");
  1020. }
  1021. }
  1022. catch (Exception ex)
  1023. {
  1024. ExLog(ex, "SaveAutoFocus_Click");
  1025. AddMessageInfo($"保存自动对焦起点异常:{ex.Message}");
  1026. }
  1027. }
  1028. // 旧"清晰图层抓图"那两个输入框(拍摄次数/循环次数)的 TextChanged 高亮处理已随抓图按钮一并移除。
  1029. private void Paiqi_Click(object sender, RoutedEventArgs e)
  1030. {
  1031. Task.Run(async () =>
  1032. {
  1033. await vm.HouseVent();
  1034. });
  1035. }
  1036. private void WriteOpenVentTime_Click(object sender, RoutedEventArgs e)
  1037. {
  1038. try
  1039. {
  1040. if (vm.CurrentHouse == null)
  1041. {
  1042. ShowMessageDefeat("未获取到对应的舱室信息");
  1043. return;
  1044. }
  1045. string timeString = this._openVentTime_TextBox.Text.Trim();
  1046. if (string.IsNullOrEmpty(timeString))
  1047. {
  1048. ShowMessageDefeat("请输入排气阀打开时间");
  1049. return;
  1050. }
  1051. if (int.TryParse(timeString, out int value) && value >= 0)
  1052. {
  1053. Task.Run(async () =>
  1054. {
  1055. await vm.WriteOpenVentTime(value);
  1056. //if (vm.CurrentHouse != null)
  1057. //{
  1058. // vm.CurrentHouse.inletValveOpeningTime = value;
  1059. // vm.SetHouseInfo();
  1060. //}
  1061. });
  1062. }
  1063. else
  1064. {
  1065. ShowMessageDefeat("排气阀打开时间只能输入大于等于0数字");
  1066. return;
  1067. }
  1068. }
  1069. catch (Exception ex)
  1070. {
  1071. ExLog(ex, "WriteOpenVentTime_Click");
  1072. }
  1073. }
  1074. private void ReadOpenVentTime_Click(object sender, RoutedEventArgs e)
  1075. {
  1076. Task.Run(async () =>
  1077. {
  1078. await vm.RedVentTime();
  1079. });
  1080. }
  1081. }
  1082. }