更新多用户登录机制:

同时只允许一个用户登录操作A;
多个用户B登录时,有权限用户A将进行确认,若确认后操作权限转移到B,A变成ReadOnly模式,仅有只读权限;
目前ReadOnly模式禁用鼠标键盘功能,只能切换页面,无法操控页面内功能(滚动条、Log等无法点击查看)
This commit is contained in:
SIC1016\caipeilun 2023-10-06 09:50:11 +08:00
parent b770eefdaf
commit fce4967a57
31 changed files with 1037 additions and 648 deletions

View File

@ -3,12 +3,10 @@
<TableRecipeFormat RecipeChamberType="Sic" RecipeVersion="1.0">
<Catalog DisplayName="Operation">
<Group DisplayName="Overview">
<Content DisplayName="Operation" Default="false" />
<Content DisplayName="RunningMode" Default="false" />
<Content DisplayName="ProcessMonitorWindow" Default="true" />
<Content DisplayName="ProcessMonitorWindow" Default="true" Description="是否允许打开工艺实时数据监控窗口"/>
</Group>
<Group DisplayName="Behaviour">
<Content DisplayName="ShutDown" Default="false" />
<Content DisplayName="ShutDown" Default="false" Description="是否允许关闭系统"/>
</Group>
</Catalog>
@ -34,11 +32,10 @@
<Catalog DisplayName="Recipe">
<Group DisplayName="Behaviour">
<Content DisplayName="AllowEditCellAccessPerm" Default="false" />
<Content DisplayName="ShowValuesInRecipeEditor" Default="false" />
<Content DisplayName="ShowValueInProcessView" Default="false" />
<Content DisplayName="AllowSaveInProcessView" Default="false" />
<Content DisplayName="AllowRipInProcessView" Default="false" />
<Content DisplayName="AllowEditCellAccessPerm" Default="false" Description="是否启用单元格权限编辑功能选中的参数将被加入白名单始终在Process视图的Recipe表格中显示其参数值"/>
<Content DisplayName="AllowRipInProcessView" Default="false" Description="是否允许工艺中更新Recipe功能" />
<Content DisplayName="ShowValueInProcessView" Default="false" Description="是否允许Process视图中显示Recipe参数如果为False则白名单以外的参数显示为*’号" />
<Content DisplayName="AllowSaveInProcessView" Default="false" Description="是否允许Process视图中保存Recipe" />
</Group>
</Catalog>

View File

@ -478,8 +478,8 @@ namespace SicModules.PMs
DATA.Subscribe($"{Module}.Status", () => StringFsmStatus.Replace("0", "").Replace("1", "").Replace("2", ""));
DATA.Subscribe($"{Module}.IsOnline", () => IsOnline);
DATA.Subscribe($"{Module}.IsService", () => IsServiceIdle);
DATA.Subscribe($"{Module}.DI206EqualsTrue_OR_DO177EqualsTrue", () => DI206EqualsTrue_OR_DO177EqualsTrue);
DATA.Subscribe($"{Module}.DI206EqualsTrue_OR_DO177EqualsTrue", () => DI206EqualsTrue_OR_DO177EqualsTrue, SubscriptionAttribute.FLAG.IgnoreSaveDB);
DATA.Subscribe($"{Module}.IsAlarm", () => IsAlarm);
DATA.Subscribe($"{Module}.IsWarning", () => IsWarning);
DATA.Subscribe($"{Module}.IsIdle", () => !IsWarning && (IsIdle || IsInit || IsSafety));

View File

