Unlock progress UI:
- UnlockProgressDialog with dark-themed progress ring, phase indicator, elapsed
time, and cancel/close buttons (non-modal, draggable borderless window)
- UnlockProgressViewModel with event-driven progress tracking via IUnlockService
- Triggers on pump selection (manual or K-Line auto-detect), not test start
UnlockService rewrite:
- Persistent CAN senders that outlive the unlock sequence (StopSenders on pump change)
- Concurrent K-Line fast unlock: awaits session Connected, sends RAM timer shortcut
({02 88 02 03 A8 01 00}), verifies via CAN TestUnlock before skipping wait
- Fix Type 1 verification (Value == 0 means unlocked, was inverted)
K-Line fast unlock support:
- IKwpService.TryFastUnlockAsync / KwpService implementation
Additional features:
- ILocalizationService with ES/EN resource dictionaries and runtime switching
- Safety dialogs: VoltageWarning, OilPumpConfirm, RpmSafetyWarning
- SettingsDialog for app configuration
- BenchService enhancements, ConfigurationService improvements, PDF report updates
- All UI strings localized via DynamicResource
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
391 lines
22 KiB
XML
391 lines
22 KiB
XML
<UserControl x:Class="HC_APTBS.Views.UserControls.TestPanelView"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
xmlns:vm="clr-namespace:HC_APTBS.ViewModels"
|
|
mc:Ignorable="d"
|
|
d:DesignHeight="600" d:DesignWidth="900"
|
|
Background="#FFEDEDED" Foreground="Black">
|
|
|
|
<UserControl.Resources>
|
|
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════
|
|
GRAPHIC INDICATOR TEMPLATE — vertical progress bar per receive param
|
|
═══════════════════════════════════════════════════════════════════ -->
|
|
<DataTemplate DataType="{x:Type vm:GraphicIndicatorViewModel}">
|
|
<Grid Width="58" Margin="2,0">
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/> <!-- Max label -->
|
|
<RowDefinition Height="90"/> <!-- Progress bar -->
|
|
<RowDefinition Height="Auto"/> <!-- Min label -->
|
|
<RowDefinition Height="Auto"/> <!-- Param name -->
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- Max bound -->
|
|
<TextBlock Text="{Binding MaxBound, StringFormat=F1}"
|
|
FontSize="9" Foreground="Gray"
|
|
HorizontalAlignment="Center" Margin="0,0,0,1"/>
|
|
|
|
<!-- Vertical progress bar area -->
|
|
<Grid Grid.Row="1">
|
|
<!-- Border around the bar -->
|
|
<Border BorderBrush="Black" BorderThickness="1"
|
|
SnapsToDevicePixels="True"/>
|
|
|
|
<!-- The progress bar itself -->
|
|
<ProgressBar Orientation="Vertical"
|
|
Minimum="0" Maximum="100"
|
|
Value="{Binding ProgressPercent, Mode=OneWay}"
|
|
BorderThickness="0"
|
|
Background="White"
|
|
Margin="1">
|
|
<ProgressBar.Style>
|
|
<Style TargetType="ProgressBar">
|
|
<Setter Property="Foreground" Value="#4CAF50"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsWithinTolerance}" Value="False">
|
|
<Setter Property="Foreground" Value="#FF5722"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ProgressBar.Style>
|
|
</ProgressBar>
|
|
|
|
<!-- Dashed tolerance boundary lines -->
|
|
<Canvas ClipToBounds="True">
|
|
<!-- Upper tolerance line at ~80% from bottom (60% band + 20% margin) -->
|
|
<Line X1="0" X2="58" StrokeDashArray="3,2"
|
|
Stroke="LightGray" StrokeThickness="1"
|
|
Canvas.Top="18"/>
|
|
<!-- Lower tolerance line at ~20% from bottom -->
|
|
<Line X1="0" X2="58" StrokeDashArray="3,2"
|
|
Stroke="LightGray" StrokeThickness="1"
|
|
Canvas.Top="72"/>
|
|
</Canvas>
|
|
|
|
<!-- Expected value label (center) -->
|
|
<TextBlock Text="{Binding ExpectedValue, StringFormat=F1}"
|
|
FontSize="9" Foreground="#999999"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Margin="0,-14,0,0"/>
|
|
|
|
<!-- Current value label (center, bold) -->
|
|
<TextBlock Text="{Binding DisplayValue}"
|
|
FontSize="14" FontWeight="Black"
|
|
HorizontalAlignment="Center" VerticalAlignment="Center"
|
|
Margin="0,10,0,0">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Foreground" Value="Black"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding HasValue}" Value="False">
|
|
<Setter Property="Foreground" Value="#CCCCCC"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
</Grid>
|
|
|
|
<!-- Min bound -->
|
|
<TextBlock Grid.Row="2"
|
|
Text="{Binding MinBound, StringFormat=F1}"
|
|
FontSize="9" Foreground="Gray"
|
|
HorizontalAlignment="Center" Margin="0,1,0,0"/>
|
|
|
|
<!-- Parameter name -->
|
|
<TextBlock Grid.Row="3"
|
|
Text="{Binding ParameterName}"
|
|
FontSize="9" FontWeight="SemiBold"
|
|
HorizontalAlignment="Center"
|
|
TextTrimming="CharacterEllipsis"
|
|
ToolTip="{Binding ParameterName}"/>
|
|
</Grid>
|
|
</DataTemplate>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════
|
|
OPERATION VALUE TEMPLATE — simple name: value pair
|
|
═══════════════════════════════════════════════════════════════════ -->
|
|
<DataTemplate DataType="{x:Type vm:OperationValueViewModel}">
|
|
<StackPanel Orientation="Horizontal" Margin="0,0,6,0">
|
|
<TextBlock Text="{Binding Name}" FontSize="10" Foreground="Black"
|
|
Padding="0,0,2,0"/>
|
|
<TextBlock Text=":" FontSize="10" Foreground="Black"/>
|
|
<TextBlock Text="{Binding Value, StringFormat=F1}" FontSize="10"
|
|
Foreground="DimGray" Padding="2,0,0,0"
|
|
HorizontalAlignment="Right"/>
|
|
</StackPanel>
|
|
</DataTemplate>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════
|
|
PHASE CARD TEMPLATE
|
|
═══════════════════════════════════════════════════════════════════ -->
|
|
<DataTemplate x:Key="PhaseCardTemplate" DataType="{x:Type vm:PhaseCardViewModel}">
|
|
<Border MinWidth="100" Margin="2,0" Padding="4"
|
|
CornerRadius="2" SnapsToDevicePixels="True">
|
|
<Border.Style>
|
|
<Style TargetType="Border">
|
|
<Setter Property="Background" Value="#F0F0F0"/>
|
|
<Setter Property="BorderBrush" Value="#CCCCCC"/>
|
|
<Setter Property="BorderThickness" Value="1"/>
|
|
<Setter Property="Opacity" Value="1"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsActive}" Value="True">
|
|
<Setter Property="Background" Value="#FFE082"/>
|
|
<Setter Property="BorderBrush" Value="#F9A825"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsPassed}" Value="True">
|
|
<Setter Property="Background" Value="#C8E6C9"/>
|
|
<Setter Property="BorderBrush" Value="#388E3C"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsFailed}" Value="True">
|
|
<Setter Property="Background" Value="#FFCDD2"/>
|
|
<Setter Property="BorderBrush" Value="#C62828"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
|
|
<Setter Property="Background" Value="#E0E0E0"/>
|
|
<Setter Property="BorderBrush" Value="#BDBDBD"/>
|
|
<Setter Property="Opacity" Value="0.5"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
|
|
<Grid>
|
|
<!-- Enable/disable toggle as a transparent overlay -->
|
|
<CheckBox IsChecked="{Binding IsEnabled}"
|
|
HorizontalAlignment="Left" VerticalAlignment="Top"
|
|
Margin="0,0,0,0" ToolTip="Enable/disable this phase"/>
|
|
|
|
<StackPanel Margin="0,2,0,0">
|
|
<!-- Phase name -->
|
|
<TextBlock Text="{Binding Name}"
|
|
FontSize="11" FontWeight="SemiBold"
|
|
HorizontalAlignment="Center"
|
|
TextWrapping="Wrap"
|
|
Margin="16,0,0,0"/>
|
|
|
|
<!-- Critical indicator -->
|
|
<TextBlock Text="{DynamicResource Test.Critical}" FontSize="9"
|
|
Foreground="#E65100" FontWeight="Bold"
|
|
HorizontalAlignment="Center"
|
|
Visibility="{Binding IsCritical, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<!-- Operation values (show/hide) -->
|
|
<StackPanel Visibility="{Binding ShowOperationValues, Converter={StaticResource BoolToVis}}"
|
|
Margin="0,3,0,0">
|
|
<!-- Ready values -->
|
|
<StackPanel Visibility="{Binding ReadyValues.Count, FallbackValue=Collapsed}">
|
|
<TextBlock Text="{DynamicResource Test.Required}" FontSize="9" Foreground="#666"
|
|
FontWeight="SemiBold" Margin="0,1,0,0"/>
|
|
<ItemsControl ItemsSource="{Binding ReadyValues}"/>
|
|
</StackPanel>
|
|
|
|
<!-- Send values -->
|
|
<TextBlock Text="{DynamicResource Test.TestLabel}" FontSize="9" Foreground="#666"
|
|
FontWeight="SemiBold" Margin="0,2,0,0"/>
|
|
<ItemsControl ItemsSource="{Binding OperationValues}"/>
|
|
</StackPanel>
|
|
|
|
<!-- Graphic result indicators -->
|
|
<ItemsControl ItemsSource="{Binding ResultIndicators}" Margin="0,4,0,0">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<StackPanel Orientation="Horizontal"
|
|
HorizontalAlignment="Center"/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
</ItemsControl>
|
|
|
|
<!-- Result text -->
|
|
<TextBlock Text="{Binding ResultText}"
|
|
FontSize="12" FontWeight="Bold"
|
|
HorizontalAlignment="Center" Margin="0,3,0,0">
|
|
<TextBlock.Style>
|
|
<Style TargetType="TextBlock">
|
|
<Setter Property="Foreground" Value="DimGray"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsPassed}" Value="True">
|
|
<Setter Property="Foreground" Value="#2E7D32"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding IsFailed}" Value="True">
|
|
<Setter Property="Foreground" Value="#B71C1C"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</TextBlock.Style>
|
|
</TextBlock>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
</DataTemplate>
|
|
|
|
<!-- ═══════════════════════════════════════════════════════════════════
|
|
TEST SECTION TEMPLATE (one per TestDefinition)
|
|
═══════════════════════════════════════════════════════════════════ -->
|
|
<DataTemplate x:Key="TestSectionTemplate" DataType="{x:Type vm:TestSectionViewModel}">
|
|
<Expander IsExpanded="{Binding IsExpanded}" Margin="0,2,0,0">
|
|
<!-- Custom header -->
|
|
<Expander.Header>
|
|
<Border Padding="4,2" CornerRadius="2">
|
|
<Border.Style>
|
|
<Style TargetType="Border">
|
|
<Setter Property="Background" Value="Transparent"/>
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsActiveTest}" Value="True">
|
|
<Setter Property="Background" Value="#FFFFF3CD"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/> <!-- Name -->
|
|
<ColumnDefinition Width="Auto"/> <!-- Description -->
|
|
<ColumnDefinition/> <!-- Timing -->
|
|
<ColumnDefinition Width="Auto"/> <!-- Check all -->
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Test name -->
|
|
<TextBlock Text="{Binding TestName}"
|
|
FontSize="20" FontFamily="Impact" FontStyle="Italic"
|
|
VerticalAlignment="Center" Foreground="Black"
|
|
Padding="4,0"/>
|
|
|
|
<!-- Description -->
|
|
<TextBlock Grid.Column="1"
|
|
Text="{Binding Description}"
|
|
FontSize="14" FontStyle="Italic" FontFamily="Impact"
|
|
VerticalAlignment="Bottom" Foreground="Gray"
|
|
Padding="8,0,0,3"/>
|
|
|
|
<!-- Timing info -->
|
|
<StackPanel Grid.Column="2" Orientation="Horizontal"
|
|
VerticalAlignment="Center" Margin="16,0,0,0">
|
|
<TextBlock FontSize="10" Foreground="DimGray">
|
|
<Run Text="{DynamicResource Test.Condition}"/>
|
|
<Run Text="{Binding ConditioningTimeSec, Mode=OneWay}"/>
|
|
<Run Text="s"/>
|
|
</TextBlock>
|
|
<TextBlock FontSize="10" Foreground="DimGray" Margin="10,0,0,0">
|
|
<Run Text="{DynamicResource Test.Measurement}"/>
|
|
<Run Text="{Binding MeasurementTimeSec, Mode=OneWay}"/>
|
|
<Run Text="s"/>
|
|
</TextBlock>
|
|
<TextBlock FontSize="10" Foreground="DimGray" Margin="10,0,0,0">
|
|
<Run Text="{DynamicResource Test.MeasPerSec}"/>
|
|
<Run Text="{Binding MeasurementsPerSecond, StringFormat=F1, Mode=OneWay}"/>
|
|
</TextBlock>
|
|
</StackPanel>
|
|
|
|
<!-- Per-section check all -->
|
|
<CheckBox Grid.Column="3"
|
|
IsChecked="{Binding AllPhasesChecked}"
|
|
VerticalAlignment="Center" Margin="8,0"
|
|
ToolTip="Enable/disable all phases in this test"/>
|
|
</Grid>
|
|
</Border>
|
|
</Expander.Header>
|
|
|
|
<!-- Phase cards (horizontal scroll) -->
|
|
<ScrollViewer HorizontalScrollBarVisibility="Auto"
|
|
VerticalScrollBarVisibility="Disabled"
|
|
Padding="0,4">
|
|
<ItemsControl ItemsSource="{Binding Phases}"
|
|
ItemTemplate="{StaticResource PhaseCardTemplate}">
|
|
<ItemsControl.ItemsPanel>
|
|
<ItemsPanelTemplate>
|
|
<StackPanel Orientation="Horizontal"/>
|
|
</ItemsPanelTemplate>
|
|
</ItemsControl.ItemsPanel>
|
|
</ItemsControl>
|
|
</ScrollViewer>
|
|
</Expander>
|
|
</DataTemplate>
|
|
</UserControl.Resources>
|
|
|
|
<Grid>
|
|
<Grid.RowDefinitions>
|
|
<RowDefinition Height="Auto"/> <!-- Test controls -->
|
|
<RowDefinition Height="Auto"/> <!-- Toolbar -->
|
|
<RowDefinition Height="Auto"/> <!-- Status bar -->
|
|
<RowDefinition/> <!-- Test sections -->
|
|
</Grid.RowDefinitions>
|
|
|
|
<!-- ── Test controls (Start / Stop / Report) ──────────────────────── -->
|
|
<Border Padding="6,8" BorderBrush="#999" BorderThickness="0,0,0,1">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition/>
|
|
<ColumnDefinition/>
|
|
<ColumnDefinition/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<Button Content="{DynamicResource Test.StartTest}" FontSize="15" FontWeight="Bold"
|
|
Height="44" Margin="0,0,4,0"
|
|
Command="{Binding DataContext.StartTestCommand,
|
|
RelativeSource={RelativeSource AncestorType=Window}}"
|
|
Foreground="DarkGreen"/>
|
|
<Button Grid.Column="1" Content="{DynamicResource Test.Stop}" FontSize="15" FontWeight="Bold"
|
|
Height="44" Margin="4,0"
|
|
Command="{Binding DataContext.StopTestCommand,
|
|
RelativeSource={RelativeSource AncestorType=Window}}"
|
|
Foreground="DarkRed"/>
|
|
<Button Grid.Column="2" Content="{DynamicResource Test.Report}" FontSize="13"
|
|
Height="44" Margin="4,0,0,0"
|
|
Command="{Binding DataContext.GenerateReportCommand,
|
|
RelativeSource={RelativeSource AncestorType=Window}}"/>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ── Toolbar ─────────────────────────────────────────────────────── -->
|
|
<Border Grid.Row="1" BorderBrush="Gray" BorderThickness="0,0,0,1" Padding="8,4">
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Show values toggle -->
|
|
<CheckBox IsChecked="{Binding ShowOperationValues}"
|
|
VerticalAlignment="Center">
|
|
<TextBlock Text="{DynamicResource Test.ShowValues}" FontSize="12"/>
|
|
</CheckBox>
|
|
|
|
<!-- Check all button -->
|
|
<Button Grid.Column="1" Margin="12,0,0,0"
|
|
Command="{Binding ToggleCheckAllCommand}"
|
|
Padding="6,2" ToolTip="Enable/disable all phases">
|
|
<TextBlock Text="{DynamicResource Test.CheckAll}" FontSize="11"/>
|
|
</Button>
|
|
|
|
<!-- Remaining time -->
|
|
<TextBlock Grid.Column="3" VerticalAlignment="Center"
|
|
Foreground="DimGray" FontSize="12" Margin="0,0,4,0">
|
|
<Run Text="~"/>
|
|
<Run Text="{Binding RemainingSeconds, Mode=OneWay}"/>
|
|
<Run Text="{DynamicResource Test.SecondsRemaining}"/>
|
|
</TextBlock>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ── Status line ─────────────────────────────────────────────────── -->
|
|
<TextBlock Grid.Row="2"
|
|
Text="{Binding StatusText}"
|
|
FontSize="12" FontStyle="Italic"
|
|
Foreground="Gray" Margin="8,2"
|
|
Visibility="{Binding IsRunning, Converter={StaticResource BoolToVis}}"/>
|
|
|
|
<!-- ── Test sections ───────────────────────────────────────────────── -->
|
|
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto">
|
|
<ItemsControl ItemsSource="{Binding Tests}"
|
|
ItemTemplate="{StaticResource TestSectionTemplate}"
|
|
Margin="4"/>
|
|
</ScrollViewer>
|
|
</Grid>
|
|
</UserControl>
|