using Aitex.Core.Account; using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.Util; using Aitex.Core.WCF; using Caliburn.Micro; using Caliburn.Micro.Core; using MECF.Framework.Common.Account.Extends; using MECF.Framework.Common.Account.Permissions; using MECF.Framework.Common.MECF.Framework.Common.SCCore; using MECF.Framework.Common.Utilities; using MECF.Framework.UI.Client.CenterViews.LogOnOff; using MECF.Framework.UI.Client.ClientBase; using MECF.Framework.UI.Client.ClientBase.Dialog; using MECF.Framework.UI.Client.Core; using MECF.Framework.UI.Client.Ctrlib.Controls; using MECF.Framework.UI.Core.Accounts; using OpenSEMI.ClientBase; using OpenSEMI.ClientBase.Command; using OpenSEMI.ClientBase.Utility; using Sicentury.Core.Collections; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; using Action = System.Action; namespace SicUI.Client { public class MainViewModel : TimeredMainViewModel { #region Variables private MainView _view; private AppMenu _alarmMenu; private AppMenu _currentMenuItem; private List _menuItems; private List _subMenuItems; private ObservableCollection _historyItems; private Dictionary _models; private string _context; private bool _isReadOnlyMode; private readonly IEventAggregator _eventAggregator; private readonly IProgress _prgShowLoginRequestConfirmDialog; private CancellationTokenSource _ctsLoginRequestConfirmDialog; private readonly IWindowManager _windowmanager; #endregion #region Constructors public MainViewModel(IEventAggregator eventAggregator, IWindowManager wmr) { ((ClientApp)BaseApp.Instance).ViewModelSwitcher = this; _models = new Dictionary(); _eventAggregator = eventAggregator; //for login part Roles = RoleAccountProvider.Instance.GetRoles(); EventLogList = new DelayedPresentRollingObservableCollection(1000); EventLogsView = CollectionViewSource.GetDefaultView(EventLogList); //WarnEventLogList = new ObservableCollection(); SoftwareVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); EventClient.Instance.OnEvent += Instance_OnEvent; EventClient.Instance.OnDisconnectedWithRT += Instance_OnDisconnectedWithRT; EventClient.Instance.Start(); IsPM1Installed = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsPM1Installed"); IsPM2Installed = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsPM2Installed"); IsBufferInstalled = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsBufferInstalled"); IsLLInstalled = (bool)QueryDataClient.Instance.Service.GetConfig("System.SetUp.IsLoadLockInstalled"); var equipmentName = (string)QueryDataClient.Instance.Service.GetConfig("System.EquipmentName"); var equipmentNo = (int)QueryDataClient.Instance.Service.GetConfig("System.EquipmentNo"); EquipmentName = equipmentName.ToLower() == "unset" ? "Unset" : $"{equipmentName}-{equipmentNo:D4}"; _prgShowLoginRequestConfirmDialog = new Progress(ShowLoginRequestConfirmDialog); Reset(); _windowmanager = wmr; } #endregion #region Menus public string EquipmentName { get; } public string NowDateTime { get; set; } private bool _isLogin = false; public bool IsLogin { get => _isLogin; set { _isLogin = value; NotifyOfPropertyChange(); } } private List _roles; public List Roles { get => _roles; set { _roles = value; RaisePropertyChangedEventImmediately("Roles"); } } private ICommand _menuItemClickCommand; public ICommand MenuItemClickCommand { get { if (_menuItemClickCommand == null) _menuItemClickCommand = new BaseCommand(SwitchMenuItem); return _menuItemClickCommand; } } private ICommand _mainMenuItemClickCommand; public ICommand MainMenuItemClickCommand { get { if (_mainMenuItemClickCommand == null) _mainMenuItemClickCommand = new BaseCommand(MainSwitchMenuItem); return _mainMenuItemClickCommand; } } public List MenuItems { get => _menuItems; set { _menuItems = value; NotifyOfPropertyChange(); } } public List SubMenuItems { get => _subMenuItems; set { _subMenuItems = value; NotifyOfPropertyChange(); } } public ObservableCollection HistoryMenus { get => _historyItems; set { _historyItems = value; NotifyOfPropertyChange(); } } public string Context { get => _context; set { _context = value; NotifyOfPropertyChange(); } } public UserContext User => BaseApp.Instance.UserContext; #endregion #region Properties public BaseModel CurrentViewModel { get; private set; } public bool IsPM1Installed { get; set; } public bool IsPM2Installed { get; set; } public bool IsBufferInstalled { get; set; } public bool IsLLInstalled { get; set; } public bool IsPermission { get; set; } public bool IsAutoLogout { get; set; } public int LogoutTime { get; set; } public bool IsReadOnlyMode { get => _isReadOnlyMode; set { _isReadOnlyMode = value; NotifyOfPropertyChange(); if (_isReadOnlyMode) { BaseApp.Instance.UserMode = UserMode.ReadOnly; // 通知RecipeEditor视图或PMProcess视图锁定Recipe _eventAggregator.PublishOnUIThread(CommonAggregateEvents.EnterReadOnlyMode); } } } //public ObservableCollection WarnEventLogList { get; set; } private DelayedPresentRollingObservableCollection EventLogList { get; } /// /// 用于在主界面显示Event Log的视图。 /// 通过该视图筛选IsAlarm条目。 /// public ICollectionView EventLogsView { get; } private bool _isShowAlarmEventOnly; /// /// IsAlarm CheckBox绑定到这里,直接从中过滤所需的数据。 /// public bool IsShowAlarmEventOnly { get => _isShowAlarmEventOnly; set { _isShowAlarmEventOnly = value; if (_isShowAlarmEventOnly) { EventLogsView.Filter = item => { if (item is EventItem ei) { return ei.Level == EventLevel.Alarm; } return false; }; } else { EventLogsView.Filter = null; } OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsShowAlarmEventOnly))); } } public Visibility AllEventsVisibility { get; set; } public Visibility WarnEventsVisibility { get; set; } [Subscription("Rt.Status")] public string RtStatus { get; set; } [Subscription("PM1.Status")] public string _PM1Status { get; set; } [Subscription("PM1.IsBypassInterlock")] public bool IsPM1InterlockBypassed { get; set; } [Subscription("PM1.IsBypassEnableTable")] public bool IsPM1EnableTableBypassed { get; set; } public bool IsPM1Warning => IsPM1InterlockBypassed || IsPM1EnableTableBypassed; public string PM1WarningMessage { get { if (IsPM1InterlockBypassed && IsPM1EnableTableBypassed) return "PM1 Interlock and Enable Table are bypassed"; if (IsPM1InterlockBypassed) return "PM1 Interlock is bypassed"; return "PM1 Enable Table is bypassed"; } } public string PM1Status { get { if (IsPM1Installed) return _PM1Status; else return "NotInstall"; } set => _PM1Status = value; } [Subscription("PM2.Status")] public string _PM2Status { get; set; } public string PM2Status { get { if (IsPM2Installed) return _PM2Status; else return "NotInstall"; } set => _PM2Status = value; } [Subscription("PM2.IsBypassInterlock")] public bool IsPM2InterlockBypassed { get; set; } [Subscription("PM2.IsBypassEnableTable")] public bool IsPM2EnableTableBypassed { get; set; } public bool IsPM2Warning => IsPM2InterlockBypassed || IsPM2EnableTableBypassed; public string PM2WarningMessage { get { if(IsPM2InterlockBypassed && IsPM2EnableTableBypassed) return "PM2 Interlock and Enable Table are bypassed"; if(IsPM2InterlockBypassed) return "PM2 Interlock is bypassed"; return "PM2 Enable Table is bypassed"; } } public string UnLoadStatus { get => "NotInstall"; set { } } [Subscription("Buffer.Status")] public string BufferStatus { get; set; } [Subscription("LoadLock.Status")] public string LLStatus { get; set; } [Subscription("TM.Status")] public string TMStatus { get; set; } [Subscription("TM.IsBypassInterlock")] public bool IsTMInterlockBypassed { get; set; } [Subscription("TM.IsBypassEnableTable")] public bool IsTMEanbleTableBypassed { get; set; } public bool IsTMWarning => IsTMInterlockBypassed || IsTMEanbleTableBypassed; public string TMWarningMessage { get { if (IsTMInterlockBypassed && IsTMEanbleTableBypassed) return "TM Interlock and Enable Table are bypassed"; if (IsTMInterlockBypassed) return "TM Interlock is bypassed"; return "TM Enable Table is bypassed"; } } [Subscription("System.IsOnline")] public bool IsOnlineSystem { get; set; } [Subscription("PM1.IsOnline")] public bool IsOnlinePM1 { get; set; } [Subscription("PM2.IsOnline")] public bool IsOnlinePM2 { get; set; } [Subscription("UnLoad.IsOnline")] public bool IsOnlineUnLoad { get; set; } [Subscription("LoadLock.IsOnline")] public bool IsOnlineLL { get; set; } [Subscription("TM.IsOnline")] public bool IsOnlineTM { get; set; } [Subscription("Buffer.IsOnline")] public bool IsOnlineBuffer { get; set; } [Subscription("System.SignalTowerData")] public AITSignalTowerData SignalTowerData { get; set; } [Subscription("PM1.AbortProcessRun")] public bool PM1PlcAbortProcessView { get; set; } [Subscription("PM2.AbortProcessRun")] public bool PM2PlcAbortProcessView { get; set; } public bool IsPlcAbortProcessView => PM1PlcAbortProcessView || PM2PlcAbortProcessView; public string SoftwareVersion { get; set; } public string RunTime => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); [Subscription("System.HasActiveAlarm")] public bool SystemHasAlarm { get; set; } #endregion #region Methods private void Instance_OnDisconnectedWithRT() { MessageBox.Show("Disconnected with RT, UI will exit", "Error", MessageBoxButton.OK, MessageBoxImage.Error); Environment.Exit(0); } private void Instance_OnEvent(EventItem evt) { switch (evt.Type) { case EventType.EventUI_Notify: LogEvent(evt); break; /*case EventType.Dialog_Nofity: Execute.OnUIThread(() => DialogBox.ShowInfo(evt.ToString())); break;*/ case EventType.KickOut_Notify: if (evt.Description == "ShutDown") { AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token); ShutdownThread = ShutdownExecute; ShutdownThread.BeginInvoke(ShutdownCallBack, ShutdownThread); } break; case EventType.Sound_Notify: break; case EventType.UIMessage_Notify: //PopUIMessage(obj); break; } } #region Log public void ShowAlarmEvents() { AllEventsVisibility = Visibility.Hidden; WarnEventsVisibility = Visibility.Visible; NotifyOfPropertyChange(nameof(AllEventsVisibility)); NotifyOfPropertyChange(nameof(WarnEventsVisibility)); } public void ShowAllEvents() { AllEventsVisibility = Visibility.Visible; WarnEventsVisibility = Visibility.Hidden; NotifyOfPropertyChange(nameof(AllEventsVisibility)); NotifyOfPropertyChange(nameof(WarnEventsVisibility)); } private void LogEvent(EventItem obj) { if (obj.Type != EventType.EventUI_Notify) return; EventLogList.Add(obj); } #endregion public void SetModuleOnline(object sender, RoutedEventArgs args) { if (sender is ModuleStatusIndicator indicator) { if (indicator.HasWarning) { DialogBox.ShowError($"Unable to set {indicator.Caption} online since {indicator.WarningTip}"); return; } if (MessageBoxResult.Yes == MessageBox.Show($"Set {indicator.Caption} Online?", "", MessageBoxButton.YesNo, MessageBoxImage.Warning)) { InvokeClient.Instance.Service.DoOperation($"{indicator.ModuleName}.SetOnline"); } } } public void SetModuleOffline(object sender, RoutedEventArgs args) { if (sender is ModuleStatusIndicator indicator) { if (MessageBoxResult.Yes == MessageBox.Show($"Set {indicator.Caption} Offline?", "", MessageBoxButton.YesNo, MessageBoxImage.Warning)) { InvokeClient.Instance.Service.DoOperation($"{indicator.ModuleName}.SetOffline"); } } } public void BuzzerOff() { InvokeClient.Instance.Service.DoOperation($"System.BuzzerOff"); } public void ShutDownPLCRun() { var selection = DialogBox.ShowDialog(DialogButton.Yes | DialogButton.Cancel, DialogType.WARNING, "Are you sure abort PLC process"); if (selection == DialogButton.Cancel) return; if (IsPM1Installed) InvokeClient.Instance.Service.DoOperation("PM1.AbortProcessInterrupt"); if (IsPM2Installed) InvokeClient.Instance.Service.DoOperation("PM2.AbortProcessInterrupt"); } public void PLCProcessBuzzerOFF() { if (IsPM1Installed) InvokeClient.Instance.Service.DoOperation("PM1.AbortProcessBuzzerOFF"); if (IsPM2Installed) InvokeClient.Instance.Service.DoOperation("PM2.AbortProcessBuzzerOFF"); BuzzerOff(); } #region Login/Logout Operations private Task _loginTask; private LoginRequestWaitDialog _loginWaitDialog = null; /// /// 密码输入框中按下Enter键。 /// /// /// /// /// public void PasswordBoxEnterKeyPressed(KeyEventArgs args, string loginName, PasswordBox password, Role role) { if (args.Key == Key.Enter) RequestLogin(loginName, password, role); } /// /// 显示远程登录请求确认对话框。 /// /// 正在请求登录的凭据 private void ShowLoginRequestConfirmDialog(Credential credRequesting) { var dlgConfirm = new LoginRequestConfirmationDialog(credRequesting, _ctsLoginRequestConfirmDialog) { Owner = Application.Current.MainWindow }; var retDlg = dlgConfirm.ShowDialog(); if (retDlg == true) { AccountClient.Instance.Service.ConfirmLoginRequest(credRequesting.AccountInfo.LoginName); var role = BaseApp.Instance.UserContext.Role; Logoff(); // 降级为仅查看模式 IsReadOnlyMode = true; BaseApp.Instance.UserContext.Role = role; BaseApp.Instance.UserContext.Credential = Credential.ReadOnlyOne; } else { AccountClient.Instance.Service.RejectLoginRequest(credRequesting.AccountInfo.LoginName); } } /// /// 显示登录确认等待对话框。 /// /// /// private Task ShowRequestLoginWaiter(CancellationTokenSource cts) { var ct = cts.Token; return Task.Run(() => { /*bool? waitResult = null;*/ // 如果超过一定时间没有从RT返回确认信息,则显示登录中的提示 Thread.Sleep(500); if (ct.IsCancellationRequested) return; Execute.OnUIThread(() => { _loginWaitDialog = new LoginRequestWaitDialog() { Owner = Application.Current.MainWindow }; _loginWaitDialog.ShowDialog(); }); /*while (true) { Thread.Sleep(500); // 等待对话框关闭,不关心返回的DialogResult // 关闭原因: // 1. 点击Cancel按钮 // 2. 超时 // 3. 远端接受或拒绝请求 if (waitResult.HasValue) break; if (ct.IsCancellationRequested) break; } // 强制关闭等待对话框 Execute.OnUIThread(() => { loginWaitDialog?.Close(); });*/ }, ct); } public async void RequestLogin(string loginName, PasswordBox password, Role role) { if (_loginTask is { Status: TaskStatus.Running }) return; #region Validate Parameters if (string.IsNullOrEmpty(loginName)) { DialogBox.ShowError("User Name can not be empty."); return; } if (string.IsNullOrEmpty(password.Password)) { DialogBox.ShowError("Password can not be empty."); return; } if (role == null || string.IsNullOrEmpty(role.RoleId)) { DialogBox.ShowError("Role can not be empty."); return; } #endregion // 向RT请求登录 try { var ctsWaitingTask = new CancellationTokenSource(); var taskWaiting = ShowRequestLoginWaiter(ctsWaitingTask); _loginTask = AccountClient.Instance.Service.LoginEx(loginName, password.Password, role?.RoleId??"", BaseApp.Instance.ClientInfo); // 等待确认超时,或已被接受/拒绝登录 await Task.WhenAny(_loginTask, taskWaiting); if (_loginTask.Status == TaskStatus.RanToCompletion) { // 被接受或拒绝登录 ctsWaitingTask.Cancel(); _loginWaitDialog?.Close(); switch (_loginTask.Result.Result) { case LoginRequestResults.Error: DialogBox.ShowError("Error occurred when login, see log for details."); break; case LoginRequestResults.WrongPwd: DialogBox.ShowError("Invalid password."); break; case LoginRequestResults.NoMatchRole: DialogBox.ShowError("The user does not belong to the role."); break; case LoginRequestResults.NoMatchUser: DialogBox.ShowError("The user is not found."); break; case LoginRequestResults.Rejected: DialogBox.ShowError("Login Request is rejected."); break; case LoginRequestResults.RequstingLogin: DialogBox.ShowError( "The same user is requesting to login from other place, please try again later."); break; case LoginRequestResults.Timeout: DialogBox.ShowError("Timeout to wait login request confirmation."); break; case LoginRequestResults.Confirmed: var cred = _loginTask.Result.Credential; BaseApp.Instance.UserMode = UserMode.Normal; BaseApp.Instance.UserContext.Credential = cred; BaseApp.Instance.UserContext.Role = role; BaseApp.Instance.UserContext.LastAccessTime = DateTime.Now; BaseApp.Instance.UserContext.IsLogin = true; //Load menu by role //filer menu if necessary... BaseApp.Instance.MenuManager.LoadMenu( RoleAccountProvider.Instance.GetAccessibleMenusByRole(role.RoleId)); IsAutoLogout = role.IsAutoLogout; LogoutTime = role.LogoutTime; IsPermission = RoleAccountProvider.Instance.GetMenuPermission(role.RoleId, "Header") == MenuPermissionEnum.MP_READ_WRITE; InitMenu(); //bind menu to main view IsLogin = true; //control the display logic of main view IsReadOnlyMode = false; LOG.Info($"{loginName} login as {role.RoleName}"); break; default: DialogBox.ShowError("Unknown login result, see log for details."); break; } } else if (taskWaiting.Status == TaskStatus.RanToCompletion) { // 等待确认超时 // 注意:该请求在RT的存活时间比UI超时时间长,因此需要显式取消请求,否则下次登录请求需要等CredentialManager超时后才能重新发起 AccountClient.Instance.Service.CancelLoginRequest(loginName); } else { // 两个任务均失败,显式调用下列方法强制RT清理凭据。 AccountClient.Instance.Service.CancelLoginRequest(loginName); } } catch (InvalidOperationException ex) { LOG.Error(ex.Message); } catch (Exception ex) { LOG.Error(ex.Message); } } public void ShowLogoutDialog() { if (!IsReadOnlyMode) { var logoffViewmodel = new LogoffViewModel(); _windowmanager.ShowDialog(logoffViewmodel); BaseApp.Instance.UserMode = logoffViewmodel.DialogResult; switch (logoffViewmodel.DialogResult) { case UserMode.Logoff: Logoff(); break; case UserMode.Exit: AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token); BaseApp.Instance.UserMode = UserMode.Exit; LOG.Info( $"{BaseApp.Instance.UserContext.LoginName} exit as {BaseApp.Instance.UserContext.Role.RoleName}"); TryClose(); break; case UserMode.Shutdown: //配置立即生效。 var roleId = BaseApp.Instance.UserContext.Role.RoleId; var canShutDown = RoleAccountProvider.Instance.GetMenuPermission(roleId, "Operation.Behaviour.ShutDown") == MenuPermissionEnum.MP_READ_WRITE; if (canShutDown) InvokeClient.Instance.Service.DoOperation("System.ShutDown"); else { DialogBox.ShowError("No Permission for ShutDown"); } break; } _eventAggregator.PublishOnUIThread(logoffViewmodel.DialogResult); } else { Logoff(); IsReadOnlyMode = false; } } public void Logoff() { BaseApp.Instance.UserMode = UserMode.Logoff; if (BaseApp.Instance.UserContext.IsLogin) { try { AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.Token); LOG.Info( $"{BaseApp.Instance.UserContext.LoginName} logoff as {BaseApp.Instance.UserContext.Role.RoleName}"); BaseApp.Instance.UserContext.Clear(); ClearItems(); } catch (Exception exp) { LOG.Write(exp); } } IsLogin = false; //no independent login page Roles = RoleAccountProvider.Instance.GetRoles(); } #endregion public void Reset() { InvokeClient.Instance.Service.DoOperation("System.Reset"); //清除GEM报警 InvokeClient.Instance.Service.DoOperation("GEM_ClearAllAlarm"); } #region Overrided Functions public override void CanClose(Action callback) { if (BaseApp.Instance.UserMode == UserMode.Normal) { callback(false); Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)ShowLogoutDialog); } else callback(true); } protected override void OnInitialize() { //display system version or other info... DisplayName = "Sic - GE Manual"; base.OnInitialize(); StartTimer(); ConfigChangeCheck(); //MaintenanceCheck(); if (Debugger.IsAttached) { RequestLogin("admin", new PasswordBox() { Password = "admin" }, new Role("0", "Manager", false, 1000, null)); } InvokeClient.Instance.Service.DoOperation("GEM_SetSoftwareVersion", SoftwareVersion); } private void MaintenanceCheck() { string info = (string)QueryDataClient.Instance.Service.GetData("MaintainManager.MaintenanceCheck"); if (info != "") { DialogBox.ShowInfo("There are UnMaintainItems in Plan\r\n" + info + "please check as soon as possible."); } } private void ConfigChangeCheck() { List s = (List)QueryDataClient.Instance.Service.GetConfig("SystemConfig.DataChangedList"); if (s != null && s.Count > 0) { string tips = ""; foreach (var t in s) { var value = QueryDataClient.Instance.Service.GetConfig(t.Name); if (value != null) { tips += t.Name + (t.Enable ? " " : " HAS ") + "CHANGED:\r\n(Previous)\t" + t.OldValue + " => " + value.ToString() + "\t(Latest)\r\n"; } } if (tips != "") { DialogBox.ShowInfo("There are some changes in SystemConfig,\r\n" + "please make sure those changes are OK:\r\n" + "****************************************************\r\n" + tips); InvokeClient.Instance.Service.DoOperation("SystemConfig.Confirm"); } } } protected override void OnActivate() { base.OnActivate(); ShowAllEvents(); EnableTimer(true); } protected override void OnViewLoaded(object view) { base.OnViewLoaded(view); _view = view as MainView; _view.tbLoginName.Focus(); _view.SplashScreen?.Complete(); } protected override void OnDeactivate(bool close) { base.OnDeactivate(close); EnableTimer(false); } #endregion #region #region Sync ShutDown Thread public delegate void ShutDownSysncThread(); private ShutDownSysncThread ShutdownThread = null; private readonly ShutdownViewModel ShutdownWindow = null; private void ShutdownExecute() { BaseApp.Instance.UserMode = UserMode.Shutdown; BaseApp.Instance.UserContext.IsLogin = false; LOG.Info($"{BaseApp.Instance.UserContext.LoginName} shutdown as {BaseApp.Instance.UserContext.Role.RoleName}"); TryClose(); } private void ShutdownCallBack(IAsyncResult result) { if (ShutdownWindow != null) { ShutdownWindow.TryClose(); } ShutdownThread.EndInvoke(result); } #endregion #region Menu Control and page switch private void InitMenu() { MenuItems = BaseApp.Instance.MenuManager.MenuItems; SubMenuItems = new List(); HistoryMenus = new ObservableCollection(); if (MenuItems.Count > 0) { AppMenu _default = null; foreach (AppMenu menuitem in MenuItems) { if (menuitem.MenuItems.Count > 0) { if (menuitem.AlarmModule == "System") { _alarmMenu = menuitem; break; } if (_default == null) _default = menuitem.MenuItems[0]; } } var pmMenus = MenuItems.Where(x => x.ResKey.StartsWith("PM")).ToList(); if (pmMenus.Any()) { foreach (var menu in pmMenus) { // rename PM menu according to config if (menu.ResKey.StartsWith("PM")) { var name = (string)QueryDataClient.Instance.Service.GetConfig( $"PM.{menu.ResKey}.DisplayName"); if (!string.IsNullOrEmpty(name)) menu.ResKey = name; } } } SwitchMenuItem(_default); } } public void MainSwitchMenuItem(AppMenu menuViewItem) { if (menuViewItem.MenuItems.Count <= 0) return; SwitchMenuItem(menuViewItem.LastSelectedSubMenu ?? menuViewItem.MenuItems[0]); } public void SwitchMenuItem(AppMenu menuViewItem) { if (string.IsNullOrEmpty(menuViewItem.ViewModel)) return; if (menuViewItem.Model == null) { menuViewItem.Model = (BaseModel)AssemblyUtil.CreateInstance(AssemblyUtil.GetType(menuViewItem.ViewModel)); ((BaseModel)menuViewItem.Model).Permission = menuViewItem.Permission; // Change display name to the one set in config if it's PM.Main var displayName = menuViewItem.ResKey; if (menuViewItem.ResKey.ToLower() == "main" && menuViewItem.System.StartsWith("PM")) { var name = (string)QueryDataClient.Instance.Service.GetConfig( $"PM.{menuViewItem.System}.DisplayName"); if (!string.IsNullOrEmpty(name)) displayName = name; } ((BaseModel)menuViewItem.Model).DisplayName = displayName; //将名称传入viewmodel中 if (menuViewItem.Model is ISupportMultipleSystem smsViewModel) smsViewModel.SystemName = menuViewItem.System; } ActivateItem(((BaseModel)menuViewItem.Model)); CurrentViewModel = ((BaseModel)menuViewItem.Model); //if (((BaseModel)menuViewItem.Model).Page != PageID.MAX_PAGE) // BaseApp.Instance.SetCurrentPage(((BaseModel)menuViewItem.Model).Page); HandleSubAndHistoryMenu(menuViewItem); if (_currentMenuItem != null) { _currentMenuItem.Selected = false; _currentMenuItem.Parent.Selected = false; } menuViewItem.Selected = true; menuViewItem.Parent.Selected = true; menuViewItem.Parent.LastSelectedSubMenu = menuViewItem; _currentMenuItem = menuViewItem; #region Change Background if (!string.IsNullOrEmpty(_currentMenuItem.System) && (_currentMenuItem.System.StartsWith("PM1") || _currentMenuItem.System.StartsWith("PM2"))) { var scPath = $"PM.{_currentMenuItem.System}.BackgroundColor"; var scConfig = QueryDataClient.Instance.Service.GetConfig(scPath); var hexColor = scConfig.ToString(); try { if (string.IsNullOrEmpty(hexColor)) { LOG.Error($"Unable to find value of config item {scPath}"); hexColor = Colors.Transparent.ToString(); } var color = (Color)ColorConverter.ConvertFromString(hexColor); UpdatePMPageBackground(color); UpdateMainMenuBackground(); } catch (Exception ex) { LOG.Error($"Unable to set active content background, {ex.Message}", ex); UpdatePMPageBackground(Colors.Transparent); UpdateMainMenuBackground(); } } else { UpdatePMPageBackground(Colors.Transparent); UpdateMainMenuBackground(); } #endregion } private void HandleSubAndHistoryMenu(AppMenu menuitem) { SubMenuItems = menuitem.Parent.MenuItems; if (!HistoryMenus.Contains(menuitem)) { if (HistoryMenus.Count >= 8) HistoryMenus.RemoveAt(7); HistoryMenus.Insert(0, menuitem); } else { HistoryMenus.Remove(menuitem); HistoryMenus.Insert(0, menuitem); } } public bool SwitchPage(string firstLevelMenuId, string secondLevelMenuId) { foreach (var menuitem in BaseApp.Instance.MenuManager.MenuItems) { if (menuitem.MenuID == firstLevelMenuId) { foreach (var menu in menuitem.MenuItems) { if (menu.MenuID == secondLevelMenuId) { SwitchMenuItem(menu); return true; } } } } return false; } #endregion #region Refresh Date Time on page protected override void InvokeAfterUpdateProperty(Dictionary data) { if (_alarmMenu != null) _alarmMenu.IsAlarm = SystemHasAlarm; } protected override bool OnTimer() { try { base.Poll(); var roles = RoleAccountProvider.Instance.GetRoles(); if (!Credential.IsEmpty(BaseApp.Instance.UserContext.Credential)) { var role = roles.Find(x => x.RoleName == BaseApp.Instance.UserContext.Role.RoleName); LogoutTime = role.LogoutTime; IsAutoLogout = role.IsAutoLogout; var idleDuration = GetLastInputTime(); if (idleDuration >= LogoutTime * 60 && IsLogin && IsAutoLogout) Logoff(); else { // keep credential alive var retKeepAlive = AccountClient.Instance.Service.KeepAlive(BaseApp.Instance.UserContext.Token); switch (retKeepAlive.State) { case CredentialKeepAliveStates.Alive or CredentialKeepAliveStates.NotFound: { if (_ctsLoginRequestConfirmDialog is { IsCancellationRequested: false }) _ctsLoginRequestConfirmDialog.Cancel(); if (retKeepAlive.State == CredentialKeepAliveStates.NotFound) { Logoff(); Execute.OnUIThread(() => { DialogBox.ShowError("You are kicked by RT."); }); } break; } case CredentialKeepAliveStates.RequestingLogin: { // 当前用户在其它地方请求登录 if (_ctsLoginRequestConfirmDialog == null || _ctsLoginRequestConfirmDialog.IsCancellationRequested) { _ctsLoginRequestConfirmDialog = new CancellationTokenSource(); _prgShowLoginRequestConfirmDialog.Report(retKeepAlive.RequestingCredential); } /* Execute.OnUIThread(() => { var dlgConfirm = new LoginRequestConfirmationDialog(retKeepAlive.RequestingCredential); var retDlg = dlgConfirm.ShowDialog(); if (retDlg == true) { AccountClient.Instance.Service.ConfirmLoginRequest(retKeepAlive.RequestingCredential.AccountInfo.LoginName); Logoff(); // 降级为仅查看模式 IsReadOnlyMode = true; BaseApp.Instance.UserContext.Role = role; BaseApp.Instance.UserContext.Credential = Credential.ReadOnlyOne; } else { AccountClient.Instance.Service.RejectLoginRequest(retKeepAlive.RequestingCredential.AccountInfo.LoginName); } });*/ // 等待RT处理凭据,否则有可能反复触发请求登录确认窗口. Thread.Sleep(1000); break; } } } } } catch (Exception ex) { LOG.Error(ex.Message); } return true; } private void StartTimer() { DispatcherTimer myDispatcherTimer = new DispatcherTimer(); myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000); myDispatcherTimer.Tick += Each_Tick; myDispatcherTimer.Start(); } public void Each_Tick(object o, EventArgs sender) { NowDateTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); NotifyOfPropertyChange(nameof(NowDateTime)); } #endregion #endregion #endregion #region Change PM Background private Brush _bgActiveContent; public Brush BgActiveContent { get => _bgActiveContent; set { _bgActiveContent = value; NotifyOfPropertyChange(); } } public void UpdatePMPageBackground(Color color) { var DEF_COLOR = Colors.LightBlue; if (color == Colors.Transparent) { try { if (Application.Current != null && Application.Current.Resources.Contains("MainArea_BG")) { var res = Application.Current.Resources["MainArea_BG"] as Brush; if (res != null) { // Clone to avoid modifying the shared resource if it's frozen var clone = res.CloneCurrentValue(); BgActiveContent = clone; return; } } } catch { } BgActiveContent = new SolidColorBrush(DEF_COLOR); return; } // Build a vertical gradient based on the provided color // Apply alpha into RGB channels, produce an opaque color (alpha applied to channels) var alphaByte = (byte)50; var factor = alphaByte / 255.0; var blendedR = (byte)Math.Round(DEF_COLOR.R * (1.0 - factor) + color.R * factor); var blendedG = (byte)Math.Round(DEF_COLOR.G * (1.0 - factor) + color.G * factor); var blendedB = (byte)Math.Round(DEF_COLOR.B * (1.0 - factor) + color.B * factor); // make opaque baseColor after blending var baseColor = Color.FromArgb(255, blendedR, blendedG, blendedB); // lighter stop (towards white) var lighter = Color.FromArgb( 255, (byte)Math.Min(255, baseColor.R + (255 - baseColor.R) / 4), (byte)Math.Min(255, baseColor.G + (255 - baseColor.G) / 4), (byte)Math.Min(255, baseColor.B + (255 - baseColor.B) / 4)); // slightly darker stop var darker = Color.FromArgb( 255, (byte)Math.Max(0, (int)(baseColor.R * 0.75)), (byte)Math.Max(0, (int)(baseColor.G * 0.75)), (byte)Math.Max(0, (int)(baseColor.B * 0.75))); var brush = new LinearGradientBrush { StartPoint = new Point(0, 0), EndPoint = new Point(0, 1) }; brush.GradientStops.Add(new GradientStop(lighter, 0.0)); brush.GradientStops.Add(new GradientStop(baseColor, 0.5)); brush.GradientStops.Add(new GradientStop(darker, 1.0)); // Freeze for performance if (brush.CanFreeze) brush.Freeze(); BgActiveContent = brush; } private void UpdateMainMenuBackground() { if (_view == null || _currentMenuItem == null) return; // Ensure UI work happens on UI thread Application.Current?.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)(() => { // Try direct FindName first (faster when available) Menu mainMenu = null; try { if (_view is FrameworkElement fe) mainMenu = fe.FindName("MainMenu") as Menu; } catch { /* ignore */ } // Fallback to visual search if FindName didn't find it if (mainMenu == null) mainMenu = FindVisualChildByName(_view, "MainMenu"); if (mainMenu == null) return; // Iterate items and use ItemContainerGenerator to get the MenuItem container foreach (var item in mainMenu.Items) { var container = mainMenu.ItemContainerGenerator.ContainerFromItem(item) as MenuItem; if (container == null) continue; if (container.DataContext is AppMenu menuInfo) { if (menuInfo.System is "PM1" or "PM2") { var rect = FindVisualChildByName(container, "InnerBG"); rect.Fill = menuInfo.Selected ? BgActiveContent : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294")); var bgBorder = FindVisualChildByName(container, "BG"); bgBorder.Background = menuInfo.Selected ? BgActiveContent : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294")); } } } })); } private static IEnumerable FindVisualChildren(DependencyObject depObj) where T : DependencyObject { if (depObj == null) yield break; for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { var child = VisualTreeHelper.GetChild(depObj, i); if (child is T t) yield return t; foreach (var childOfChild in FindVisualChildren(child)) yield return childOfChild; } } private static T FindVisualChildByName(DependencyObject depObj, string name) where T : FrameworkElement { if (depObj == null) return null; for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++) { var child = VisualTreeHelper.GetChild(depObj, i); if (child is T fe && fe.Name == name) return fe; var result = FindVisualChildByName(child, name); if (result != null) return result; } return null; } #endregion } }