@ -38,9 +38,11 @@
<appender-ref ref="txtFileAppender"/>
</root>
</log4net>
<system.diagnostics>
<!--<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Information,ActivityTracing" propagateActivity="true">
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
@ -56,7 +58,7 @@
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
<!--<system.diagnostics>
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging">
<listeners>
@ -68,9 +70,6 @@
</sources>
</system.diagnostics>-->
<system.serviceModel>
<!--<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="false" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false" maxMessagesToLog="3000" maxSizeOfMessageToLog="2000" />
</diagnostics>-->
<bindings>
<netTcpBinding>
<binding name="Aitex_netTcpBinding" maxReceivedMessageSize="102400000" receiveTimeout="infinite">
@ -101,7 +100,12 @@
</services>
<client>
<endpoint address="net.tcp://localhost:6701/SimulatorAdsPlcService" behaviorConfiguration="EndpointBehavior" binding="netTcpBinding" bindingConfiguration="Aitex_netTcpBinding" contract="MECF.Framework.Common.PLC.IWcfPlcService" name="Client_IWcfPlcService"/>
<endpoint address="net.tcp://localhost:6701/SimulatorAdsPlcService"
behaviorConfiguration="EndpointBehavior"
binding="netTcpBinding"
bindingConfiguration="Aitex_netTcpBinding"
contract="MECF.Framework.Common.PLC.IWcfPlcService"
name="Client_IWcfPlcService"/>
</client>
<behaviors>
<serviceBehaviors>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<roles>
<roleItem id="0" name="Manager" autologout="1" logouttime="20">
<roleItem id="0" name="Manager" autologout="1" logouttime="20" buildin="true">
Header,3;Overview,3;TM,3;EFEM,3;Device,3;ioPlatform,3;LeakCheck,3;Sequence,3;Recipe,3;EventLog,3;RealTimeCharting,3;WaferHistory,3;DataHistory,3;
Charting,3;Config,3;Account,3;Role,3;Runtime,3;Alarm,3;mainPM1,3;processPM1,3;ioPM1,3;motionPM1,3;heaterPM1,3;alarmPM1,3;MFCFlowPM1,3;mainPM2,3;
processPM2,3;ioPM2,3;motionPM2,3;heaterPM2,3;alarmPM2,3;MFCFlowPM2,3;
@ -26,7 +26,7 @@
Step33,3;Step34,3;Step35,3;Step36,3;Step37,3;Step38,3;Step39,3;Step40,3;Step41,3;Step42,3;Step43,3;Step44,3;Step45,3;Step46,3;Step47,3;Step48,3;Step49,3;
Step50,3;
</roleItem>
<roleItem id="1" name="Engineer" autologout="1" logouttime="20">
<roleItem id="1" name="Engineer" autologout="1" logouttime="20" buildin="true">
Header,3;Overview,3;TM,3;EFEM,3;Device,3;ioPlatform,3;LeakCheck,3;Sequence,3;Recipe,3;EventLog,3;RealTimeCharting,3;WaferHistory,3;DataHistory,3;
Charting,3;Config,3;Account,3;Role,3;Runtime,3;Alarm,3;mainPM1,3;processPM1,3;ioPM1,3;motionPM1,3;heaterPM1,3;alarmPM1,3;MFCFlowPM1,3;mainPM2,3;
processPM2,3;ioPM2,3;motionPM2,3;heaterPM2,3;alarmPM2,3;MFCFlowPM2,3;
@ -51,7 +51,7 @@
Step33,3;Step34,3;Step35,3;Step36,3;Step37,3;Step38,3;Step39,3;Step40,3;Step41,3;Step42,3;Step43,3;Step44,3;Step45,3;Step46,3;Step47,3;Step48,3;Step49,3;
Step50,3;
</roleItem>
<roleItem id="2" name="Technician" autologout="1" logouttime="20">
<roleItem id="2" name="Technician" autologout="1" logouttime="20" buildin="true">
Header,3;Overview,3;TM,3;EFEM,3;Device,3;ioPlatform,3;LeakCheck,3;Sequence,3;Recipe,3;EventLog,3;RealTimeCharting,3;WaferHistory,3;DataHistory,3;
Charting,3;Config,3;Account,3;Role,3;Runtime,3;Alarm,3;mainPM1,3;processPM1,3;ioPM1,3;motionPM1,3;heaterPM1,3;alarmPM1,3;MFCFlowPM1,3;mainPM2,3;
processPM2,3;ioPM2,3;motionPM2,3;heaterPM2,3;alarmPM2,3;MFCFlowPM2,3;
@ -76,7 +76,7 @@
Step33,3;Step34,3;Step35,3;Step36,3;Step37,3;Step38,3;Step39,3;Step40,3;Step41,3;Step42,3;Step43,3;Step44,3;Step45,3;Step46,3;Step47,3;Step48,3;Step49,3;
Step50,3;
</roleItem>
<roleItem id="3" name="Operator" autologout="1" logouttime="20">
<roleItem id="3" name="Operator" autologout="1" logouttime="20" buildin="true">
Header,3;Overview,3;TM,3;EFEM,3;Device,3;ioPlatform,3;LeakCheck,3;Sequence,3;Recipe,3;EventLog,3;RealTimeCharting,3;WaferHistory,3;DataHistory,3;
Charting,3;Config,3;Account,3;Role,3;Runtime,3;Alarm,3;mainPM1,3;processPM1,3;ioPM1,3;motionPM1,3;heaterPM1,3;alarmPM1,3;MFCFlowPM1,3;mainPM2,3;
processPM2,3;ioPM2,3;motionPM2,3;heaterPM2,3;alarmPM2,3;MFCFlowPM2,3;

