using Basler.Pylon; using CSScriptLib; using NPOI.SS.Formula.Functions; using NPOI.XSSF.Streaming.Values; using Prism.Commands; using Prism.Events; using Prism.Ioc; using Prism.Mvvm; using Prism.Regions; using Prism.Services.Dialogs; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Threading; using Team.FFFeederService.Interfaces; using TeamAAS_VP; using TeamAAS_VP.Controls; using TeamAAS_VP.Core; using TeamAAS_VP.Data; using TeamAAS_VP.Enums; using TeamAAS_VP.Events; using TeamAAS_VP.Interfaces; using TeamAAS_VP.Models; using TeamAAS_VP.Resources.Languages; using TeamAAS_VP.Services; using TeamAAS_VP.Views; using WPFLocalizeExtension.Engine; using static MaterialDesignThemes.Wpf.Theme.ToolBar; namespace TeamAAS_VP.ViewModels { public class MainWindowViewModel : BindableBase { IRegionManager _regionManager; IRegionNavigationService _regionNavigationService; IContainerProvider _container; IDialogService _dialogService; IEventAggregator _eventAggregator; IConfigService _configService; IRobotService _robotService; ICameraService _cameraService; IFeederService _feederService; IPlcService _plcService; IProductService _productService; ICalibrationService _calibrationService; ISystemDatabaseService _systemDatabaseService; IDatabaseInitializer _databaseInitializer; //private Management management; private Guid CurrentTime; private Guid RunTime; private DateTime StartTime; private Guid currentUser; Timer timer; /// /// 工程师和管理员权限的无操作超时时长。 /// 超过该时长后自动切回操作员权限。 /// private TimeSpan PermissionIdleTimeout = TimeSpan.FromMinutes(1); /// /// 权限空闲检测计时器。 /// 周期性检查最近一次用户输入距离当前时间是否超过超时阈值。 /// private DispatcherTimer _permissionIdleTimer; /// /// 最近一次键盘、鼠标、触摸或手写笔输入时间。 /// private DateTime _lastPermissionActivityTime; /// /// 标识权限超时处理是否正在执行,避免计时器重入。 /// private bool _isPermissionTimeoutRunning; /// /// 标识是否已订阅全局输入事件,避免重复订阅。 /// private bool _isTrackingUserInput; #region 属性 private Management _management; public Management management { get { return _management; } set { SetProperty(ref _management, value); } } private string _CompanyTitle; public string CompanyTitle { get { return _CompanyTitle; } set { SetProperty(ref _CompanyTitle, value); } } private string _AppVersion; public string AppVersion { get { return _AppVersion; } set { SetProperty(ref _AppVersion, value); } } private int _NavigationIndex = 0; public int NavigationIndex { get { return _NavigationIndex; } set { SetProperty(ref _NavigationIndex, value); } } private ObservableCollection _Status; public ObservableCollection Status { get { return _Status; } set { SetProperty(ref _Status, value); } } private string _MyTitle; public string MyTitle { get { return _MyTitle; } set { SetProperty(ref _MyTitle, value); } } private Language _CurrentLanguage; public Language CurrentLanguage { get { return _CurrentLanguage; } set { if (SetProperty(ref _CurrentLanguage, value)) { _configService?.SetCurrentLanguage(value); } } } #endregion #region 命令 private DelegateCommand _NavigationCommand; public DelegateCommand NavigationCommand { get { return _NavigationCommand; } set { _NavigationCommand = value; } } public DelegateCommand LoadedCommand { get; set; } public DelegateCommand ClosedCommand { get; set; } #endregion #region 事件 #endregion public MainWindowViewModel(IRegionManager regionManager, IEventAggregator ea, IRegionNavigationService regionNavigationService, IContainerProvider container, IDialogService dialogService, IConfigService configService, IRobotService robotService, ICameraService cameraService, IFeederService feederService, IPlcService plcService, IProductService productService, ICalibrationService calibrationService, ISystemDatabaseService systemDatabaseService, IDatabaseInitializer databaseInitializer) { _regionManager = regionManager; _regionNavigationService = regionNavigationService; _container = container; _dialogService = dialogService; _eventAggregator = ea; _configService = configService; _robotService = robotService; _cameraService = cameraService; _feederService = feederService; _plcService = plcService; _productService = productService; _calibrationService = calibrationService; _systemDatabaseService = systemDatabaseService; _databaseInitializer = databaseInitializer; //订阅权限登录事件 _eventAggregator.GetEvent().Subscribe(LoginChange); management = _container.Resolve(); NavigationCommand = new DelegateCommand(NavigationVoid); LoadedCommand = new DelegateCommand(OnLoad); ClosedCommand = new DelegateCommand(OnClose); Status = management.Status; try { _databaseInitializer.InitializeDatabase(); } catch (Exception ex) { LogHelper.WriteLogError("Database initialization failed during application startup", ex); } } #region 方法 //上一次导航的名称 string lastNavigationName = string.Empty; private void NavigationVoid(string obj) { if(!string.IsNullOrEmpty(lastNavigationName)) { if (lastNavigationName == obj) { return; } } lastNavigationName = obj; switch (obj) { case "Home": _regionManager.RequestNavigate("MainRegionContext", "HomeView"); NavigationIndex = 0; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Products": _regionManager.RequestNavigate("MainRegionContext", "ProductsView"); NavigationIndex = 1; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Debug": _regionManager.RequestNavigate("MainRegionContext", "DebugView"); NavigationIndex = 2; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Setting": _regionManager.RequestNavigate("MainRegionContext", "SettingView"); NavigationIndex = 3; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Image": _regionManager.RequestNavigate("MainRegionContext", "ImageView"); NavigationIndex = 5; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Calib": _regionManager.RequestNavigate("MainRegionContext", "CalibrationView"); NavigationIndex = 6; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "Statistics": _regionManager.RequestNavigate("MainRegionContext", "StatisticsView"); NavigationIndex = 7; _eventAggregator.GetEvent().Publish(NavigationIndex); break; case "User": _regionManager.RequestNavigate("MainRegionContext", "UserView"); NavigationIndex = 8; _eventAggregator.GetEvent().Publish(NavigationIndex); break; default: break; } } private async void OnLoad() { // 1. 读取配置参数 _configService.LoadAll(); if (_regionManager != null && _configService.GetSystemConfiguration().PermissionIdleTimeout > 0) { PermissionIdleTimeout = TimeSpan.FromMinutes(_configService.GetSystemConfiguration().PermissionIdleTimeout); } // 2. 加载校准 _calibrationService.LoadAll(); // 设置标题和版本号 var systemconfig = _configService.GetSystemConfiguration(); // 设置语言 CurrentLanguage = systemconfig.CurrentLanguage; _configService.SetCurrentLanguage(CurrentLanguage); MyTitle = systemconfig.Title; CompanyTitle = systemconfig.CompanyTitle; AppVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); systemconfig.SoftwareVersion = AppVersion; _configService.SaveSystemConfiguration(systemconfig); // 设置语言 #region 设置语言 var currentlanguage = _configService.GetSystemConfiguration().CurrentLanguage; if (currentlanguage == Enums.Language.ChineseSimplified) { var culture = new CultureInfo("zh-CN"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei"); } else if (currentlanguage == Enums.Language.ChineseTraditional) { var culture = new CultureInfo("zh-TW"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Microsoft YaHei"); } else if (currentlanguage == Enums.Language.English) { var culture = new CultureInfo("en"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } else if (currentlanguage == Enums.Language.Japanese) { var culture = new CultureInfo("ja-JP"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } else if (currentlanguage == Enums.Language.Vietnamese) { var culture = new CultureInfo("vi"); App.Current.Dispatcher.Thread.CurrentCulture = culture; App.Current.Dispatcher.Thread.CurrentUICulture = culture; LocalizeDictionary.Instance.Culture = culture; Lang.Culture = culture; Application.Current.Resources["DefaultFont"] = new System.Windows.Media.FontFamily("Segoe UI"); } #endregion SendTaskMessage(Lang.软件启动, MessageLevel.Info); StartTime = DateTime.Now; _regionManager.RequestNavigate("MainRegionContext", "HomeView"); double max = 14; double curvalue = 0; currentUser = Guid.NewGuid(); Status.Add(new StatusInfo(currentUser, $"{Lang.当前用户}:{_systemDatabaseService.GetCurrentUser().UserName}[{_systemDatabaseService.GetCurrentUser().userPart.ToString()}]", new SolidColorBrush(Colors.White))); var _= Application.Current.Dispatcher.BeginInvoke(new Action(() => { _dialogService.ShowDialog("UProgressBar", rst => { }); })); _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.初始化数据库}...", Value = curvalue }); })); //-------------------SETP 1: 初始化数据库----------------------------------------------------------------------- await Task.Delay(200); //初始化数据库 DatabaseHelper.InitDatabase(); SendTaskMessage($"{Lang.数据库读取完成}!", MessageLevel.Info); curvalue += 1; //-------------------SETP 2: 初始化Feeder----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化Feeder}...", Value = curvalue }); })); await management.InitFeeders(); //-------------------SETP 3: 初始化PLC----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化PLC}...", Value = curvalue }); })); await management.InitPlc(); //-------------------SETP 4: 初始化Robot----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化机器人}...", Value = curvalue }); })); await management.InitRobots(); //-------------------SETP 4.1: 初始化Scanner----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化机器人}...", Value = curvalue }); })); await management.InitScanners(); //-------------------SETP 5: 初始化Camera----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化相机}...", Value = curvalue }); })); await management.InitCameras(); //-------------------SETP 6: 初始化光源控制器----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在初始化光源控制器}...", Value = curvalue }); })); await management.InitLightControllers(); //-------------------SETP 7: 初始化电批----------------------------------------------------------------------- curvalue += 1; //_ = App.Current.Dispatcher.BeginInvoke(new Action(() => //{ // _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"正在连接电批...", Value = curvalue }); //})); //await management.InitScrewDriver(); //-------------------SETP 8: 加载所有产品----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.加载所有产品}...", Value = curvalue }); })); await _productService.InitializeAsync(); //-------------------SETP 9: 开启服务器监听----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.开启服务器监听}...", Value = curvalue }); })); management.InitBgTcp(); //-------------------SETP 10: 开启Modbus Tcp----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在开启}Modbus TCP Slave...", Value = curvalue }); })); management.InitModbusTcp(); //-------------------SETP 11: 开启后台脚本----------------------------------------------------------------------- curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在开启后台脚本}...", Value = curvalue }); })); management.InitBackgroundcript(); var sysConfig = _configService.GetSystemConfiguration(); if (sysConfig.ScannerUse == true) { await management.InitScan(); } //当前日期 CurrentTime = Guid.NewGuid(); Status.Add(new StatusInfo(CurrentTime, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), new SolidColorBrush(Colors.White))); //当前运行时长 RunTime = Guid.NewGuid(); var run = (DateTime.Now - StartTime); string formattedTime = string.Format(Lang.已运行时长, (int)run.TotalDays, run.Hours, run.Minutes, run.Seconds); Status.Add(new StatusInfo(RunTime, formattedTime, new SolidColorBrush(Colors.White))); timer = new Timer(DoTime, null, 1000, 1000); curvalue += 1; _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.正在加载产品}...", Value = curvalue }); })); Guid productid = _configService.GetSystemConfiguration().LastOpenedProductId; if (productid != Guid.Empty) { await _productService.LoadProductAsync(productid); _productService.SetCurrentProduct(productid); await management.WritePositionToPlcAsync(); } var homeviewmodel = _container.Resolve(); homeviewmodel.OnNavigatedTo(null); _ = App.Current.Dispatcher.BeginInvoke(new Action(() => { _eventAggregator.GetEvent().Publish(new UProgressBar.ProgressParameter { Maximum = max, Minimum = 0, SubTitle = $"{Lang.初始化中}...", Message = $"{Lang.完成}", Value = -1 }); })); // 初始弹窗登录发生在主窗口加载前,加载后需按当前用户补启权限空闲计时。 ConfigurePermissionIdleMonitor(_systemDatabaseService.GetCurrentUser()); } private void OnClose() { StopPermissionIdleMonitor(); Stop(); } private void SendTaskMessage(string msg, MessageLevel level) { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = msg, level = level }); } private void DoTime(object state) { //当前日期 var c = Status.FirstOrDefault(b => b.ID == CurrentTime); c.Message = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); //当前运行时长 var d = Status.FirstOrDefault(b => b.ID == RunTime); var run = (DateTime.Now - StartTime); d.Message = string.Format(Lang.已运行时长, (int)run.TotalDays, run.Hours, run.Minutes, run.Seconds); } public void Stop() { timer?.Dispose(); timer = null; } /// /// 处理用户登录或权限切换事件。 /// 更新状态栏当前用户,登录后跳转主页,并根据用户权限启停空闲降权检测。 /// /// 触发事件的用户对象。 private void LoginChange(Models.User user) { var current = _systemDatabaseService.GetCurrentUser(); var us = Status.FirstOrDefault(u => u.ID == currentUser); if (us != null) { us.Message = $"{Lang.当前用户}:{current.UserName}[{current.userPart.ToString()}]"; } _regionManager.RequestNavigate("MainRegionContext", "HomeView"); lastNavigationName = "Home"; NavigationIndex = 0; _eventAggregator.GetEvent().Publish(NavigationIndex); ConfigurePermissionIdleMonitor(current); } /// /// 配置工程师和管理员权限的空闲检测。 /// 操作员权限不需要超时检测,会停止现有计时和输入监听。 /// /// 当前登录用户。 private void ConfigurePermissionIdleMonitor(Models.User user) { if (user == null || user.userPart == UserPart.Operator) { StopPermissionIdleMonitor(); return; } _lastPermissionActivityTime = DateTime.Now; if (!_isTrackingUserInput) { InputManager.Current.PostProcessInput += OnUserInput; ComponentDispatcher.ThreadFilterMessage += OnThreadFilterMessage; _isTrackingUserInput = true; } if (_permissionIdleTimer == null) { _permissionIdleTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) }; _permissionIdleTimer.Tick += PermissionIdleTimer_Tick; } _permissionIdleTimer.Start(); } /// /// 停止权限空闲检测并解绑全局输入事件。 /// private void StopPermissionIdleMonitor() { if (_permissionIdleTimer != null) { _permissionIdleTimer.Stop(); } if (_isTrackingUserInput) { InputManager.Current.PostProcessInput -= OnUserInput; ComponentDispatcher.ThreadFilterMessage -= OnThreadFilterMessage; _isTrackingUserInput = false; } } private void RecordPermissionActivity() { _lastPermissionActivityTime = DateTime.Now; } /// /// 记录用户输入时间。 /// 键盘、鼠标、滚轮、触摸和手写笔输入都会刷新空闲计时。 /// /// 输入事件发送方。 /// 输入事件参数。 private void OnUserInput(object sender, ProcessInputEventArgs e) { var input = e?.StagingItem?.Input; if (input is KeyEventArgs || input is MouseButtonEventArgs || input is MouseWheelEventArgs || input is MouseEventArgs || input is TouchEventArgs || input is StylusEventArgs) { RecordPermissionActivity(); } } private void OnThreadFilterMessage(ref MSG msg, ref bool handled) { if (IsUserInputMessage(msg.message)) { RecordPermissionActivity(); } } private static bool IsUserInputMessage(int message) { switch (message) { case 0x0100: // WM_KEYDOWN case 0x0101: // WM_KEYUP case 0x0104: // WM_SYSKEYDOWN case 0x0105: // WM_SYSKEYUP case 0x0200: // WM_MOUSEMOVE case 0x0201: // WM_LBUTTONDOWN case 0x0202: // WM_LBUTTONUP case 0x0203: // WM_LBUTTONDBLCLK case 0x0204: // WM_RBUTTONDOWN case 0x0205: // WM_RBUTTONUP case 0x0206: // WM_RBUTTONDBLCLK case 0x0207: // WM_MBUTTONDOWN case 0x0208: // WM_MBUTTONUP case 0x0209: // WM_MBUTTONDBLCLK case 0x020A: // WM_MOUSEWHEEL case 0x020B: // WM_XBUTTONDOWN case 0x020C: // WM_XBUTTONUP case 0x020D: // WM_XBUTTONDBLCLK case 0x020E: // WM_MOUSEHWHEEL return true; default: return false; } } /// /// 周期性检查工程师或管理员权限是否超时。 /// 如果 3 分钟无操作,则将当前用户切回操作员并广播权限切换事件。 /// /// 计时器事件发送方。 /// 计时器事件参数。 private async void PermissionIdleTimer_Tick(object sender, EventArgs e) { if (_isPermissionTimeoutRunning) return; var current = _systemDatabaseService.GetCurrentUser(); if (current == null || current.userPart == UserPart.Operator) { StopPermissionIdleMonitor(); return; } if (DateTime.Now - _lastPermissionActivityTime < PermissionIdleTimeout) return; _isPermissionTimeoutRunning = true; try { var users = await _systemDatabaseService.GetAllUsersAsync(); var operatorUser = users?.FirstOrDefault(u => u.userPart == UserPart.Operator); if (operatorUser == null) { App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = "权限超时,但未找到操作员账号", level = MessageLevel.Error }); }); StopPermissionIdleMonitor(); return; } await _systemDatabaseService.SetCurrentUserAsync(operatorUser.Id); _eventAggregator.GetEvent().Publish(_systemDatabaseService.GetCurrentUser()); _eventAggregator.GetEvent().Publish(); _regionManager.RequestNavigate("MainRegionContext", "HomeView"); NavigationIndex = 0; _eventAggregator.GetEvent().Publish(NavigationIndex); App.Current.Dispatcher.Invoke(() => { _eventAggregator.GetEvent().Publish(new Models.MessageStruct() { Message = "权限超时,已自动退出工程师/管理员权限", level = MessageLevel.Error }); }); } finally { _isPermissionTimeoutRunning = false; } } #endregion } }