SSPC-Tester/SSPCTester.UI/Views/MainWindow.xaml
2026-07-08 00:25:45 +08:00

399 lines
29 KiB
XML

<Window x:Class="SSPCTester.UI.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:SSPCTester.UI.ViewModels"
xmlns:v="clr-namespace:SSPCTester.UI.Views"
mc:Ignorable="d"
Title="固态功率控制器动态参数检测系统"
Width="1440" Height="900"
WindowState="Maximized"
WindowStartupLocation="CenterScreen"
Background="{StaticResource BgAppBrush}"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- ====== 标题栏 ====== -->
<Border Grid.Row="0" Background="{StaticResource BgTitlebarBrush}"
BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,0,0,1">
<Grid Margin="20,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Grid.Column="0">
<Viewbox Width="22" Height="22" Margin="0,0,12,0">
<Canvas Width="24" Height="24">
<Path Data="{StaticResource LogoGeometry}" Fill="{StaticResource PrimaryBrush}" />
</Canvas>
</Viewbox>
<TextBlock Text="固态功率控制器动态参数检测系统"
FontSize="{StaticResource FsTitle}" FontWeight="Bold" />
</StackPanel>
<StackPanel Orientation="Horizontal" Grid.Column="2" VerticalAlignment="Center">
<Grid Width="48" Height="48" Margin="0,0,6,0"
ToolTip="{Binding SafetyDetailText}">
<Ellipse x:Name="AlarmHalo" IsHitTestVisible="False"
RenderTransformOrigin="0.5,0.5">
<Ellipse.Fill>
<RadialGradientBrush Center="0.5,0.5" GradientOrigin="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="#66FF001A" Offset="0.0" />
<GradientStop Color="#99FF1030" Offset="0.30" />
<GradientStop Color="#44FF0018" Offset="0.60" />
<GradientStop Color="#00FF0018" Offset="1.0" />
</RadialGradientBrush>
</Ellipse.Fill>
<Ellipse.RenderTransform>
<ScaleTransform ScaleX="1" ScaleY="1" />
</Ellipse.RenderTransform>
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
<Setter Property="Opacity" Value="1" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="AlarmHaloPulse">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1.0" To="0.35" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="AlarmHaloPulse" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<Viewbox Width="36" Height="36" HorizontalAlignment="Center" VerticalAlignment="Center">
<Canvas Width="64" Height="64">
<Canvas.Resources>
<RadialGradientBrush x:Key="AlarmRedGlassBrush" Center="0.34,0.16" GradientOrigin="0.34,0.16" RadiusX="0.78" RadiusY="0.78">
<GradientStop Color="#FFFF8A8A" Offset="0" />
<GradientStop Color="#FFDF0010" Offset="0.36" />
<GradientStop Color="#FF720006" Offset="1" />
</RadialGradientBrush>
<RadialGradientBrush x:Key="AlarmGrayGlassBrush" Center="0.34,0.16" GradientOrigin="0.34,0.16" RadiusX="0.78" RadiusY="0.78">
<GradientStop Color="#FFDEDEDE" Offset="0" />
<GradientStop Color="#FF969696" Offset="0.36" />
<GradientStop Color="#FF4B4B4B" Offset="1" />
</RadialGradientBrush>
<LinearGradientBrush x:Key="AlarmBaseBrush" StartPoint="0,0" EndPoint="0,1">
<GradientStop Color="#FF3B3B3B" Offset="0" />
<GradientStop Color="#FF101010" Offset="0.45" />
<GradientStop Color="#FF303030" Offset="1" />
</LinearGradientBrush>
</Canvas.Resources>
<Path x:Name="AlarmGlass" Data="M14 44V23C14 10.5 22.1 4 32 4s18 6.5 18 19v21z">
<Path.Style>
<Style TargetType="Path">
<Setter Property="Fill" Value="{StaticResource AlarmGrayGlassBrush}" />
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
<Setter Property="Fill" Value="{StaticResource AlarmRedGlassBrush}" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="AlarmGlassBlink">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To=".42" Duration="0:0:0.36" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="AlarmGlassBlink" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Path x:Name="AlarmRim" Data="M14 44V23C14 10.5 22.1 4 32 4s18 6.5 18 19v21z"
Fill="Transparent">
<Path.Style>
<Style TargetType="Path">
<Setter Property="Stroke" Value="#FF555555" />
<Setter Property="StrokeThickness" Value="1.4" />
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
<Setter Property="Stroke" Value="#FFFF2635" />
<Setter Property="StrokeThickness" Value="2.2" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="AlarmRimBlink">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To=".35" Duration="0:0:0.36" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="AlarmRimBlink" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
<Canvas Width="64" Height="64" IsHitTestVisible="False">
<Canvas.Clip>
<PathGeometry Figures="M14 44V23C14 10.5 22.1 4 32 4s18 6.5 18 19v21z" />
</Canvas.Clip>
<Path x:Name="AlarmSweep" Data="M17 6h8l-11 42h-8z"
Fill="White">
<Path.RenderTransform>
<TranslateTransform />
</Path.RenderTransform>
<Path.Style>
<Style TargetType="Path">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
<Setter Property="Opacity" Value=".65" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="AlarmSweepMove">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
From="-24" To="30" Duration="0:0:0.95" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="AlarmSweepMove" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Path.Style>
</Path>
</Canvas>
<Path Data="M25 13l7-3 3 6-8 3zM38 11c4 1 6 3 8 7l-5 2c-1-3-3-4-6-5z"
Fill="White" Opacity=".72" />
<Ellipse Canvas.Left="10" Canvas.Top="39" Width="44" Height="10" Fill="#771A0000" />
<Rectangle x:Name="AlarmBaseFlash" Canvas.Left="13" Canvas.Top="42" Width="38" Height="2"
Fill="#FFFF0014">
<Rectangle.Style>
<Style TargetType="Rectangle">
<Setter Property="Opacity" Value="0" />
<Style.Triggers>
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
<Setter Property="Opacity" Value="1" />
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="AlarmBaseBlink">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To=".15" Duration="0:0:0.36" AutoReverse="True" RepeatBehavior="Forever" />
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="AlarmBaseBlink" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<Path Data="M11 43h42v6H11z" Fill="#DD240004" />
<Path Data="M9 48h46v8c0 3-3 5-6 5H15c-3 0-6-2-6-5z"
Fill="{StaticResource AlarmBaseBrush}" />
<Path Data="M13 50h38" Stroke="#777777" StrokeThickness="1" Opacity=".45" />
<Path Data="M42 56v4M47 55v4M52 54v3" Stroke="#050505" StrokeThickness="2" StrokeStartLineCap="Round" StrokeEndLineCap="Round" />
</Canvas>
</Viewbox>
</Grid>
<Button Margin="0,0,8,0" Padding="12,4"
Command="{Binding ToggleEmergencyAlarmCommand}"
IsEnabled="{Binding EmergencyOperationBusy, Converter={StaticResource InverseBoolConverter}}"
ToolTip="{Binding SafetyDetailText}">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource BaseButton}">
<Setter Property="Content" Value="{Binding EmergencyButtonText}" />
<Setter Property="Background" Value="{StaticResource DangerBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource DangerBrush}" />
<Setter Property="Foreground" Value="{StaticResource TextInverseBrush}" />
<Setter Property="FontWeight" Value="SemiBold" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="EmergencyButtonChrome"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{StaticResource RadiusMd}"
SnapsToDevicePixels="True">
<TextBlock x:Name="EmergencyButtonText"
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"
FontFamily="{Binding FontFamily, RelativeSource={RelativeSource TemplatedParent}}"
FontSize="{Binding FontSize, RelativeSource={RelativeSource TemplatedParent}}"
FontWeight="{Binding FontWeight, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="{TemplateBinding Padding}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="EmergencyButtonChrome" Property="Background" Value="#EAF3FC" />
<Setter TargetName="EmergencyButtonChrome" Property="BorderBrush" Value="{StaticResource DangerBrush}" />
<Setter Property="Foreground" Value="#000000" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="EmergencyButtonChrome" Property="Opacity" Value="0.9" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
<Grid>
<Button x:Name="DeviceStatusButton" Padding="10,4" BorderThickness="0"
Background="{Binding DevicesConnected, Converter={StaticResource StatusToSoftBrush}, ConverterParameter=Pass}"
Command="{Binding ToggleDevicePanelCommand}" ToolTip="查看和控制硬件连接">
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" Margin="0,0,6,0" VerticalAlignment="Center"
Fill="{Binding DevicesConnected, Converter={StaticResource StatusToBrush}, ConverterParameter=Pass}" />
<TextBlock Text="{Binding DeviceSummary}" VerticalAlignment="Center" FontSize="{StaticResource FsSmall}" />
</StackPanel>
</Button>
<Popup IsOpen="{Binding DevicePanelOpen}" PlacementTarget="{Binding ElementName=DeviceStatusButton}"
Placement="Bottom" StaysOpen="False" AllowsTransparency="True">
<Border Width="330" Margin="0,8,0,0" Padding="16"
Background="{StaticResource BgSurfaceBrush}" BorderBrush="{StaticResource BorderBrush}"
BorderThickness="1" CornerRadius="{StaticResource RadiusMd}">
<StackPanel>
<TextBlock Text="硬件设备状态" FontWeight="Bold" FontSize="{StaticResource FsH2}" Margin="0,0,0,12" />
<Grid Margin="0,3">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="SSPC Modbus" /><TextBlock Grid.Column="1" Text="{Binding SspcConnectionStatus}" />
</Grid>
<Grid Margin="0,3">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="电源 UDP5080" /><TextBlock Grid.Column="1" Text="{Binding PowerConnectionStatus}" />
</Grid>
<Grid Margin="0,3">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="IT8702P 电子负载" /><TextBlock Grid.Column="1" Text="{Binding LoadConnectionStatus}" />
</Grid>
<Grid Margin="0,3">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="PCIe8586 高速采集卡" /><TextBlock Grid.Column="1" Text="{Binding DaqConnectionStatus}" />
</Grid>
<Grid Margin="0,3,0,12">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="报警/急停" /><TextBlock Grid.Column="1" Text="{Binding Pci2312ConnectionStatus}" />
</Grid>
<WrapPanel>
<Button Content="连接全部" Command="{Binding ConnectAllDevicesCommand}" Margin="0,0,8,8" />
<Button Content="断开全部" Command="{Binding DisconnectAllDevicesCommand}" Margin="0,0,8,8" />
<Button Content="刷新状态" Command="{Binding RefreshDevicesCommand}" Margin="0,0,0,8" />
</WrapPanel>
<Button Content="打开设备设置" Command="{Binding OpenSettingsFromDevicePanelCommand}"
HorizontalAlignment="Stretch" />
</StackPanel>
</Border>
</Popup>
</Grid>
<Button Margin="12,0,0,0" Width="34" Height="30" Padding="0"
Command="{Binding OpenSettingsCommand}"
ToolTip="系统设置" Background="Transparent" BorderBrush="Transparent">
<Viewbox Width="18" Height="18">
<Canvas Width="24" Height="24">
<Path Data="{StaticResource SettingsGeometry}" Fill="{StaticResource TextSecondaryBrush}" />
</Canvas>
</Viewbox>
</Button>
</StackPanel>
</Grid>
</Border>
<!-- ====== 主体 ====== -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="232" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<!-- 侧栏 -->
<Border Background="{StaticResource BgSidebarBrush}" BorderBrush="{StaticResource BorderBrush}" BorderThickness="0,0,1,0">
<DockPanel>
<Border DockPanel.Dock="Top" Padding="12,10" BorderBrush="{StaticResource BorderSubtleBrush}" BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="最近项目" FontWeight="Bold" VerticalAlignment="Center" />
<Button Grid.Column="1" Width="26" Height="26" Padding="0"
Command="{Binding NewProjectCommand}"
ToolTip="新建项目" Background="Transparent" BorderBrush="Transparent">
<Viewbox Width="16" Height="16">
<Canvas Width="24" Height="24">
<Path Data="{StaticResource PlusGeometry}" Fill="{StaticResource PrimaryBrush}" />
</Canvas>
</Viewbox>
</Button>
</Grid>
</Border>
<TreeView x:Name="ProjectTree" ItemsSource="{Binding TreeRoots}"
MouseDoubleClick="ProjectTree_MouseDoubleClick" Padding="6,6">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type vm:ProjectNode}" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Title}" Padding="0,2" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</DockPanel>
</Border>
<!-- 内容区 -->
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- 测试进行中状态条 -->
<Border Grid.Row="0" Padding="16,10"
Background="{StaticResource TestingBarBrush}"
BorderBrush="{StaticResource TestingBarBorderBrush}"
BorderThickness="0,0,0,1"
Visibility="{Binding CurrentSession, Converter={StaticResource NullToVis}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Ellipse Width="10" Height="10" VerticalAlignment="Center" Fill="{StaticResource WarningBrush}" />
<TextBlock Grid.Column="1" Margin="10,0,0,0" VerticalAlignment="Center"
FontWeight="SemiBold" Foreground="{StaticResource TestingBarTextBrush}"
Text="{Binding TestingMessage}" />
<Button Grid.Column="2" Style="{StaticResource DangerButton}" Content="退出测试"
Command="{Binding AbortTestCommand}" />
</Grid>
</Border>
<ContentControl Grid.Row="1" Content="{Binding CurrentView}" Margin="0" />
</Grid>
</Grid>
</Grid>
</Window>