View File

@ -1,6 +1,37 @@
create or replace function update_db_model() returns void as
$$
begin
------------------------------------------------------------------------------------------------
--
if not exists(select * from information_schema.tables
where
table_catalog = CURRENT_CATALOG and table_schema = CURRENT_SCHEMA
and table_name = 'credentials_history') then
CREATE TABLE credentials_history
(
"uid" serial NOT NULL,
"login_name" varchar(50),
"role_id" varchar(10),
"host_name" varchar(100),
"host_ip" varchar(50),
"host_port" varchar(10),
"os_version" varchar(50),
"computer_info" text,
"cpu_info" text,
"disk_info" text,
"operation" varchar(50),
"operation_time" timestamp without time zone,
CONSTRAINT "credentials_history_pkey" PRIMARY KEY ("uid" )
)
WITH (
OIDS=FALSE
);
ALTER TABLE credentials_history
OWNER TO postgres;
GRANT SELECT ON TABLE credentials_history TO postgres;
end if;
------------------------------------------------------------------------------------------------
--

View File

@ -10,7 +10,7 @@
</EventDefinition>
<EventDefinition>
<Id>10</Id>
<Description>'{0}' 退出系统。</Description>
<Description>{0} logged out</Description>
<Level>Information</Level>
<EventEnum>UserLoggedOff</EventEnum>
<DetailDesc>用户退出系统。</DetailDesc>
@ -18,11 +18,11 @@
</EventDefinition>
<EventDefinition>
<Id>11</Id>
<Description>User {0} login</Description>
<Description>User {0} logged in</Description>
<Level>Information</Level>
<EventEnum>UserLoggedIn</EventEnum>
<DetailDesc>用户登录</DetailDesc>
<GlobalDescription_en>User {0} login</GlobalDescription_en>
<GlobalDescription_en>User {0} logged in</GlobalDescription_en>
<GlobalDescription_zh>账号{0}登录</GlobalDescription_zh>
<Solution />
</EventDefinition>

View File

@ -237,6 +237,12 @@
<Name>SicModules</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Config\Menu.xml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>

View File

@ -3,17 +3,35 @@
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a" />
</configSections>
<!--<system.diagnostics>
<sources>
<source name="System.ServiceModel"
switchValue="Information, ActivityTracing"
propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging">
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource"
switchValue="Information, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="Error.svclog" />
</sharedListeners>
</system.diagnostics>-->
<system.serviceModel>
<!--<diagnostics>
<messageLogging
logEntireMessage="false"
logMalformedMessages="false"
logMessagesAtServiceLevel="true"
logMessagesAtTransportLevel="false"
maxMessagesToLog="3000"
maxSizeOfMessageToLog="2000"/>
</diagnostics>-->
<bindings>
<netTcpBinding>

View File

@ -12,7 +12,7 @@
<ResourceDictionary>
<converters:DummyConverter x:Key="dummyConverter" />
<cv:ReserveBoolSensorConverter x:Key="BoolReverseConverter" />
<cv:BoolReverseConverter x:Key="BoolReverseConverter" />
<cv:BoolVisibilityConverter x:Key="BoolVisibilityConverter" />
<cv:BoolCollapsedConverter x:Key="BoolCollapsedConverter" />

