SIC-12/SicUI/MainViewModel.cs
2026-03-24 15:04:02 +08:00

1429 lines
50 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<AppMenu> _menuItems;
private List<AppMenu> _subMenuItems;
private ObservableCollection<AppMenu> _historyItems;
private Dictionary<Type, BaseModel> _models;
private string _context;
private bool _isReadOnlyMode;
private readonly IEventAggregator _eventAggregator;
private readonly IProgress<Credential> _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<Type, BaseModel>();
_eventAggregator = eventAggregator;
//for login part
Roles = RoleAccountProvider.Instance.GetRoles();
EventLogList = new DelayedPresentRollingObservableCollection<EventItem>(1000);
EventLogsView = CollectionViewSource.GetDefaultView(EventLogList);
//WarnEventLogList = new ObservableCollection<EventItem>();
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<Credential>(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<Role> _roles;
public List<Role> Roles
{
get => _roles;
set { _roles = value; RaisePropertyChangedEventImmediately("Roles"); }
}
private ICommand _menuItemClickCommand;
public ICommand MenuItemClickCommand
{
get
{
if (_menuItemClickCommand == null)
_menuItemClickCommand = new BaseCommand<AppMenu>(SwitchMenuItem);
return _menuItemClickCommand;
}
}
private ICommand _mainMenuItemClickCommand;
public ICommand MainMenuItemClickCommand
{
get
{
if (_mainMenuItemClickCommand == null)
_mainMenuItemClickCommand = new BaseCommand<AppMenu>(MainSwitchMenuItem);
return _mainMenuItemClickCommand;
}
}
public List<AppMenu> MenuItems
{
get => _menuItems;
set { _menuItems = value; NotifyOfPropertyChange(); }
}
public List<AppMenu> SubMenuItems
{
get => _subMenuItems;
set { _subMenuItems = value; NotifyOfPropertyChange(); }
}
public ObservableCollection<AppMenu> 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<EventItem> WarnEventLogList { get; set; }
private DelayedPresentRollingObservableCollection<EventItem> EventLogList { get; }
/// <summary>
/// 用于在主界面显示Event Log的视图。
/// 通过该视图筛选IsAlarm条目。
/// </summary>
public ICollectionView EventLogsView { get; }
private bool _isShowAlarmEventOnly;
/// <summary>
/// IsAlarm CheckBox绑定到这里直接从<see cref="EventLogsView"/>中过滤所需的数据。
/// </summary>
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<LoginResult> _loginTask;
private LoginRequestWaitDialog _loginWaitDialog = null;
/// <summary>
/// 密码输入框中按下Enter键。
/// </summary>
/// <param name="args"></param>
/// <param name="loginName"></param>
/// <param name="password"></param>
/// <param name="role"></param>
public void PasswordBoxEnterKeyPressed(KeyEventArgs args, string loginName, PasswordBox password, Role role)
{
if (args.Key == Key.Enter)
RequestLogin(loginName, password, role);
}
/// <summary>
/// 显示远程登录请求确认对话框。
/// </summary>
/// <param name="credRequesting">正在请求登录的凭据</param>
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);
}
}
/// <summary>
/// 显示登录确认等待对话框。
/// </summary>
/// <param name="cts"></param>
/// <returns></returns>
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<bool> 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<ConfigChangedInfo> s = (List<ConfigChangedInfo>)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<AppMenu>();
HistoryMenus = new ObservableCollection<AppMenu>();
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<string, object> 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<Menu>(_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<Rectangle>(container, "InnerBG");
rect.Fill = menuInfo.Selected
? BgActiveContent
: new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294"));
var bgBorder = FindVisualChildByName<Border>(container, "BG");
bgBorder.Background = menuInfo.Selected
? BgActiveContent
: new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294"));
}
}
}
}));
}
private static IEnumerable<T> FindVisualChildren<T>(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<T>(child))
yield return childOfChild;
}
}
private static T FindVisualChildByName<T>(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<T>(child, name);
if (result != null)
return result;
}
return null;
}
#endregion
}
}