MainWindow.xaml.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. using ivf_tl_Operate.View;
  2. using ivf_tl_Operate.ViewModel;
  3. using ivf_tl_Operate.Windows;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Windows;
  11. using System.Windows.Controls;
  12. using System.Windows.Data;
  13. using System.Windows.Documents;
  14. using System.Windows.Input;
  15. using System.Windows.Media;
  16. using System.Windows.Media.Imaging;
  17. using System.Windows.Navigation;
  18. using System.Windows.Shapes;
  19. namespace ivf_tl_Operate
  20. {
  21. /// <summary>
  22. /// Interaction logic for MainWindow.xaml
  23. /// </summary>
  24. public partial class MainWindow : Window
  25. {
  26. public MainWindow()
  27. {
  28. InitializeComponent();
  29. //DisableWPFTabletSupport();
  30. Loaded += MainWindow_Loaded;
  31. // M4-01-1:自适应竖屏框架。原仅 #if DEBUG 才用 Viewbox 等比缩放,
  32. // 导致 Release 无缩放、换分辨率必错位。此处去掉条件编译,让 Viewbox
  33. // 在所有配置(含 Release)生效:MainGrid → Viewbox(Stretch=Uniform,等比保竖屏比例) → 窗口内容。
  34. // 窗口尺寸由 WindowState=Maximized 铺满实际屏幕,不再硬编码 1824×2736(已删 .xaml 写死 Width/Height)。
  35. // [D6] 最终是否需 Stretch=Fill 或切真弹性布局、设计基准分辨率取值依赖 Surface 真机,登记待验证。
  36. Grid originalCanvas = MainGrid;
  37. this.Content = null;
  38. Viewbox viewbox = new Viewbox
  39. {
  40. Child = originalCanvas,
  41. Stretch = Stretch.Uniform
  42. };
  43. this.Content = viewbox;
  44. }
  45. private void MainWindow_Loaded(object sender, RoutedEventArgs e)
  46. {
  47. var a = new LoginWindow(this).ShowDialog();
  48. if (a != true)
  49. {
  50. this.Close();
  51. return;
  52. }
  53. AppData.Instance.MainWindow = this;
  54. MainPageViewModel mainPageViewModel = new MainPageViewModel(AppData.Instance.TlSn);
  55. MainPageView mainPageView = new MainPageView();
  56. mainPageView.DataContext = mainPageViewModel;
  57. AppData.Instance.MainPageView = mainPageView;
  58. LoadPage(mainPageView);
  59. // M1-01 步骤3:operate 登录成功后,在后台线程托管 control 的 StartMain.StartRun()。
  60. // StartRun 内部阻塞(AppData.StartAsync().Wait()),必须放后台线程避免卡 UI。
  61. // 复刻原 control Window1_Loaded1 的 Task.Run 形态,但去掉 Environment.Exit(0)——
  62. // 合并后 control 后台启动失败不应杀掉前台 operate 进程,改为捕获异常 + 记录日志降级。
  63. System.Threading.Tasks.Task.Run(() =>
  64. {
  65. try
  66. {
  67. // M1-02 步骤2:单登录账号透传。
  68. // 合并后只保留 operate 的 LoginWindow,control 的 Window1 登录窗已从启动路径剔除。
  69. // 把 operate 登录成功后保存的账号/密码(AppData.Instance.CurrentUserInfo)
  70. // 透传给 control 的 AppData.Login,使 control 后台用同一账号工作,不再弹独立登录窗。
  71. // 复刻原 control Window1_Loaded1(Window1.xaml.cs:51-87)里 StartRun 之前的前置:
  72. // ① ivf_tl_Control.AppData.Instance.Login(account, password)
  73. // ② PathHelper.pan / LogService.Pan = cacheDisk(缓存盘,仍从 control 侧 config 读,配置统一属 M5)
  74. // 去掉原 Window1 里的 Environment.Exit(0):合并后 control 登录/启动失败不杀前台进程,仅记日志降级。
  75. string account = AppData.Instance.CurrentUserInfo.account;
  76. string password = AppData.Instance.CurrentUserInfo.password;
  77. if (string.IsNullOrEmpty(account) || string.IsNullOrEmpty(password))
  78. {
  79. ivf_tl_Services.Log4netHelper.WriteLog("control 后台启动跳过:operate 登录账号/密码为空");
  80. return;
  81. }
  82. // ① control 端登录(与 operate 同一账号)。失败仅记日志、不退进程(V-012 真机验证)。
  83. if (!ivf_tl_Control.AppData.Instance.Login(account, password))
  84. {
  85. ivf_tl_Services.Log4netHelper.WriteLog("control 后台登录失败:单登录账号透传未通过 control 端 AppData.Login");
  86. return;
  87. }
  88. // ② 缓存盘:沿用 control 原 config 项 cacheDisk(StartRun 内随后会用服务器 tmpDir 覆盖 PathHelper.pan)。
  89. string cacheDisk = System.Configuration.ConfigurationManager.AppSettings["cacheDisk"];
  90. if (!string.IsNullOrEmpty(cacheDisk))
  91. {
  92. ivf_tl_UtilHelper.PathHelper.pan = cacheDisk;
  93. ivf_tl_Control.AppData.Instance.LogService.Pan = cacheDisk;
  94. }
  95. var startMain = new ivf_tl_Control.StartMain(); // 已并入的 control 类库
  96. // M1-03 步骤3:HAL 单例在 control StartRun 之前初始化一次(设备发现),
  97. // 确保后台采集与前台调试拿到的是同一组句柄(同 COM 口/同相机唯一持有)。
  98. // ⚠ 待验证 V-027:HAL.ScanDevices 设备发现稳定(相机index/COM漂移、CCDSN配对)。
  99. try
  100. {
  101. IvfTl.Hardware.Impl.HardwareAccessLayer.Instance.Log =
  102. msg => ivf_tl_Services.Log4netHelper.WriteLog(msg);
  103. var devices = IvfTl.Hardware.Impl.HardwareAccessLayer.Instance.ScanDevices();
  104. ivf_tl_Services.Log4netHelper.WriteLog($"HAL 设备发现完成:发现 {devices.Count} 个舱");
  105. }
  106. catch (Exception hex)
  107. {
  108. // HAL 发现失败不阻断 control 启动(control 自身仍会枚举),仅记日志降级。
  109. ivf_tl_Services.Log4netHelper.WriteLog("HAL 设备发现异常(降级)", hex);
  110. }
  111. string err = startMain.StartRun(); // 阻塞跑 InitTL→InitHouse→StartAsync
  112. if (!string.IsNullOrEmpty(err))
  113. ivf_tl_Services.Log4netHelper.WriteLog($"control 后台启动失败:{err}");
  114. else
  115. ivf_tl_Services.Log4netHelper.WriteLog("control 后台启动成功");
  116. }
  117. catch (Exception ex)
  118. {
  119. // 不退进程:仅记录日志,前台 operate 继续可用(自愈策略属后续)。
  120. ivf_tl_Services.Log4netHelper.WriteLog("control 后台启动异常", ex);
  121. }
  122. });
  123. }
  124. public void LoadPage(UserControl t)
  125. {
  126. _container.Content = t;
  127. }
  128. /// <summary>
  129. /// 显示与隐藏遮罩层
  130. /// </summary>
  131. /// <param name="v"></param>
  132. public void Mark(bool v)
  133. {
  134. Application.Current.Dispatcher.Invoke(() =>
  135. {
  136. if (v == true)
  137. {
  138. this._mask.Visibility = Visibility.Visible;
  139. }
  140. else
  141. {
  142. this._mask.Visibility = Visibility.Hidden;
  143. }
  144. });
  145. }
  146. public void DisableWPFTabletSupport()
  147. {
  148. TabletDeviceCollection devices = System.Windows.Input.Tablet.TabletDevices;
  149. if (devices.Count > 0)
  150. {
  151. Type inputManagerType = typeof(System.Windows.Input.InputManager);
  152. object stylusLogic = inputManagerType.InvokeMember("StylusLogic", BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, null, InputManager.Current, null);
  153. if (stylusLogic != null)
  154. {
  155. Type stylusLogicType = stylusLogic.GetType();
  156. while (devices.Count > 0)
  157. {
  158. stylusLogicType.InvokeMember("OnTabletRemoved",
  159. BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
  160. null, stylusLogic, new object[] { (uint)0 });
  161. }
  162. }
  163. }
  164. }
  165. }
  166. }