View File

@ -15,6 +15,7 @@ using Aitex.Core.WCF;
using CommandLine;
using MECF.Framework.UI.Client.ClientBase;
using SciChart.Charting.Visuals;
using System.Threading.Tasks;
namespace SicUI.Client
{
@ -137,6 +138,7 @@ namespace SicUI.Client
#endif
Task tLoadSysInfo;
try
{
@ -178,6 +180,16 @@ namespace SicUI.Client
#endregion
_splashScreen?.SetMessage1("Initialize logging system ...");
Singleton<Aitex.Core.RT.Log.LogManager>.Instance.Initialize();
BaseApp.Instance = new ClientApp();
tLoadSysInfo = Task.Run(() =>
{
BaseApp.Instance.LoadSystemInfo();
});
_splashScreen?.SetMessage1(
$"Connecting to SicRT ({hostEndpoint.Address.Host}), please wait ...");
@ -204,7 +216,6 @@ namespace SicUI.Client
}
}
if (!EventClient.Instance.ConnectRT())
{
_splashScreen?.Complete();
@ -223,11 +234,10 @@ namespace SicUI.Client
return;
}
_splashScreen?.SetMessage1("Initialize logging system ...");
Singleton<Aitex.Core.RT.Log.LogManager>.Instance.Initialize();
BaseApp.Instance.Initialize();
_splashScreen?.SetMessage1("Initialize sub modules ...");
BaseApp.Instance = new ClientApp();
_splashScreen?.SetMessage1("Preparing Environment ...");
Task.WaitAll(tLoadSysInfo);
_splashScreen?.SetMessage1("Loading the window ...");

View File

