2026-03-24 15:04:02 +08:00
|
|
|
|
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);
|
2026-06-05 09:59:58 +08:00
|
|
|
|
if (scConfig == null) return;
|
2026-03-24 15:04:02 +08:00
|
|
|
|
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");
|
2026-06-08 16:56:30 +08:00
|
|
|
|
rect.Fill = menuInfo.Selected
|
2026-03-24 15:04:02 +08:00
|
|
|
|
? BgActiveContent
|
|
|
|
|
|
: new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FF346294"));
|
|
|
|
|
|
|
|
|
|
|
|
var bgBorder = FindVisualChildByName<Border>(container, "BG");
|
2026-06-08 16:56:30 +08:00
|
|
|
|
bgBorder.Background = menuInfo.Selected
|
2026-03-24 15:04:02 +08:00
|
|
|
|
? 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
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|