Sic.Framework/UIDebug/SicUI/MainViewModel.cs

802 lines
26 KiB
C#
Raw 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.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<Cali.Screen>.Collection.OneActive
{
private readonly PeriodicJob _timer;
private readonly ConcurrentBag<string> _subscribedKeys = new ConcurrentBag<string>();
private readonly Func<object, bool> _isSubscriptionAttribute;
private readonly Func<MemberInfo, bool> _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);
/// <summary>
/// 获取鼠标键盘不活动的时间
/// </summary>
/// <returns>结果</returns>
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<string, object> 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<string, object> data)
{
}
protected virtual void InvokeAfterUpdateProperty(Dictionary<string, object> data)
{
}
private void UpdateValue(Dictionary<string, object> data)
{
if (data == null)
return;
UpdateSubscribe(data, this);
var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<SubscriptionModuleAttribute>() != null);
foreach (var property in properties)
{
var moduleAttr = property.GetCustomAttribute<SubscriptionModuleAttribute>();
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<string, object> 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<Role> _roles;
public List<Role> Roles
{
get => _roles;
set { _roles = value; RaisePropertyChangedEventImmediately(nameof(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 BaseModel CurrentViewModel { get; private set; }
public UserContext User => BaseApp.Instance.UserContext;
private AppMenu _currentMenuItem;
private List<AppMenu> _menuItems;
private List<AppMenu> _subMenuItems;
private ObservableCollection<AppMenu> _historyItems;
private string _context;
private MainView _view;
private Dictionary<Type, BaseModel> _models;
#endregion
public bool IsAutoLogout { get; set; }
public int LogoutTime { get; set; }
//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; }
public string SoftwareVersion
{
get;
set;
}
private AppMenu _alarmMenu;
public MainViewModel()
{
BaseApp.Instance.Initialize();
((ClientApp)BaseApp.Instance).ViewModelSwitcher = this;
_models = new Dictionary<Type, BaseModel>();
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<bool> 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<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 == 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<string, object> 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
}
}