@ -0,0 +1,37 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace SicUI.Converter
{
internal class IsReadOnlyModeToUserInfoNameBgConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var defaultColor = Color.FromRgb(0xC8, 0xE8, 0xFF); //FFC8E8FF
if (value is bool isReadOnlyMode)
{
if (isReadOnlyMode)
return new SolidColorBrush(Colors.Orange);
else
{
var res = Application.Current.TryFindResource("TopFrame_UserInfoBG");
if (res is SolidColorBrush brush)
return brush;
else
return new SolidColorBrush(defaultColor);
}
}
return new SolidColorBrush(defaultColor);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -4,7 +4,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cal="http://www.caliburn.org"
xmlns:client="clr-namespace:SicUI.Client"
xmlns:converter="clr-namespace:MECF.Framework.UI.Client.Ctrlib.Converter;assembly=MECF.Framework.UI.Client"
xmlns:deviceControl="clr-namespace:Aitex.Core.UI.DeviceControl;assembly=MECF.Framework.UI.Core"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:wnd="http://OpenSEMI.Ctrlib.com/presentation"
@ -13,20 +12,45 @@
xmlns:controls="clr-namespace:MECF.Framework.UI.Client.Ctrlib.Controls;assembly=MECF.Framework.UI.Client"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Title="MainView"
Width="1900"
Height="980"
xmlns:converter="clr-namespace:SicUI.Converter"
Title="Sic Auto Edition"
WindowStartupLocation="CenterScreen"
WindowState="Maximized"
WindowState="Maximized"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=client:MainViewModel, IsDesignTimeCreatable=False}">
<Window.Resources>
<client:CollectionLastIndexConverter x:Key="collectionLastIndexConverter" />
<converter:UnitOnlineBorderColorConverter x:Key="UnitOnlineBorderColorConverter" />
<converter:IsReadOnlyModeToUserInfoNameBgConverter x:Key="IsReadOnlyModeToUserInfoNameBgConverter" />
</Window.Resources>
<Grid Background="{DynamicResource MainArea_BG}">
<Grid x:Name="LoginPart" Background="{StaticResource Login_BG}">
<Grid x:Name="LoginPart" Panel.ZIndex="0" Background="{StaticResource Login_BG}">
<Grid.Style>
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsLogin}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Grid.Visibility" Value="Hidden" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsLogin}" Value="False"/>
<Condition Binding="{Binding IsReadOnlyMode}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Grid.Visibility" Value="Hidden" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsLogin}" Value="False"/>
<Condition Binding="{Binding IsReadOnlyMode}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Grid.Visibility" Value="Visible" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Ellipse
MaxWidth="1000"
MaxHeight="800"
@ -241,7 +265,7 @@
TabIndex="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<cal:ActionMessage MethodName="Enter">
<cal:ActionMessage MethodName="PasswordBoxEnterKeyPressed">
<cal:Parameter Value="$eventargs" />
<cal:Parameter Value="{Binding ElementName=tbLoginName, Path=Text}" />
<cal:Parameter Value="{Binding ElementName=pdbPassword}" />
@ -306,7 +330,7 @@
Style="{StaticResource Login_Button}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Login">
<cal:ActionMessage MethodName="RequestLogin">
<cal:Parameter Value="{Binding ElementName=tbLoginName, Path=Text}" />
<cal:Parameter Value="{Binding ElementName=pdbPassword}" />
<cal:Parameter Value="{Binding ElementName=cbRole, Path=SelectedValue}" />
@ -318,15 +342,6 @@
</Grid>
</Border>
</Grid>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLogin}" Value="True">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
<Border
@ -334,6 +349,26 @@
Padding="1"
CornerRadius="4">
<Grid x:Name="MainPage">
<Grid.Style>
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsLogin}" Value="False"/>
<Condition Binding="{Binding IsReadOnlyMode}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="Grid.Visibility" Value="Visible" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsLogin}" Value="False"/>
<Condition Binding="{Binding IsReadOnlyMode}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Grid.Visibility" Value="Hidden" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Grid.RowDefinitions>
<RowDefinition Height="120" />
<RowDefinition />
@ -396,6 +431,7 @@
Text="{Binding SoftwareVersion}" />
</StackPanel>
<Grid
IsHitTestVisible="{Binding IsReadOnlyMode, Converter={StaticResource BoolReverseConverter}}"
Grid.Column="1"
Margin="-25,8,0,8"
ColumnSpan="2">
@ -478,7 +514,6 @@
IsOnline="{Binding IsOnlineWaferRobot}"
Status="{Binding WaferRobotStatus}"
cal:Message.Attach="[SetOnline] = [Action SetModuleOnline($source, $eventArgs)];[SetOffline] = [Action SetModuleOffline($source, $eventArgs)]" />
<controls:ModuleStatusIndicator
Grid.Row="1"
Grid.Column="0"
@ -572,7 +607,6 @@
SelectedIndex="{Binding EventLogsView.Count, Mode=OneWay, Converter={StaticResource collectionLastIndexConverter}}"
Style="{DynamicResource Top_ComboBox}"
Visibility="{Binding AllEventsVisibility}"/>
<Label
Grid.Column="1"
HorizontalContentAlignment="Center"
@ -587,8 +621,6 @@
Foreground="{DynamicResource FG_Black}"
IsChecked="{Binding IsShowAlarmEventOnly, Delay=10}" />
</Label>
<Button
Grid.Column="2"
Width="90"
@ -615,22 +647,8 @@
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</Grid>
<Grid
Grid.Column="2"
Margin="28,10"
IsEnabled="{Binding IsPermission}">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
<Label
Grid.Column="3"
Width="50"
@ -653,7 +671,7 @@
</Grid.RowDefinitions>
<Border
Margin="2,4"
Background="{DynamicResource TopFrame_UserInfoBG}"
Background="{Binding IsReadOnlyMode, Converter={StaticResource IsReadOnlyModeToUserInfoNameBgConverter}}"
BorderBrush="{DynamicResource TopFrame_UserInfoBD}"
BorderThickness="1"
CornerRadius="5"
@ -674,7 +692,7 @@
Style="{DynamicResource Logout_Button}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cal:ActionMessage MethodName="Logout" />
<cal:ActionMessage MethodName="ShowLogoutDialog" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
@ -682,7 +700,11 @@
</Grid>
</Border>
</Grid>
<Grid Grid.Row="1" Background="{DynamicResource MainArea_BG}">
<Grid Grid.Row="1"
x:Name="GdContentContainer"
Background="{DynamicResource MainArea_BG}"
IsHitTestVisible="{Binding IsReadOnlyMode, Converter={StaticResource BoolReverseConverter}}"
PreviewKeyDown="GdContentContainer_OnPreviewKeyDown">
<ContentControl
x:Name="ActiveItem"
Margin="10"
@ -734,15 +756,7 @@
</Menu>
</Grid>
</Border>
<Grid.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsLogin}" Value="False">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
</Border>
</Grid>

