MainWindowViewModel.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. using Basler.Pylon;
  2. using CSScriptLib;
  3. using NPOI.SS.Formula.Functions;
  4. using NPOI.XSSF.Streaming.Values;
  5. using Prism.Commands;
  6. using Prism.Events;
  7. using Prism.Ioc;
  8. using Prism.Mvvm;
  9. using Prism.Regions;
  10. using Prism.Services.Dialogs;
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Globalization;
  15. using System.Linq;
  16. using System.Text;
  17. using System.Text.RegularExpressions;
  18. using System.Threading;
  19. using System.Threading.Tasks;
  20. using System.Windows;
  21. using System.Windows.Controls;
  22. using System.Windows.Controls.Primitives;
  23. using System.Windows.Input;
  24. using System.Windows.Interop;
  25. using System.Windows.Media;
  26. using System.Windows.Threading;
  27. using Team.FFFeederService.Interfaces;
  28. using TeamAAS_VP;
  29. using TeamAAS_VP.Controls;
  30. using TeamAAS_VP.Core;
  31. using TeamAAS_VP.Data;
  32. using TeamAAS_VP.Enums;
  33. using TeamAAS_VP.Events;
  34. using TeamAAS_VP.Interfaces;
  35. using TeamAAS_VP.Models;
  36. using TeamAAS_VP.Resources.Languages;
  37. using TeamAAS_VP.Services;
  38. using TeamAAS_VP.Views;
  39. using WPFLocalizeExtension.Engine;
  40. using static MaterialDesignThemes.Wpf.Theme.ToolBar;
  41. namespace TeamAAS_VP.ViewModels
  42. {
  43. public class MainWindowViewModel : BindableBase
  44. {
  45. IRegionManager _regionManager;
  46. IRegionNavigationService _regionNavigationService;
  47. IContainerProvider _container;
  48. IDialogService _dialogService;
  49. IEventAggregator _eventAggregator;
  50. IConfigService _configService;
  51. IRobotService _robotService;
  52. ICameraService _cameraService;
  53. IFeederService _feederService;
  54. IPlcService _plcService;
  55. IProductService _productService;
  56. ICalibrationService _calibrationService;
  57. ISystemDatabaseService _systemDatabaseService;
  58. IDatabaseInitializer _databaseInitializer;
  59. //private Management management;
  60. private Guid CurrentTime;
  61. private Guid RunTime;
  62. private DateTime StartTime;
  63. private Guid currentUser;
  64. Timer timer;
  65. /// <summary>
  66. /// 工程师和管理员权限的无操作超时时长。
  67. /// 超过该时长后自动切回操作员权限。
  68. /// </summary>
  69. private readonly TimeSpan PermissionIdleTimeout = TimeSpan.FromMinutes(0.5);
  70. /// <summary>
  71. /// 权限空闲检测计时器。
  72. /// 周期性检查最近一次用户输入距离当前时间是否超过超时阈值。
  73. /// </summary>
  74. private DispatcherTimer _permissionIdleTimer;
  75. /// <summary>
  76. /// 最近一次键盘、鼠标、触摸或手写笔输入时间。
  77. /// </summary>
  78. private DateTime _lastPermissionActivityTime;
  79. /// <summary>
  80. /// 标识权限超时处理是否正在执行,避免计时器重入。
  81. /// </summary>
  82. private bool _isPermissionTimeoutRunning;
  83. /// <summary>
  84. /// 标识是否已订阅全局输入事件,避免重复订阅。
  85. /// </summary>
  86. private bool _isTrackingUserInput;
  87. #region 属性
  88. private Management _management;
  89. public Management management
  90. {
  91. get { return _management; }
  92. set { SetProperty(ref _management, value); }
  93. }
  94. private string _CompanyTitle;
  95. public string CompanyTitle
  96. {
  97. get { return _CompanyTitle; }
  98. set { SetProperty(ref _CompanyTitle, value); }
  99. }
  100. private string _AppVersion;
  101. public string AppVersion
  102. {
  103. get { return _AppVersion; }
  104. set { SetProperty(ref _AppVersion, value); }
  105. }
  106. private int _NavigationIndex = 0;
  107. public int NavigationIndex
  108. {
  109. get { return _NavigationIndex; }
  110. set { SetProperty(ref _NavigationIndex, value); }
  111. }
  112. private ObservableCollection<StatusInfo> _Status;
  113. public ObservableCollection<StatusInfo> Status
  114. {
  115. get { return _Status; }
  116. set { SetProperty(ref _Status, value); }
  117. }
  118. private string _MyTitle;
  119. public string MyTitle
  120. {
  121. get { return _MyTitle; }
  122. set { SetProperty(ref _MyTitle, value); }
  123. }
  124. #endregion
  125. #region 命令
  126. private DelegateCommand<string> _NavigationCommand;
  127. public DelegateCommand<string> NavigationCommand
  128. {
  129. get { return _NavigationCommand; }
  130. set { _NavigationCommand = value; }
  131. }
  132. public DelegateCommand LoadedCommand { get; set; }
  133. public DelegateCommand ClosedCommand { get; set; }
  134. #endregion
  135. #region 事件
  136. #endregion
  137. public MainWindowViewModel(IRegionManager regionManager, IEventAggregator ea, IRegionNavigationService regionNavigationService, IContainerProvider container, IDialogService dialogService,
  138. IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService,
  139. ICalibrationService calibrationService, ISystemDatabaseService systemDatabaseService, IDatabaseInitializer databaseInitializer)
  140. {
  141. _regionManager = regionManager;
  142. _regionNavigationService = regionNavigationService;
  143. _container = container;
  144. _dialogService = dialogService;
  145. _eventAggregator = ea;
  146. _configService = configService;
  147. _robotService = robotService;
  148. _cameraService = cameraService;
  149. _feederService = feederService;
  150. _plcService = plcService;
  151. _productService = productService;
  152. _calibrationService = calibrationService;
  153. _systemDatabaseService = systemDatabaseService;
  154. _databaseInitializer = databaseInitializer;
  155. //订阅权限登录事件
  156. _eventAggregator.GetEvent<UserLoginNotification>().Subscribe(LoginChange);
  157. management = _container.Resolve<Management>();
  158. NavigationCommand = new DelegateCommand<string>(NavigationVoid);
  159. LoadedCommand = new DelegateCommand(OnLoad);
  160. ClosedCommand = new DelegateCommand(OnClose);
  161. Status = management.Status;
  162. try
  163. {
  164. _databaseInitializer.InitializeDatabase();
  165. }
  166. catch (Exception ex)
  167. {
  168. LogHelper.WriteLogError("Database initialization failed during application startup", ex);
  169. }
  170. }
  171. #region 方法
  172. //上一次导航的名称
  173. string lastNavigationName = string.Empty;
  174. private void NavigationVoid(string obj)
  175. {
  176. if(!string.IsNullOrEmpty(lastNavigationName))
  177. {
  178. if (lastNavigationName == obj)
  179. {
  180. return;
  181. }
  182. }
  183. lastNavigationName = obj;
  184. switch (obj)
  185. {
  186. case "Home":
  187. _regionManager.RequestNavigate("MainRegionContext", "HomeView");
  188. NavigationIndex = 0;
  189. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  190. break;
  191. case "Products":
  192. _regionManager.RequestNavigate("MainRegionContext", "ProductsView");
  193. NavigationIndex = 1;
  194. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  195. break;
  196. case "Debug":
  197. _regionManager.RequestNavigate("MainRegionContext", "DebugView");
  198. NavigationIndex = 2;
  199. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  200. break;
  201. case "Setting":
  202. _regionManager.RequestNavigate("MainRegionContext", "SettingView");
  203. NavigationIndex = 3;
  204. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  205. break;
  206. case "Image":
  207. _regionManager.RequestNavigate("MainRegionContext", "ImageView");
  208. NavigationIndex = 5;
  209. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  210. break;
  211. case "Calib":
  212. _regionManager.RequestNavigate("MainRegionContext", "CalibrationView");
  213. NavigationIndex = 6;
  214. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  215. break;
  216. case "Statistics":
  217. _regionManager.RequestNavigate("MainRegionContext", "StatisticsView");
  218. NavigationIndex = 7;
  219. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  220. break;
  221. case "User":
  222. _regionManager.RequestNavigate("MainRegionContext", "UserView");
  223. NavigationIndex = 8;
  224. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  225. break;
  226. default:
  227. break;
  228. }
  229. }
  230. private async void OnLoad()
  231. {
  232. // 1. 读取配置参数
  233. _configService.LoadAll();
  234. // 2. 加载校准
  235. _calibrationService.LoadAll();
  236. // 设置标题和版本号
  237. var systemconfig = _configService.GetSystemConfiguration();
  238. MyTitle = systemconfig.Title;
  239. CompanyTitle = systemconfig.CompanyTitle;
  240. AppVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
  241. systemconfig.SoftwareVersion = AppVersion;
  242. _configService.SaveSystemConfiguration(systemconfig);
  243. // 设置语言
  244. #region 设置语言
  245. var currentlanguage = _configService.GetSystemConfiguration().CurrentLanguage;
  246. if (currentlanguage == Enums.Language.ChineseSimplified)
  247. {
  248. var culture = new CultureInfo("zh-CN");
  249. App.Current.Dispatcher.Thread.CurrentCulture = culture;
  250. App.Current.Dispatcher.Thread.CurrentUICulture = culture;
  251. LocalizeDictionary.Instance.Culture = culture;
  252. Lang.Culture = culture;
  253. Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei");
  254. }
  255. else if (currentlanguage == Enums.Language.ChineseTraditional)
  256. {
  257. var culture = new CultureInfo("zh-TW");
  258. App.Current.Dispatcher.Thread.CurrentCulture = culture;
  259. App.Current.Dispatcher.Thread.CurrentUICulture = culture;
  260. LocalizeDictionary.Instance.Culture = culture;
  261. Lang.Culture = culture;
  262. Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei");
  263. }
  264. else if (currentlanguage == Enums.Language.English)
  265. {
  266. var culture = new CultureInfo("en");
  267. App.Current.Dispatcher.Thread.CurrentCulture = culture;
  268. App.Current.Dispatcher.Thread.CurrentUICulture = culture;
  269. LocalizeDictionary.Instance.Culture = culture;
  270. Lang.Culture = culture;
  271. Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI");
  272. }
  273. else if (currentlanguage == Enums.Language.Japanese)
  274. {
  275. var culture = new CultureInfo("ja-JP");
  276. App.Current.Dispatcher.Thread.CurrentCulture = culture;
  277. App.Current.Dispatcher.Thread.CurrentUICulture = culture;
  278. LocalizeDictionary.Instance.Culture = culture;
  279. Lang.Culture = culture;
  280. Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI");
  281. }
  282. else if (currentlanguage == Enums.Language.Vietnamese)
  283. {
  284. var culture = new CultureInfo("vi");
  285. App.Current.Dispatcher.Thread.CurrentCulture = culture;
  286. App.Current.Dispatcher.Thread.CurrentUICulture = culture;
  287. LocalizeDictionary.Instance.Culture = culture;
  288. Lang.Culture = culture;
  289. Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI");
  290. }
  291. #endregion
  292. SendTaskMessage(Lang.软件启动, MessageLevel.Info);
  293. StartTime = DateTime.Now;
  294. _regionManager.RequestNavigate("MainRegionContext", "HomeView");
  295. double max = 14;
  296. double curvalue = 0;
  297. currentUser = Guid.NewGuid();
  298. Status.Add(new StatusInfo(currentUser, $"{Lang.当前用户}:{_systemDatabaseService.GetCurrentUser().UserName}[{_systemDatabaseService.GetCurrentUser().userPart.ToString()}]", new SolidColorBrush(Colors.White)));
  299. var _= Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); }));
  300. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  301. {
  302. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.初始化数据库}...", Value = curvalue });
  303. }));
  304. //-------------------SETP 1: 初始化数据库-----------------------------------------------------------------------
  305. await Task.Delay(200);
  306. //初始化数据库
  307. DatabaseHelper.InitDatabase();
  308. SendTaskMessage($"{Lang.数据库读取完成}!", MessageLevel.Info);
  309. curvalue += 1;
  310. //-------------------SETP 2: 初始化Feeder-----------------------------------------------------------------------
  311. curvalue += 1;
  312. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  313. {
  314. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化Feeder}...", Value = curvalue });
  315. }));
  316. await management.InitFeeders();
  317. //-------------------SETP 3: 初始化PLC-----------------------------------------------------------------------
  318. curvalue += 1;
  319. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  320. {
  321. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化PLC}...", Value = curvalue });
  322. }));
  323. await management.InitPlc();
  324. //-------------------SETP 4: 初始化Robot-----------------------------------------------------------------------
  325. curvalue += 1;
  326. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  327. {
  328. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化机器人}...", Value = curvalue });
  329. }));
  330. await management.InitRobots();
  331. //-------------------SETP 4.1: 初始化Scanner-----------------------------------------------------------------------
  332. curvalue += 1;
  333. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  334. {
  335. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化机器人}...", Value = curvalue });
  336. }));
  337. await management.InitScanners();
  338. //-------------------SETP 5: 初始化Camera-----------------------------------------------------------------------
  339. curvalue += 1;
  340. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  341. {
  342. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化相机}...", Value = curvalue });
  343. }));
  344. await management.InitCameras();
  345. //-------------------SETP 6: 初始化光源控制器-----------------------------------------------------------------------
  346. curvalue += 1;
  347. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  348. {
  349. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化光源控制器}...", Value = curvalue });
  350. }));
  351. await management.InitLightControllers();
  352. //-------------------SETP 7: 初始化电批-----------------------------------------------------------------------
  353. curvalue += 1;
  354. //_ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  355. //{
  356. // _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"正在连接电批...", Value = curvalue });
  357. //}));
  358. //await management.InitScrewDriver();
  359. //-------------------SETP 8: 加载所有产品-----------------------------------------------------------------------
  360. curvalue += 1;
  361. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  362. {
  363. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.加载所有产品}...", Value = curvalue });
  364. }));
  365. await _productService.InitializeAsync();
  366. //-------------------SETP 9: 开启服务器监听-----------------------------------------------------------------------
  367. curvalue += 1;
  368. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  369. {
  370. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.开启服务器监听}...", Value = curvalue });
  371. }));
  372. management.InitBgTcp();
  373. //-------------------SETP 10: 开启Modbus Tcp-----------------------------------------------------------------------
  374. curvalue += 1;
  375. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  376. {
  377. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在开启}Modbus TCP Slave...", Value = curvalue });
  378. }));
  379. management.InitModbusTcp();
  380. //-------------------SETP 11: 开启后台脚本-----------------------------------------------------------------------
  381. curvalue += 1;
  382. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  383. {
  384. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在开启后台脚本}...", Value = curvalue });
  385. }));
  386. management.InitBackgroundcript();
  387. var sysConfig = _configService.GetSystemConfiguration();
  388. if (sysConfig.ScannerUse == true)
  389. {
  390. await management.InitScan();
  391. }
  392. //当前日期
  393. CurrentTime = Guid.NewGuid();
  394. Status.Add(new StatusInfo(CurrentTime, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), new SolidColorBrush(Colors.White)));
  395. //当前运行时长
  396. RunTime = Guid.NewGuid();
  397. var run = (DateTime.Now - StartTime);
  398. string formattedTime = string.Format(Lang.已运行时长, (int)run.TotalDays, run.Hours, run.Minutes, run.Seconds);
  399. Status.Add(new StatusInfo(RunTime, formattedTime, new SolidColorBrush(Colors.White)));
  400. timer = new Timer(DoTime, null, 1000, 1000);
  401. curvalue += 1;
  402. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  403. {
  404. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在加载产品}...", Value = curvalue });
  405. }));
  406. Guid productid = _configService.GetSystemConfiguration().LastOpenedProductId;
  407. if (productid != Guid.Empty)
  408. {
  409. await _productService.LoadProductAsync(productid);
  410. _productService.SetCurrentProduct(productid);
  411. await management.WritePositionToPlcAsync();
  412. }
  413. var homeviewmodel = _container.Resolve<HomeViewModel>();
  414. homeviewmodel.OnNavigatedTo(null);
  415. _ = App.Current.Dispatcher.BeginInvoke(new Action(() =>
  416. {
  417. _eventAggregator.GetEvent<ProgressNotification>().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.完成}", Value = -1 });
  418. }));
  419. // 初始弹窗登录发生在主窗口加载前,加载后需按当前用户补启权限空闲计时。
  420. ConfigurePermissionIdleMonitor(_systemDatabaseService.GetCurrentUser());
  421. }
  422. private void OnClose()
  423. {
  424. StopPermissionIdleMonitor();
  425. Stop();
  426. }
  427. private void SendTaskMessage(string msg, MessageLevel level)
  428. {
  429. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = msg, level = level });
  430. }
  431. private void DoTime(object state)
  432. {
  433. //当前日期
  434. var c = Status.FirstOrDefault(b => b.ID == CurrentTime);
  435. c.Message = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  436. //当前运行时长
  437. var d = Status.FirstOrDefault(b => b.ID == RunTime);
  438. var run = (DateTime.Now - StartTime);
  439. d.Message = string.Format(Lang.已运行时长, (int)run.TotalDays, run.Hours, run.Minutes, run.Seconds);
  440. }
  441. public void Stop()
  442. {
  443. timer?.Dispose();
  444. timer = null;
  445. }
  446. /// <summary>
  447. /// 处理用户登录或权限切换事件。
  448. /// 更新状态栏当前用户,登录后跳转主页,并根据用户权限启停空闲降权检测。
  449. /// </summary>
  450. /// <param name="user">触发事件的用户对象。</param>
  451. private void LoginChange(Models.User user)
  452. {
  453. var current = _systemDatabaseService.GetCurrentUser();
  454. var us = Status.FirstOrDefault(u => u.ID == currentUser);
  455. if (us != null)
  456. {
  457. us.Message = $"{Lang.当前用户}:{current.UserName}[{current.userPart.ToString()}]";
  458. }
  459. _regionManager.RequestNavigate("MainRegionContext", "HomeView");
  460. lastNavigationName = "Home";
  461. NavigationIndex = 0;
  462. _eventAggregator.GetEvent<MainTabSwitchNotification>().Publish(NavigationIndex);
  463. ConfigurePermissionIdleMonitor(current);
  464. }
  465. /// <summary>
  466. /// 配置工程师和管理员权限的空闲检测。
  467. /// 操作员权限不需要超时检测,会停止现有计时和输入监听。
  468. /// </summary>
  469. /// <param name="user">当前登录用户。</param>
  470. private void ConfigurePermissionIdleMonitor(Models.User user)
  471. {
  472. if (user == null || user.userPart == UserPart.Operator)
  473. {
  474. StopPermissionIdleMonitor();
  475. return;
  476. }
  477. _lastPermissionActivityTime = DateTime.Now;
  478. if (!_isTrackingUserInput)
  479. {
  480. InputManager.Current.PostProcessInput += OnUserInput;
  481. _isTrackingUserInput = true;
  482. }
  483. if (_permissionIdleTimer == null)
  484. {
  485. _permissionIdleTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
  486. _permissionIdleTimer.Tick += PermissionIdleTimer_Tick;
  487. }
  488. _permissionIdleTimer.Start();
  489. }
  490. /// <summary>
  491. /// 停止权限空闲检测并解绑全局输入事件。
  492. /// </summary>
  493. private void StopPermissionIdleMonitor()
  494. {
  495. if (_permissionIdleTimer != null)
  496. {
  497. _permissionIdleTimer.Stop();
  498. }
  499. if (_isTrackingUserInput)
  500. {
  501. InputManager.Current.PostProcessInput -= OnUserInput;
  502. _isTrackingUserInput = false;
  503. }
  504. }
  505. /// <summary>
  506. /// 记录用户输入时间。
  507. /// 键盘、鼠标、滚轮、触摸和手写笔输入都会刷新空闲计时。
  508. /// </summary>
  509. /// <param name="sender">输入事件发送方。</param>
  510. /// <param name="e">输入事件参数。</param>
  511. private void OnUserInput(object sender, ProcessInputEventArgs e)
  512. {
  513. var input = e?.StagingItem?.Input;
  514. if (input is KeyEventArgs ||
  515. input is MouseButtonEventArgs ||
  516. input is MouseWheelEventArgs ||
  517. input is MouseEventArgs ||
  518. input is TouchEventArgs ||
  519. input is StylusEventArgs)
  520. {
  521. _lastPermissionActivityTime = DateTime.Now;
  522. }
  523. }
  524. /// <summary>
  525. /// 周期性检查工程师或管理员权限是否超时。
  526. /// 如果 3 分钟无操作,则将当前用户切回操作员并广播权限切换事件。
  527. /// </summary>
  528. /// <param name="sender">计时器事件发送方。</param>
  529. /// <param name="e">计时器事件参数。</param>
  530. private async void PermissionIdleTimer_Tick(object sender, EventArgs e)
  531. {
  532. if (_isPermissionTimeoutRunning)
  533. return;
  534. var current = _systemDatabaseService.GetCurrentUser();
  535. if (current == null || current.userPart == UserPart.Operator)
  536. {
  537. StopPermissionIdleMonitor();
  538. return;
  539. }
  540. if (DateTime.Now - _lastPermissionActivityTime < PermissionIdleTimeout)
  541. return;
  542. _isPermissionTimeoutRunning = true;
  543. try
  544. {
  545. var users = await _systemDatabaseService.GetAllUsersAsync();
  546. var operatorUser = users?.FirstOrDefault(u => u.userPart == UserPart.Operator);
  547. if (operatorUser == null)
  548. {
  549. App.Current.Dispatcher.Invoke(() =>
  550. {
  551. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = "权限超时,但未找到操作员账号", level = MessageLevel.Error });
  552. });
  553. StopPermissionIdleMonitor();
  554. return;
  555. }
  556. await _systemDatabaseService.SetCurrentUserAsync(operatorUser.Id);
  557. _eventAggregator.GetEvent<UserLoginNotification>().Publish(_systemDatabaseService.GetCurrentUser());
  558. _eventAggregator.GetEvent<CurrentStatusNotification>().Publish();
  559. App.Current.Dispatcher.Invoke(() =>
  560. {
  561. _eventAggregator.GetEvent<TaskMessageNotification>().Publish(new Models.MessageStruct() { Message = "权限超时,已自动退出工程师/管理员权限", level = MessageLevel.Error });
  562. });
  563. }
  564. finally
  565. {
  566. _isPermissionTimeoutRunning = false;
  567. }
  568. }
  569. #endregion
  570. }
  571. }