using Aitex.Core.Common.DeviceData; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.Util; using Caliburn.Micro; using MECF.Framework.Common.Account.Extends; using MECF.Framework.Common.DataCenter; using MECF.Framework.Common.OperationCenter; using MECF.Framework.UI.Client.CenterViews.LogOnOff; using MECF.Framework.UI.Client.ClientBase; using MECF.Framework.UI.Core.Accounts; using OpenSEMI.ClientBase.Command; using OpenSEMI.ClientBase.Utility; using SciChart.Charting.ChartModifiers; using SciChart.Charting.Visuals; using SciChart.Charting.Visuals.Annotations; using SciChart.Charting.Visuals.Axes; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Input; using System.Windows.Threading; using Sicentury.Core.Collections; using Cali = Caliburn.Micro.Core; namespace SicUI.Client { public class TimeredMainViewModel : Cali.Conductor.Collection.OneActive { private readonly PeriodicJob _timer; private readonly ConcurrentBag _subscribedKeys = new ConcurrentBag(); private readonly Func _isSubscriptionAttribute; private readonly Func _hasSubscriptionAttribute; public TimeredMainViewModel() { _timer = new PeriodicJob(1000, OnTimer, "UIUpdaterThread - " + GetType().Name); _isSubscriptionAttribute = attribute => attribute is SubscriptionAttribute; _hasSubscriptionAttribute = mi => mi.GetCustomAttributes(false).Any(_isSubscriptionAttribute); SubscribeKeys(this); } [StructLayout(LayoutKind.Sequential)] internal struct LASTINPUTINFO { [MarshalAs(UnmanagedType.U4)] public int cbSize; [MarshalAs(UnmanagedType.U4)] public int dwTime; } [DllImport("user32.dll")] internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); /// /// 获取鼠标键盘不活动的时间 /// /// 结果 public static int GetLastInputTime() { var lastInputInfo = new LASTINPUTINFO(); lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo); lastInputInfo.dwTime = 0; var idleTime = 0; if (GetLastInputInfo(ref lastInputInfo)) { idleTime = Environment.TickCount - lastInputInfo.dwTime; } return ((idleTime > 0) ? (idleTime / 1000) : 0); } protected virtual bool OnTimer() { try { Poll(); } catch (Exception ex) { LOG.Error(ex.Message); } return true; } public virtual void EnableTimer(bool enable) { if (enable) _timer.Start(); else _timer.Pause(); } protected virtual void Poll() { if (_subscribedKeys.Count > 0) { Dictionary result = QueryDataClient.Instance.Service.PollData(_subscribedKeys); if (result == null) { LOG.Error("获取RT数据失败"); return; } if (result.Count != _subscribedKeys.Count) { string unknowKeys = string.Empty; foreach (string key in _subscribedKeys) { if (!result.ContainsKey(key)) { unknowKeys += key + "\r\n"; } } //System.Diagnostics.Debug.Assert(false, unknowKeys); } InvokeBeforeUpdateProperty(result); UpdateValue(result); Application.Current.Dispatcher.Invoke(new System.Action(() => { InvokePropertyChanged(); InvokeAfterUpdateProperty(result); })); } } private void InvokePropertyChanged() { Refresh(); } protected virtual void InvokeBeforeUpdateProperty(Dictionary data) { } protected virtual void InvokeAfterUpdateProperty(Dictionary data) { } private void UpdateValue(Dictionary data) { if (data == null) return; UpdateSubscribe(data, this); var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute() != null); foreach (var property in properties) { var moduleAttr = property.GetCustomAttribute(); UpdateSubscribe(data, property.GetValue(this), moduleAttr.Module); } } protected void Subscribe(string key) { if (!string.IsNullOrEmpty(key)) { _subscribedKeys.Add(key); } } public void SubscribeKeys(TimeredMainViewModel target) { Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute), property => { SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute; string key = subscription.ModuleKey; if (!_subscribedKeys.Contains(key)) _subscribedKeys.Add(key); }); Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute), method => { SubscriptionAttribute subscription = method.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute; string key = subscription.ModuleKey; if (!_subscribedKeys.Contains(key)) _subscribedKeys.Add(key); }); } public void UpdateSubscribe(Dictionary data, object target, string module = null) { Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute), property => { PropertyInfo pi = (PropertyInfo)property; SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute; string key = subscription.ModuleKey; key = module == null ? key : string.Format("{0}.{1}", module, key); if (_subscribedKeys.Contains(key) && data.ContainsKey(key)) { try { var convertedValue = Convert.ChangeType(data[key], pi.PropertyType); var originValue = Convert.ChangeType(pi.GetValue(target, null), pi.PropertyType); if (originValue != convertedValue) { if (pi.Name == "PumpLimitSetPoint") pi.SetValue(target, convertedValue, null); else pi.SetValue(target, convertedValue, null); } } catch (Exception ex) { LOG.Error("由RT返回的数据更新失败" + key, ex); } } }); Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute), property => { FieldInfo pi = (FieldInfo)property; SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute; string key = subscription.ModuleKey; if (_subscribedKeys.Contains(key) && data.ContainsKey(key)) { try { var convertedValue = Convert.ChangeType(data[key], pi.FieldType); pi.SetValue(target, convertedValue); } catch (Exception ex) { LOG.Error("由RT返回的数据更新失败" + key, ex); } } }); } } public class MainViewModel : TimeredMainViewModel { #region Menus 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(nameof(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 BaseModel CurrentViewModel { get; private set; } public UserContext User => BaseApp.Instance.UserContext; private AppMenu _currentMenuItem; private List _menuItems; private List _subMenuItems; private ObservableCollection _historyItems; private string _context; private MainView _view; private Dictionary _models; #endregion public bool IsAutoLogout { get; set; } public int LogoutTime { get; set; } //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; } public string SoftwareVersion { get; set; } private AppMenu _alarmMenu; public MainViewModel() { BaseApp.Instance.Initialize(); ((ClientApp)BaseApp.Instance).ViewModelSwitcher = this; _models = new Dictionary(); SoftwareVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); /*EventClient.Instance.OnEvent += Instance_OnEvent; EventClient.Instance.OnDisconnectedWithRT += Instance_OnDisconnectedWithRT; EventClient.Instance.Start();*/ } private void Instance_OnDisconnectedWithRT() { MessageBox.Show("Disconnected with RT, UI will exit", "Error", MessageBoxButton.OK, MessageBoxImage.Error); Environment.Exit(0); } 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 Instance_OnEvent(EventItem obj) { switch (obj.Type) { case EventType.EventUI_Notify: LogEvent(obj); break; case EventType.Dialog_Nofity: //PopDialog(obj); break; case EventType.KickOut_Notify: if (obj.Description == "ShutDown") { AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.LoginName, BaseApp.Instance.UserContext.LoginId); _shutdownThread = ShutdownExecute; _shutdownThread.BeginInvoke(ShutdownCallBack, _shutdownThread); } break; case EventType.Sound_Notify: break; case EventType.UIMessage_Notify: //PopUIMessage(obj); break; } } private void LogEvent(EventItem obj) { if (obj.Type != EventType.EventUI_Notify) return; EventLogList.Add(obj); } public void SetModuleOnline(string module) { if (MessageBoxResult.Yes == MessageBox.Show($"Set {module} Online ?", "", MessageBoxButton.YesNo, MessageBoxImage.Warning)) { InvokeClient.Instance.Service.DoOperation($"{module}.SetOnline"); } } public void SetModuleOffline(string module) { if (MessageBoxResult.Yes == MessageBox.Show($"Set {module} Offline ?", "", MessageBoxButton.YesNo, MessageBoxImage.Warning)) { InvokeClient.Instance.Service.DoOperation($"{module}.SetOffline"); } } public void Logout() { OnLogoutCommand(); } public void OnLogoutCommand() { WindowManager windowmanager = new WindowManager(); 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.LoginName, BaseApp.Instance.UserContext.LoginId); BaseApp.Instance.UserMode = UserMode.Exit; LOG.Info(string.Format("{0} exit as {1}", BaseApp.Instance.UserContext.LoginName, BaseApp.Instance.UserContext.RoleName)); TryClose(); break; case UserMode.Shutdown: InvokeClient.Instance.Service.DoOperation("System.ShutDown"); break; } } public void Logoff() { BaseApp.Instance.UserMode = UserMode.Logoff; if (BaseApp.Instance.UserContext.IsLogin) { try { AccountClient.Instance.Service.LogoutEx(BaseApp.Instance.UserContext.LoginName, BaseApp.Instance.UserContext.LoginId); BaseApp.Instance.UserContext.IsLogin = false; LOG.Info(string.Format("{0} logoff as {1}", BaseApp.Instance.UserContext.LoginName, BaseApp.Instance.UserContext.RoleName)); } catch (Exception exp) { LOG.Write(exp); } } IsLogin = false; //no independent login page Roles = RoleAccountProvider.Instance.GetRoles(); } public void Reset() { InvokeClient.Instance.Service.DoOperation("System.Reset"); } public void BuzzerOff() { InvokeClient.Instance.Service.DoOperation($"PM1.SignalTower.{AITSignalTowerOperation.SwitchOffBuzzer}",true); } #region override functions public override void CanClose(Action callback) { if (BaseApp.Instance.UserMode == UserMode.Normal) { callback(false); Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (ThreadStart)delegate { OnLogoutCommand(); }); } else callback(true); } protected override void OnInitialize() { //display system version or other info... DisplayName = "Sic"; base.OnInitialize(); StartTimer(); } protected override void OnActivate() { base.OnActivate(); ShowAllEvents(); EnableTimer(true); } private void DrawSciChart() { // Create the chart surface var sciChartSurface = new SciChartSurface(); // Create the X and Y Axis var xAxis = new NumericAxis() { AxisTitle = "Number of Samples (per series)" }; var yAxis = new NumericAxis() { AxisTitle = "Value" }; sciChartSurface.XAxis = xAxis; sciChartSurface.YAxis = yAxis; // Specify Interactivity Modifiers sciChartSurface.ChartModifier = new ModifierGroup(new RubberBandXyZoomModifier(), new ZoomExtentsModifier()); // Add annotation hints to the user var textAnnotation = new TextAnnotation() { Text = "Hello World!", X1 = 5.0, Y1 = 5.0 }; sciChartSurface.Annotations.Add(textAnnotation); } 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 ShutdownViewModel _shutdownWindow = null; private void ShutdownExecute() { BaseApp.Instance.UserMode = UserMode.Shutdown; BaseApp.Instance.UserContext.IsLogin = false; LOG.Info(string.Format("{0} shutdown as {1}", BaseApp.Instance.UserContext.LoginName, BaseApp.Instance.UserContext.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 == nameof(System)) { _alarmMenu = menuitem; break; } if (@default == null) @default = menuitem.MenuItems[0]; } } SwitchMenuItem(@default); } } public void MainSwitchMenuItem(AppMenu menuViewItem) { if (menuViewItem.MenuItems.Count > 0) { if (menuViewItem.LastSelectedSubMenu != null) SwitchMenuItem(menuViewItem.LastSelectedSubMenu); else SwitchMenuItem(menuViewItem.MenuItems[0]); } } public void SwitchMenuItem(AppMenu menuViewItem) { if (menuViewItem.ViewModel != null && menuViewItem.ViewModel != string.Empty) { if (menuViewItem.Model == null) { menuViewItem.Model = (BaseModel)AssemblyUtil.CreateInstance(AssemblyUtil.GetType(menuViewItem.ViewModel)); ((BaseModel)menuViewItem.Model).Permission = menuViewItem.Permission; ((BaseModel)menuViewItem.Model).Token = BaseApp.Instance.UserContext.Token; if (menuViewItem.Model is ISupportMultipleSystem) (menuViewItem.Model as ISupportMultipleSystem).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; } } 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 (AppMenu menuitem in BaseApp.Instance.MenuManager.MenuItems) { if (menuitem.MenuID == firstLevelMenuId) { foreach (AppMenu 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) { } protected override bool OnTimer() { try { base.Poll(); } catch (Exception ex) { LOG.Error(ex.Message); } return true; } private void StartTimer() { var myDispatcherTimer = new DispatcherTimer(); myDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1000); myDispatcherTimer.Tick += new EventHandler(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 } }