View File

@ -33,6 +33,13 @@ namespace SicUI.Client
{
}
private void GdContentContainer_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
// 如果禁用了鼠标输入,同时禁用键盘输入
if (!GdContentContainer.IsHitTestVisible)
e.Handled = true;
}
}
public class CollectionLastIndexConverter : IValueConverter

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@ using SicUI.Controls;
using MECF.Framework.UI.Client.CenterViews.Modules.PM;
using System.Dynamic;
using Caliburn.Micro.Core;
using MECF.Framework.Common.Account.Permissions;
namespace SicUI.Models.Operations.Overviews
{
@ -770,7 +771,7 @@ namespace SicUI.Models.Operations.Overviews
protected override void OnActivate()
{
var roleID = BaseApp.Instance.UserContext.RoleID;
var roleID = BaseApp.Instance.UserContext.Role.RoleId;
var allowProcessMonitorWin = RoleAccountProvider.Instance.GetMenuPermission(roleID, "Operation.Overview.ProcessMonitorWindow") != (int)MenuPermissionEnum.MP_NONE;
ProcessMonitorButtonVisibility = allowProcessMonitorWin ? Visibility.Visible : Visibility.Collapsed;
base.OnActivate();

View File

@ -0,0 +1,37 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace SicUI.Converter
{
internal class IsReadOnlyModeToUserInfoNameBgConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var defaultColor = Color.FromRgb(0xC8, 0xE8, 0xFF); //FFC8E8FF
if (value is bool isReadOnlyMode)
{
if (isReadOnlyMode)
return new SolidColorBrush(Colors.Orange);
else
{
var res = Application.Current.TryFindResource("TopFrame_UserInfoBG");
if (res is SolidColorBrush brush)
return brush;
else
return new SolidColorBrush(defaultColor);
}
}
return new SolidColorBrush(defaultColor);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@ -7,8 +7,6 @@ namespace SicUI.Models.PMs
{
public class PMAlarmViewModel : SicModuleUIViewModelBase, ISupportMultipleSystem
{
public bool IsPermission { get => this.Permission == 3; }
[IgnorePropertyChange]
public List<AlarmItem> AlarmEvents { get; set; }

View File

@ -43,7 +43,6 @@ namespace SicUI.Models.PMs
public Visibility TipsVisble => IsConfinementRingUp && !IsOnline ? Visibility.Hidden : Visibility.Visible;
public bool IsPermission { get => this.Permission == 3; }
public bool IsActionEnable => IsConfinementRingUp && !IsOnline;
public bool IsActionEnable1 => false;

View File

@ -48,7 +48,6 @@ namespace SicUI.Models.PMs
public Visibility TipsVisble => RingUpSensor ? Visibility.Hidden : Visibility.Visible;
public bool IsPermission { get => this.Permission == 3; }
public bool IsActionEnable => RingUpSensor && !IsOnline;
public bool IsActionEnable1 => false;

View File

@ -19,6 +19,7 @@ using Aitex.Core.Common.DeviceData.IoDevice;
using MECF.Framework.Common.Aitex.Core.Common.DeviceData;
using System.CodeDom;
using System;
using MECF.Framework.Common.Account.Permissions;
namespace SicUI.Models.PMs
{
@ -829,8 +830,6 @@ namespace SicUI.Models.PMs
}
}
public bool IsPermission { get => this.Permission == 3; }
public string Module => SystemName;
@ -1346,9 +1345,9 @@ namespace SicUI.Models.PMs
base.InitPM();
//权限
var roleID = BaseApp.Instance.UserContext.RoleID;
reactorStatusEnable = RoleAccountProvider.Instance.GetMenuPermission(roleID, "PM1.Main.ReactorStatus") == 3;
reactorServiceEnable = RoleAccountProvider.Instance.GetMenuPermission(roleID, "PM1.Main.ReactorService") == 3;
var roleID = BaseApp.Instance.UserContext.Role.RoleId;
reactorStatusEnable = RoleAccountProvider.Instance.GetMenuPermission(roleID, "PM1.Main.ReactorStatus") == MenuPermissionEnum.MP_READ_WRITE;
reactorServiceEnable = RoleAccountProvider.Instance.GetMenuPermission(roleID, "PM1.Main.ReactorService") == MenuPermissionEnum.MP_READ_WRITE;
}
private bool reactorStatusEnable;

View File

@ -291,6 +291,7 @@ if exist "$(ProjectDir)..\SicSetup\Packages\SicUI\PresetGroups" rd "$(ProjectDir
<Compile Include="Models\PMs\ContinueSelectDialogView.xaml.cs">
<DependentUpon>ContinueSelectDialogView.xaml</DependentUpon>
</Compile>
<Compile Include="Models\PMs\Converters\IsReadOnlyModeToUserInfoNameBgConverter.cs" />
<Compile Include="Models\PMs\Converters\WaterTempDeviceDataToToolTipConverter.cs" />
<Compile Include="Models\PMs\InverseBoolConverter.cs" />
<Compile Include="Models\PMs\PMAlarmView.xaml.cs">
@ -398,6 +399,7 @@ if exist "$(ProjectDir)..\SicSetup\Packages\SicUI\PresetGroups" rd "$(ProjectDir
<DependentUpon>Splash.xaml</DependentUpon>
</Compile>
<Compile Include="StartupArguments.cs" />
<Compile Include="TimeredMainViewModel.cs" />
<Page Include="Controls\AITPump.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@ -693,12 +695,6 @@ if exist "$(ProjectDir)..\SicSetup\Packages\SicUI\PresetGroups" rd "$(ProjectDir
<ItemGroup>
<Resource Include="Themes\Images\parts\atmrobot\image02.png" />
</ItemGroup>
<ItemGroup>
<None Include="Config\Menu.xml">
<SubType>Designer</SubType>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Resource Include="Themes\Images\parts\image15.png" />
</ItemGroup>

View File

@ -5,13 +5,19 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SicUI"
mc:Ignorable="d"
ResizeMode="NoResize"
ResizeMode="CanResize"
WindowStyle="None"
WindowStartupLocation="CenterScreen"
Title="Splash" Height="510" Width="850"
Background="Transparent"
AllowsTransparency="True"
SnapsToDevicePixels="True">
<WindowChrome.WindowChrome>
<WindowChrome ResizeBorderThickness="0" CaptionHeight="0"/>
</WindowChrome.WindowChrome>
<Window.Effect>
<DropShadowEffect Color="Gray" BlurRadius="20" Direction="-90" RenderingBias="Quality" ShadowDepth="10"/>
</Window.Effect>
<Grid >
<Image Source="/Resources/Images/Splash.png" Stretch="Fill" />
<TextBlock

View File

@ -0,0 +1,243 @@
using Aitex.Core.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using Aitex.Core.RT.Log;
using Cali = Caliburn.Micro.Core;
using MECF.Framework.Common.DataCenter;
namespace SicUI
{
public class TimeredMainViewModel : Cali.Conductor<Cali.Screen>.Collection.OneActive
{
PeriodicJob _timer;
ConcurrentBag<string> _subscribedKeys = new ConcurrentBag<string>();
Func<object, bool> _isSubscriptionAttribute;
Func<MemberInfo, bool> _hasSubscriptionAttribute;
public TimeredMainViewModel()
{
_timer = new PeriodicJob(1000, this.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()
{
LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo);
lastInputInfo.dwTime = 0;
int 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)
{
}
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);
}
}
});
}
}
}

Binary file not shown.