feat: page-based navigation shell + Tests page wizard

Replace the monolithic MainWindow with a SelectedPage-driven shell
(Dashboard / Pump / Bench / Tests / Results / Settings). The Tests
page gets the Plan -> Preconditions -> Running -> Done wizard from
ui-structure.md \u00a74, backed by a 7-item precondition gate and
shared sub-views (PhaseCardView / TestSectionView / GraphicIndicatorView)
extracted from the now-deleted monolithic TestPanelView.

New VMs / views:
- Tests wizard: TestPreconditions, PhaseCard, GraphicIndicator,
  TestSection, TestPlan, TestRunning, TestDone
- Dashboard panels: DashboardConnection, DashboardReadings,
  DashboardAlarms, InterlockBanner, ResultHistory
- Pump / bench panels: PumpIdentificationPanel, PumpLiveData,
  UnlockPanel, BenchDriveControl, BenchReadings, RelayBank,
  TemperatureControl, DtcList, AuthGate
- Dialogs: generic ConfirmDialog, UserManageDialog, UserPromptDialog

Supporting changes:
- IsOilPumpOn exposed on MainViewModel for precondition evaluation
- RequiresAuth added to TestDefinition (XML round-trip)
- BipStatusDefinition + CompletedTestRun models
- ~35 new Test.* localization keys (en + es)
- Settings moved from modal dialog to full page
- Pause / Retry / Skip stubs in TestRunningView; full spec in
  docs/gap-test-running-controls.md for follow-up implementation
- docs/ui-structure.md captures the wizard design

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-18 13:11:34 +02:00
parent 37d099cdbd
commit 0280a2fad1
110 changed files with 8008 additions and 1115 deletions

View File

@@ -0,0 +1,85 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.AuthGateView"
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"
mc:Ignorable="d"
d:DesignHeight="240" d:DesignWidth="500">
<!--
Wraps any child content behind an operator-authentication gate.
DataContext = AuthGateViewModel. Set GatedContent via the attached-like
property below to supply the content to show when authenticated.
Usage (in a parent view):
<uc:AuthGateView DataContext="{Binding AdaptationAuth}">
<uc:AuthGateView.GatedContent>
<uc:DfiManageView DataContext="{Binding DfiViewModel}"/>
</uc:AuthGateView.GatedContent>
</uc:AuthGateView>
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Grid>
<!-- ── Locked state: authenticate prompt ─────────────────────── -->
<Border Background="#F5F5F5" BorderBrush="#CCC" BorderThickness="1"
CornerRadius="4" Padding="20" Margin="6">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsAuthenticated}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="🔒"
HorizontalAlignment="Center" FontSize="40" Margin="0,0,0,8"/>
<TextBlock Text="{DynamicResource AuthGate.LockedTitle}"
FontSize="14" FontWeight="SemiBold"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Text="{DynamicResource AuthGate.LockedMessage}"
FontSize="12" Foreground="#666"
HorizontalAlignment="Center" TextWrapping="Wrap"
MaxWidth="360" TextAlignment="Center" Margin="0,0,0,14"/>
<Button Content="{DynamicResource AuthGate.Authenticate}"
Command="{Binding AuthenticateCommand}"
HorizontalAlignment="Center"
MinWidth="160" Height="34" FontWeight="Bold"/>
</StackPanel>
</Border>
<!-- ── Unlocked state: child content + re-lock button ─────────── -->
<Grid Visibility="{Binding IsAuthenticated, Converter={StaticResource BoolToVis}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" Background="#E8F5E9" BorderBrush="#66BB6A"
BorderThickness="0,0,0,1" Padding="10,4" Margin="6,6,6,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock FontSize="12" Foreground="#2E7D32" VerticalAlignment="Center">
<Run Text="{DynamicResource AuthGate.UnlockedAs}"/>
<Run Text="{Binding AuthenticatedUser, Mode=OneWay}" FontWeight="Bold"/>
</TextBlock>
<Button Grid.Column="1"
Content="{DynamicResource AuthGate.Lock}"
Command="{Binding LockCommand}"
Height="22" MinWidth="70" FontSize="11"/>
</Grid>
</Border>
<ContentPresenter Grid.Row="1"
Content="{Binding GatedContent, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,31 @@
using System.Windows;
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// A gated container that shows <see cref="GatedContent"/> only after the operator
/// authenticates via <c>AuthGateViewModel</c>. DataContext is
/// <c>AuthGateViewModel</c>.
/// </summary>
public partial class AuthGateView : UserControl
{
/// <summary>The content to show once the gate unlocks.</summary>
public static readonly DependencyProperty GatedContentProperty =
DependencyProperty.Register(
nameof(GatedContent), typeof(object), typeof(AuthGateView),
new PropertyMetadata(null));
/// <summary>Gets or sets the content displayed when the gate is unlocked.</summary>
public object? GatedContent
{
get => GetValue(GatedContentProperty);
set => SetValue(GatedContentProperty, value);
}
public AuthGateView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,109 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.BenchDriveControlView"
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"
mc:Ignorable="d"
d:DesignHeight="420" d:DesignWidth="180">
<!--
Manual drive control panel: direction, RPM start/stop with quick-select,
oil pump, and turn counter.
DataContext = BenchPageViewModel; operates on BenchControl.X.
-->
<StackPanel Width="170">
<!-- Direction toggle -->
<TextBlock Text="{DynamicResource Bench.Direction}" FontSize="10" Foreground="DimGray" Margin="0,4,0,2"/>
<ToggleButton IsChecked="{Binding BenchControl.IsDirectionRight}"
Height="32" FontSize="12" FontWeight="SemiBold">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="{DynamicResource Bench.Left}"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.Right}"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<!-- Start / Stop bench -->
<TextBlock Text="{DynamicResource Bench.Motor}" FontSize="10" Foreground="DimGray" Margin="0,8,0,2"/>
<Button Content="{DynamicResource Bench.Start}" FontSize="13" FontWeight="Bold" Height="36"
Foreground="DarkGreen" Margin="0,0,0,4"
Command="{Binding BenchControl.OpenRpmPopupCommand}"/>
<Popup StaysOpen="False" Placement="Left"
IsOpen="{Binding BenchControl.IsRpmPopupOpen, Mode=TwoWay}">
<Border Background="White" BorderBrush="Black" BorderThickness="1" Padding="8">
<StackPanel Width="200">
<TextBlock Text="{DynamicResource Bench.SetRpm}" FontSize="12" Margin="0,0,0,4"/>
<TextBox Text="{Binding BenchControl.RpmInputText, UpdateSourceTrigger=PropertyChanged}"
FontSize="16" FontFamily="Consolas" Height="28" Margin="0,0,0,6"/>
<Button Content="{DynamicResource Bench.Go}" FontSize="13" FontWeight="Bold" Height="30"
Foreground="DarkGreen" Margin="0,0,0,6"
Command="{Binding BenchControl.StartBenchCommand}"/>
<UniformGrid Columns="5">
<Button Content="100" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="100"/>
<Button Content="200" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="200"/>
<Button Content="300" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="300"/>
<Button Content="400" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="400"/>
<Button Content="500" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="500"/>
<Button Content="600" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="600"/>
<Button Content="700" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="700"/>
<Button Content="800" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="800"/>
<Button Content="900" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="900"/>
<Button Content="1000" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="1000"/>
<Button Content="1200" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="1200"/>
<Button Content="1400" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="1400"/>
<Button Content="1600" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="1600"/>
<Button Content="1800" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="1800"/>
<Button Content="2000" Style="{StaticResource RelayButton}" Command="{Binding BenchControl.SetQuickRpmCommand}" CommandParameter="2000"/>
</UniformGrid>
</StackPanel>
</Border>
</Popup>
<Button Content="{DynamicResource Bench.Stop}" FontSize="13" FontWeight="Bold" Height="36"
Foreground="DarkRed"
Command="{Binding BenchControl.StopBenchCommand}"/>
<!-- Oil pump toggle -->
<TextBlock Text="{DynamicResource Bench.OilPump}" FontSize="10" Foreground="DimGray" Margin="0,8,0,2"/>
<ToggleButton IsChecked="{Binding BenchControl.IsOilPumpOn}"
Height="32" FontSize="12" FontWeight="SemiBold">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="{DynamicResource Bench.OilOff}"/>
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.OilOn}"/>
<Setter Property="Background" Value="#80FF80"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<!-- Turn counter -->
<TextBlock Text="{DynamicResource Bench.Counter}" FontSize="10" Foreground="DimGray" Margin="0,8,0,2"/>
<ToggleButton Content="{DynamicResource Bench.Counter}"
IsChecked="{Binding BenchControl.IsCounterPopupOpen}"
Height="28" FontSize="11"/>
<Popup StaysOpen="False" Placement="Left"
IsOpen="{Binding BenchControl.IsCounterPopupOpen, Mode=TwoWay}">
<Border Background="White" BorderBrush="Black" BorderThickness="1" Padding="8">
<StackPanel Width="160">
<TextBlock Text="{DynamicResource Bench.Turns}" FontSize="12" Margin="0,0,0,4"/>
<TextBox Text="{Binding BenchControl.CounterInputText, UpdateSourceTrigger=PropertyChanged}"
FontSize="16" FontFamily="Consolas" Height="28" Margin="0,0,0,4"/>
<Button Content="{DynamicResource Bench.Send}" FontSize="12" Height="28"
Command="{Binding BenchControl.SendCounterCommand}"/>
</StackPanel>
</Border>
</Popup>
<TextBlock FontSize="14" FontFamily="Consolas" Margin="0,2"
Text="{Binding BenchControl.BenchCounterValue, StringFormat=00000000}"/>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Manual drive control panel for the Bench page: direction toggle,
/// RPM start/stop with quick-select popup, oil pump, and turn counter.
/// </summary>
public partial class BenchDriveControlView : UserControl
{
/// <summary>Initializes a new instance of the <see cref="BenchDriveControlView"/> control.</summary>
public BenchDriveControlView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,140 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.BenchReadingsView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="380">
<!--
HMI-style bench readings panel used on the Bench page.
Larger than the Dashboard variant; includes QDelivery + QOver and
shows the live voltage/target RPM summary.
DataContext = BenchPageViewModel. Reads via Root.X / BenchControl.X.
-->
<StackPanel>
<!-- RPM — oversized, with target + voltage summary -->
<Border Style="{StaticResource LcdBlue}" Margin="0,0,0,4" Padding="10,6">
<StackPanel>
<Grid Height="86">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Root.BenchRpm, StringFormat=F0}"
FontSize="72" FontWeight="UltraBold" Foreground="#EBEBFF"
HorizontalAlignment="Right" VerticalAlignment="Center"
FontFamily="Consolas"/>
<TextBlock Grid.Column="1" Text="{DynamicResource Bench.Rpm}"
FontSize="20" Foreground="#FFFFEB6E"
VerticalAlignment="Center" Margin="6,0,0,0"/>
</Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<TextBlock Text="{DynamicResource Bench.Target}" Foreground="#EBEBFF" FontSize="11" Margin="0,0,4,0"/>
<TextBlock Text="{Binding BenchControl.TargetRpm, StringFormat=F0}"
Foreground="#FFFFEB6E" FontSize="11" FontFamily="Consolas" Margin="0,0,8,0"/>
<TextBlock Text="{DynamicResource Bench.Voltage}" Foreground="#EBEBFF" FontSize="11" Margin="0,0,4,0"/>
<TextBlock Text="{Binding BenchControl.CommandVoltage, StringFormat=F3}"
Foreground="#FFFFEB6E" FontSize="11" FontFamily="Consolas"/>
</StackPanel>
</StackPanel>
</Border>
<!-- Pressures + temperatures side by side -->
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource LcdBlue}" Margin="0,0,2,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{DynamicResource Bench.P1}" Grid.Row="0" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.P2}" Grid.Row="1" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.Pressure, StringFormat=F1}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="22" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.Pressure2, StringFormat=F1}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="22" FontFamily="Consolas"/>
<TextBlock Text="bar" Grid.Row="0" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="bar" Grid.Row="1" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
<Border Grid.Column="1" Style="{StaticResource LcdBlue}" Margin="2,0,0,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{DynamicResource Bench.TempIn}" Grid.Row="0" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.TempOut}" Grid.Row="1" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.Temp4}" Grid.Row="2" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.TempTank}" Grid.Row="3" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.TempIn, StringFormat=F1}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="18" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.TempOut, StringFormat=F1}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="18" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.Temp4, StringFormat=F1}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="18" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.BenchTemp, StringFormat=F1}" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="18" FontFamily="Consolas"/>
<TextBlock Text="°C" Grid.Row="0" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="°C" Grid.Row="1" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="°C" Grid.Row="2" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="°C" Grid.Row="3" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
</Grid>
<!-- Flow: QDelivery + QOver -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Style="{StaticResource LcdBlue}" Margin="0,0,2,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
<ColumnDefinition Width="56"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Bench.QDelivery}" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.QDelivery, StringFormat=F1}"
Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontFamily="Consolas"/>
<TextBlock Text="{DynamicResource Bench.CcStroke}" Grid.Column="2"
Foreground="#EBEBFF" FontSize="11" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
<Border Grid.Column="1" Style="{StaticResource LcdBlue}" Margin="2,0,0,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
<ColumnDefinition Width="56"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Bench.QOver}" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.QOver, StringFormat=F1}"
Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontFamily="Consolas"/>
<TextBlock Text="{DynamicResource Bench.CcStroke}" Grid.Column="2"
Foreground="#EBEBFF" FontSize="11" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,18 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// HMI-style bench readings panel (RPM, pressures, temperatures, Q flows)
/// rendered on the Bench page. Shares the <c>LcdBlue</c> style with the
/// dashboard equivalent but is wider and includes QDelivery + QOver.
/// </summary>
public partial class BenchReadingsView : UserControl
{
/// <summary>Initializes a new instance of the <see cref="BenchReadingsView"/> control.</summary>
public BenchReadingsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,158 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.DashboardConnectionView"
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:models="clr-namespace:HC_APTBS.Models"
mc:Ignorable="d"
d:DesignHeight="160" d:DesignWidth="220">
<!--
Connection status block for the Dashboard.
DataContext is DashboardPageViewModel; pills read from Root.X.
Pill uses the shared ConnIndicator style. Gray = offline, green = live, red = K-Line failed.
-->
<StackPanel>
<TextBlock Text="{DynamicResource Dashboard.Connections}"
FontSize="13" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,6"/>
<!-- CAN bus -->
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Dashboard.Conn.Can}" VerticalAlignment="Center" FontSize="12"/>
<Border Grid.Column="1" MinWidth="72" Height="22">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsCanConnected}" Value="True">
<Setter Property="Background" Value="#26C200"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10" FontWeight="SemiBold" Foreground="White"
Text="{DynamicResource Dashboard.StateOnline}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOffline}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsCanConnected}" Value="True">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOnline}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</Grid>
<!-- Bench liveness -->
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Dashboard.Conn.Bench}" VerticalAlignment="Center" FontSize="12"/>
<Border Grid.Column="1" MinWidth="72" Height="22">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsBenchConnected}" Value="True">
<Setter Property="Background" Value="#26C200"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10" FontWeight="SemiBold" Foreground="White">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOffline}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsBenchConnected}" Value="True">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOnline}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</Grid>
<!-- Pump liveness -->
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Dashboard.Conn.Pump}" VerticalAlignment="Center" FontSize="12"/>
<Border Grid.Column="1" MinWidth="72" Height="22">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsPumpConnected}" Value="True">
<Setter Property="Background" Value="#26C200"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10" FontWeight="SemiBold" Foreground="White">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOffline}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Root.IsPumpConnected}" Value="True">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOnline}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</Grid>
<!-- K-Line session -->
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Dashboard.Conn.KLine}" VerticalAlignment="Center" FontSize="12"/>
<Border Grid.Column="1" MinWidth="72" Height="22">
<Border.Style>
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
<Style.Triggers>
<DataTrigger Binding="{Binding Root.KLineState}" Value="{x:Static models:KLineConnectionState.Connected}">
<Setter Property="Background" Value="#26C200"/>
</DataTrigger>
<DataTrigger Binding="{Binding Root.KLineState}" Value="{x:Static models:KLineConnectionState.Failed}">
<Setter Property="Background" Value="#FF3333"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="10" FontWeight="SemiBold" Foreground="White">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateClosed}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Root.KLineState}" Value="{x:Static models:KLineConnectionState.Connected}">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateOpen}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Root.KLineState}" Value="{x:Static models:KLineConnectionState.Failed}">
<Setter Property="Text" Value="{DynamicResource Dashboard.StateFailed}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Connection/liveness status pills for the Dashboard page.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.Pages.DashboardPageViewModel"/>.
/// </summary>
public partial class DashboardConnectionView : UserControl
{
public DashboardConnectionView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,103 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.DashboardReadingsView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="320">
<!--
Dashboard-only compact LCD panel.
DataContext is DashboardPageViewModel; binds via Root.X to MainViewModel live readings.
Read-first: no controls, no popups.
-->
<StackPanel>
<!-- RPM: oversized single-line read -->
<Border Style="{StaticResource LcdBlue}" Margin="0,0,0,4" Padding="10,6">
<Grid Height="76">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Root.BenchRpm, StringFormat=F0}"
FontSize="60" FontWeight="UltraBold" Foreground="#EBEBFF"
HorizontalAlignment="Right" VerticalAlignment="Center"
FontFamily="Consolas"/>
<TextBlock Grid.Column="1" Text="{DynamicResource Bench.Rpm}"
FontSize="18" Foreground="#FFFFEB6E"
VerticalAlignment="Center" Margin="6,0,0,0"/>
</Grid>
</Border>
<!-- Temperatures + Pressures side by side -->
<Grid Margin="0,0,0,4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Pressures -->
<Border Grid.Column="0" Style="{StaticResource LcdBlue}" Margin="0,0,2,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{DynamicResource Bench.P1}" Grid.Row="0" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.P2}" Grid.Row="1" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.Pressure, StringFormat=F1}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="20" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.Pressure2, StringFormat=F1}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="20" FontFamily="Consolas"/>
<TextBlock Text="bar" Grid.Row="0" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="bar" Grid.Row="1" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
<!-- Temperatures -->
<Border Grid.Column="1" Style="{StaticResource LcdBlue}" Margin="2,0,0,0" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{DynamicResource Bench.TempIn}" Grid.Row="0" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.TempOut}" Grid.Row="1" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.TempTank}" Grid.Row="2" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.TempIn, StringFormat=F1}" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="20" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.TempOut, StringFormat=F1}" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="20" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.BenchTemp, StringFormat=F1}" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right" Foreground="#EBEBFF" FontSize="20" FontFamily="Consolas"/>
<TextBlock Text="°C" Grid.Row="0" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="°C" Grid.Row="1" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
<TextBlock Text="°C" Grid.Row="2" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
</Grid>
<!-- Flow: Q-Delivery -->
<Border Style="{StaticResource LcdBlue}" Padding="8,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition/>
<ColumnDefinition Width="46"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource Bench.QDelivery}" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.QDelivery, StringFormat=F1}"
Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontFamily="Consolas"/>
<TextBlock Text="cc/s" Grid.Column="2" Foreground="#EBEBFF" FontSize="13" VerticalAlignment="Center" Margin="4,0"/>
</Grid>
</Border>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Compact read-only LCD panel for the Dashboard page.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.Pages.DashboardPageViewModel"/>.
/// </summary>
public partial class DashboardReadingsView : UserControl
{
public DashboardReadingsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,94 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.DtcListView"
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"
mc:Ignorable="d"
d:DesignHeight="360" d:DesignWidth="700">
<!--
Pump page §3.b DTC list. DataContext = DtcListViewModel.
Moves the fault-code surface out of the one-shot KlineErrorsDialog
into a proper sub-section with read / clear actions and a row list.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Border Background="#FAFAFA" BorderBrush="#DDD" BorderThickness="1"
CornerRadius="4" Padding="12" Margin="6">
<DockPanel>
<!-- ── Header row: title + actions ─────────────────────────── -->
<Grid DockPanel.Dock="Top" Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource PumpSub.Dtcs}"
FontSize="15" FontWeight="SemiBold" Foreground="#333"
VerticalAlignment="Center"/>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<Button Content="{DynamicResource Dtc.Read}"
Command="{Binding ReadCommand}"
MinWidth="90" Height="28" Margin="0,0,6,0"
FontWeight="Bold"/>
<Button Content="{DynamicResource Dtc.Clear}"
Command="{Binding ClearCommand}"
MinWidth="90" Height="28"/>
</StackPanel>
</Grid>
<!-- ── Status line ─────────────────────────────────────────── -->
<TextBlock DockPanel.Dock="Top"
Text="{Binding StatusText}"
Foreground="#666" FontSize="11" Margin="0,0,0,6"/>
<!-- ── Busy indicator ──────────────────────────────────────── -->
<ProgressBar DockPanel.Dock="Top"
IsIndeterminate="True" Height="3" Margin="0,0,0,6"
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
<!-- ── "No faults" banner ──────────────────────────────────── -->
<Border DockPanel.Dock="Top"
Background="#26C200" CornerRadius="3" Padding="10,6"
HorizontalAlignment="Left"
Visibility="{Binding IsClear, Converter={StaticResource BoolToVis}}">
<TextBlock Text="{DynamicResource Dtc.NoFaults}"
Foreground="White" FontWeight="Bold" FontSize="12"/>
</Border>
<!-- ── DTC rows ────────────────────────────────────────────── -->
<ListBox ItemsSource="{Binding Codes}"
BorderThickness="0" Background="Transparent">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="#FDECEA" BorderBrush="#D62828"
BorderThickness="0,0,0,1" Padding="8,6" Margin="0,2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Code}"
FontFamily="Consolas" FontWeight="Bold"
FontSize="13" Foreground="#B22222"
VerticalAlignment="Center"/>
<TextBlock Grid.Column="1"
Text="{Binding Description}"
FontSize="12" Foreground="#333"
TextWrapping="Wrap"
VerticalAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Border>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Pump page §3.b DTC list view. DataContext is
/// <c>DtcListViewModel</c>.
/// </summary>
public partial class DtcListView : UserControl
{
public DtcListView()
{
InitializeComponent();
}
}
}

View File

@@ -1,7 +1,8 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.FlowmeterChartView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF">
xmlns:lvc="clr-namespace:LiveChartsCore.SkiaSharpView.WPF;assembly=LiveChartsCore.SkiaSharpView.WPF"
>
<Grid>
<Grid.RowDefinitions>
@@ -23,6 +24,7 @@
YAxes="{Binding YAxes}"
Sections="{Binding Sections}"
TooltipPosition="Hidden"
AnimationsSpeed="00:00:00"/>
AnimationsSpeed="00:00:00"
/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,90 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.GraphicIndicatorView"
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:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:GraphicIndicatorViewModel, IsDesignTimeCreatable=False}">
<!--
Vertical progress bar showing a single measurement value against its
expected value and tolerance band. Shared across phase cards and the
Running view's live measurement table.
-->
<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>
<TextBlock Text="{Binding MaxBound, StringFormat=F1}"
FontSize="9" Foreground="Gray"
HorizontalAlignment="Center" Margin="0,0,0,1"/>
<Grid Grid.Row="1">
<Border BorderBrush="Black" BorderThickness="1" SnapsToDevicePixels="True"/>
<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>
<Canvas ClipToBounds="True">
<Line X1="0" X2="58" StrokeDashArray="3,2"
Stroke="LightGray" StrokeThickness="1"
Canvas.Top="18"/>
<Line X1="0" X2="58" StrokeDashArray="3,2"
Stroke="LightGray" StrokeThickness="1"
Canvas.Top="72"/>
</Canvas>
<TextBlock Text="{Binding ExpectedValue, StringFormat=F1}"
FontSize="9" Foreground="#999999"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="0,-14,0,0"/>
<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>
<TextBlock Grid.Row="2"
Text="{Binding MinBound, StringFormat=F1}"
FontSize="9" Foreground="Gray"
HorizontalAlignment="Center" Margin="0,1,0,0"/>
<TextBlock Grid.Row="3"
Text="{Binding ParameterName}"
FontSize="9" FontWeight="SemiBold"
HorizontalAlignment="Center"
TextTrimming="CharacterEllipsis"
ToolTip="{Binding ParameterName}"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Vertical progress bar indicator for a single measurement value with tolerance band.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.GraphicIndicatorViewModel"/>.
/// </summary>
public partial class GraphicIndicatorView : UserControl
{
public GraphicIndicatorView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,47 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.InterlockBannerView"
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"
mc:Ignorable="d"
d:DesignHeight="40" d:DesignWidth="800">
<!--
Soft safety interlock banner. Visible only when
InterlockBannerViewModel.IsVisible is true.
Colour switches from warning amber to critical red.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Border CornerRadius="3" Padding="10,6" Margin="0,4"
Visibility="{Binding IsVisible, Converter={StaticResource BoolToVis}}">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#FFB020"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsCritical}" Value="True">
<Setter Property="Background" Value="#D62828"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="⚠" Foreground="White" FontSize="16"
FontWeight="Bold" VerticalAlignment="Center" Margin="0,0,8,0"/>
<TextBlock Grid.Column="1" Text="{Binding Message}"
Foreground="White" FontWeight="SemiBold" FontSize="12"
VerticalAlignment="Center" TextWrapping="Wrap"/>
<Button Grid.Column="2" Content="{DynamicResource Common.Close}"
Command="{Binding DismissCommand}"
Foreground="White" Background="Transparent" BorderBrush="White"
BorderThickness="1" Padding="8,2" FontSize="11"
VerticalAlignment="Center"/>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,18 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Soft safety interlock banner rendered on the Bench page.
/// Shows when oil-pump interlock or RPM-over-limit conditions are active;
/// the operator can dismiss it until the condition changes.
/// </summary>
public partial class InterlockBannerView : UserControl
{
/// <summary>Initializes a new instance of the <see cref="InterlockBannerView"/> control.</summary>
public InterlockBannerView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,126 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.PhaseCardView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
xmlns:vm="clr-namespace:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:PhaseCardViewModel, IsDesignTimeCreatable=False}">
<!--
Single phase card. DataContext: PhaseCardViewModel.
Shared between TestPlanView (shows ReadyValues/OperationValues and enable toggle)
and TestRunningView (shows live ResultIndicators and pass/fail colour).
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<!-- Simple Name: Value pair used by ReadyValues and OperationValues -->
<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>
<!-- Vertical progress bar indicator (wraps GraphicIndicatorView) -->
<DataTemplate DataType="{x:Type vm:GraphicIndicatorViewModel}">
<uc:GraphicIndicatorView/>
</DataTemplate>
</UserControl.Resources>
<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 -->
<CheckBox IsChecked="{Binding IsEnabled}"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="0,0,0,0" ToolTip="Enable/disable this phase"/>
<StackPanel Margin="0,2,0,0">
<TextBlock Text="{Binding Name}"
FontSize="11" FontWeight="SemiBold"
HorizontalAlignment="Center"
TextWrapping="Wrap"
Margin="16,0,0,0"/>
<TextBlock Text="{DynamicResource Test.Critical}" FontSize="9"
Foreground="#E65100" FontWeight="Bold"
HorizontalAlignment="Center"
Visibility="{Binding IsCritical, Converter={StaticResource BoolToVis}}"/>
<StackPanel Visibility="{Binding ShowOperationValues, Converter={StaticResource BoolToVis}}"
Margin="0,3,0,0">
<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>
<TextBlock Text="{DynamicResource Test.TestLabel}" FontSize="9" Foreground="#666"
FontWeight="SemiBold" Margin="0,2,0,0"/>
<ItemsControl ItemsSource="{Binding OperationValues}"/>
</StackPanel>
<ItemsControl ItemsSource="{Binding ResultIndicators}" Margin="0,4,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<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>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Single phase card (critical / operation values / graphic indicators / result).
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.PhaseCardViewModel"/>.
/// </summary>
public partial class PhaseCardView : UserControl
{
public PhaseCardView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,165 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.PumpIdentificationPanelView"
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"
mc:Ignorable="d"
d:DesignHeight="360" d:DesignWidth="700">
<!--
Pump-page variant of PumpIdentificationView (spec §3.a Identification).
Reuses PumpIdentificationViewModel. Presents the ECU info panel
in a larger, two-column format suitable for the Pump page.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<Style x:Key="IdLabel" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="Foreground" Value="#555"/>
<Setter Property="Width" Value="110"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<Style x:Key="IdValue" TargetType="TextBlock">
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontFamily" Value="Consolas"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
</Style>
</UserControl.Resources>
<Border Background="#FAFAFA" BorderBrush="#DDD" BorderThickness="1"
CornerRadius="4" Padding="12" Margin="6">
<StackPanel>
<!-- ── Section title ────────────────────────────────────────── -->
<TextBlock Text="{DynamicResource PumpSub.Identification}"
FontSize="15" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,8"/>
<!-- ── Pump selector row ────────────────────────────────────── -->
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{DynamicResource PumpId.Label}"
VerticalAlignment="Center" FontSize="13"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding PumpIds}"
SelectedItem="{Binding SelectedPumpId}"
FontSize="18" Margin="0,0,12,0"
VerticalContentAlignment="Center"/>
<TextBlock Grid.Column="2"
Text="{Binding CurrentPump.Model}"
FontSize="13" Foreground="#888"
VerticalAlignment="Center"/>
</Grid>
<!-- ── Action row: Read + Disconnect + progress ─────────────── -->
<Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{DynamicResource PumpId.ReadKLine}"
Command="{Binding ReadKlineCommand}"
MinWidth="130" Height="34" FontWeight="Bold"
Margin="0,0,8,0"/>
<Button Grid.Column="1"
Content="{DynamicResource PumpId.Disconnect}"
Command="{Binding DisconnectKLineCommand}"
MinWidth="110" Height="34"
Margin="0,0,12,0"/>
<StackPanel Grid.Column="2"
Orientation="Vertical"
VerticalAlignment="Center"
Visibility="{Binding IsReading, Converter={StaticResource BoolToVis}}">
<TextBlock Text="{Binding ProgressMessage}"
FontSize="11" Foreground="#666"
TextTrimming="CharacterEllipsis"/>
<ProgressBar Value="{Binding ProgressPercent, Mode=OneWay}"
Minimum="0" Maximum="100" Height="8" Margin="0,2,0,0"/>
</StackPanel>
</Grid>
<!-- ── Two-column ECU info grid ─────────────────────────────── -->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Left column -->
<StackPanel Grid.Column="0">
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.PumpId}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlinePumpId}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.SerialNo}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineSerialNumber}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.ModelRef}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineModelRef}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.ModelIndex}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineModelIndex}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.DataRecord}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineDataRecord}" Style="{StaticResource IdValue}"/>
</StackPanel>
</StackPanel>
<!-- Right column -->
<StackPanel Grid.Column="2">
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.SwVer1}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineSwVersion1}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.SwVer2}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineSwVersion2}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.PumpCtrl}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlinePumpControl}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.Dfi}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineDfi}" Style="{StaticResource IdValue}"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,2">
<TextBlock Text="{DynamicResource PumpId.Errors}" Style="{StaticResource IdLabel}"/>
<TextBlock Text="{Binding KlineErrors}" Style="{StaticResource IdValue}" Foreground="DarkRed"/>
</StackPanel>
</StackPanel>
</Grid>
<!-- ── Connection error footer (auto-collapses) ─────────────── -->
<Border Background="#FDECEA" BorderBrush="#D62828" BorderThickness="1"
CornerRadius="3" Padding="8,4" Margin="0,10,0,0">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<DataTrigger Binding="{Binding KlineConnectError}" Value="">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding KlineConnectError}"
FontSize="12" FontFamily="Consolas" Foreground="#B22222"
TextWrapping="Wrap"/>
</Border>
</StackPanel>
</Border>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Larger page-scoped variant of <see cref="PumpIdentificationView"/> used by the
/// Pump page §3.a Identification sub-section. DataContext is
/// <c>PumpIdentificationViewModel</c>.
/// </summary>
public partial class PumpIdentificationPanelView : UserControl
{
public PumpIdentificationPanelView()
{
InitializeComponent();
}
}
}

View File

@@ -12,14 +12,14 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{DynamicResource PumpId.Label}" VerticalAlignment="Center" Margin="0,0,8,0" FontSize="14"/>
<ComboBox Grid.Column="1"
ItemsSource="{Binding PumpIds}"
SelectedItem="{Binding SelectedPumpId}"
FontSize="14" VerticalAlignment="Center"/>
FontSize="36" VerticalAlignment="Center"/>
<TextBlock Grid.Column="2" Margin="8,0,0,0"
Text="{Binding CurrentPump.Model}" FontSize="12"
Foreground="Gray" VerticalAlignment="Center"/>

View File

@@ -0,0 +1,130 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.PumpLiveDataView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
mc:Ignorable="d"
d:DesignHeight="380" d:DesignWidth="700">
<!--
Pump page §3.c Live Data. DataContext = PumpPageViewModel so the view
can reach the MainViewModel-owned live readings via {Binding Root.X}
and the two StatusDisplay VMs.
-->
<Border Background="#FAFAFA" BorderBrush="#DDD" BorderThickness="1"
CornerRadius="4" Padding="12" Margin="6">
<StackPanel>
<!-- ── Section title ─────────────────────────────────────────── -->
<TextBlock Text="{DynamicResource PumpSub.LiveData}"
FontSize="15" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,8"/>
<!-- ── LCD-style readings block ──────────────────────────────── -->
<Border Style="{StaticResource LcdBlue}" Padding="10,6">
<Grid Height="110">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="90"/>
<ColumnDefinition/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition/>
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Left column: T-hyb / RPM / T-ein -->
<TextBlock Text="{DynamicResource Pump.THyb}"
VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Pump.Rpm}"
Grid.Row="1" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Pump.TEin}"
Grid.Row="2" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.PumpTemp, StringFormat=F2}"
Grid.Column="1"
HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontWeight="Bold" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.PumpRpm, StringFormat=F0}"
Grid.Column="1" Grid.Row="1"
HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontWeight="Bold" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.PumpTein, StringFormat=F0}"
Grid.Column="1" Grid.Row="2"
HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontWeight="Bold" FontFamily="Consolas"/>
<TextBlock Text="°C" Grid.Column="2"
VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="12" Margin="4,0"/>
<TextBlock Text="{DynamicResource Pump.UnitRpm}"
Grid.Column="2" Grid.Row="1"
VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="12" Margin="4,0"/>
<TextBlock Text="µs" Grid.Column="2" Grid.Row="2"
VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="12" Margin="4,0"/>
<!-- Right column: ME / FBKW -->
<TextBlock Text="{DynamicResource Bench.PumpMe}"
Grid.Column="4" VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{DynamicResource Bench.PumpFbkw}"
Grid.Column="4" Grid.Row="1"
VerticalAlignment="Center" Foreground="#EBEBFF" FontSize="13"/>
<TextBlock Text="{Binding Root.PumpMe, StringFormat=F2}"
Grid.Column="5"
HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontWeight="Bold" FontFamily="Consolas"/>
<TextBlock Text="{Binding Root.PumpFbkw, StringFormat=F2}"
Grid.Column="5" Grid.Row="1"
HorizontalAlignment="Right" VerticalAlignment="Center"
Foreground="#EBEBFF" FontSize="22" FontWeight="Bold" FontFamily="Consolas"/>
</Grid>
</Border>
<!-- ── Status displays (Status word + Empf3) ─────────────────── -->
<StackPanel Margin="0,8,0,0">
<uc:StatusDisplayView DataContext="{Binding StatusDisplay1}"/>
<uc:StatusDisplayView DataContext="{Binding StatusDisplay2}" Margin="0,4,0,0"/>
</StackPanel>
<!-- ── Engineering expander (raw values) ─────────────────────── -->
<Expander Header="{DynamicResource PumpLive.Engineering}"
IsExpanded="False" Margin="0,10,0,0" FontSize="12">
<Border Background="#1E1E1E" Padding="10,6" CornerRadius="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="PumpRpm = "/><Run Text="{Binding Root.PumpRpm, StringFormat=F2, Mode=OneWay}"/>
</TextBlock>
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="PumpTemp = "/><Run Text="{Binding Root.PumpTemp, StringFormat=F2, Mode=OneWay}"/>
</TextBlock>
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="PumpMe = "/><Run Text="{Binding Root.PumpMe, StringFormat=F3, Mode=OneWay}"/>
</TextBlock>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="PumpFbkw = "/><Run Text="{Binding Root.PumpFbkw, StringFormat=F3, Mode=OneWay}"/>
</TextBlock>
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="PumpTein = "/><Run Text="{Binding Root.PumpTein, StringFormat=F0, Mode=OneWay}"/>
</TextBlock>
<TextBlock Foreground="#9CDCFE" FontFamily="Consolas" FontSize="11">
<Run Text="KLineState = "/><Run Text="{Binding Root.KLineState, Mode=OneWay}"/>
</TextBlock>
</StackPanel>
</Grid>
</Border>
</Expander>
</StackPanel>
</Border>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Pump page §3.c Live Data view: pump CAN readings, status-word displays,
/// and a collapsible engineering panel. DataContext is
/// <c>PumpPageViewModel</c> (reaches MainViewModel via <c>Root</c>).
/// </summary>
public partial class PumpLiveDataView : UserControl
{
public PumpLiveDataView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,49 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.RelayBankView"
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"
mc:Ignorable="d"
d:DesignHeight="220" d:DesignWidth="180">
<!--
Curated bank of auxiliary relay toggles.
DataContext = RelayBankViewModel.
-->
<UserControl.Resources>
<Style x:Key="RelayToggle" TargetType="ToggleButton">
<Setter Property="Height" Value="28"/>
<Setter Property="FontSize" Value="11"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Margin" Value="0,2,0,0"/>
<Setter Property="Content" Value="{DynamicResource Bench.RelayOff}"/>
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOn}"/>
<Setter Property="Background" Value="#26C200"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Border Background="#FAFAFA" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4" Padding="8">
<StackPanel>
<TextBlock Text="{DynamicResource Bench.Relays}"
FontSize="13" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,6"/>
<TextBlock Text="{DynamicResource Bench.Electronic}" FontSize="10" Foreground="DimGray"/>
<ToggleButton Style="{StaticResource RelayToggle}"
IsChecked="{Binding IsElectronicOn}"/>
<TextBlock Text="{DynamicResource Bench.Flasher}" FontSize="10" Foreground="DimGray" Margin="0,8,0,0"/>
<ToggleButton Style="{StaticResource RelayToggle}"
IsChecked="{Binding IsFlasherOn}"/>
<TextBlock Text="{DynamicResource Bench.Pulse4Signal}" FontSize="10" Foreground="DimGray" Margin="0,8,0,0"/>
<ToggleButton Style="{StaticResource RelayToggle}"
IsChecked="{Binding IsPulse4SignalOn}"/>
</StackPanel>
</Border>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Curated bank of auxiliary relay toggles rendered on the Bench page.
/// Hosts Electronic, Flasher, and Pulse4Signal toggles with state indication.
/// </summary>
public partial class RelayBankView : UserControl
{
/// <summary>Initializes a new instance of the <see cref="RelayBankView"/> control.</summary>
public RelayBankView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,147 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.ResultHistoryView"
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"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="320">
<!--
Session-only test-run history list.
DataContext: ResultsPageViewModel. Renders one row per CompletedTestRun in History.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Header -->
<TextBlock Grid.Row="0" Text="{DynamicResource Results.HistoryHeader}"
FontSize="13" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,6"/>
<!-- Empty-state prompt -->
<Border Grid.Row="1"
Visibility="{Binding IsHistoryEmpty, Converter={StaticResource BoolToVis}}"
VerticalAlignment="Top" Padding="8" Margin="0,0,0,4"
Background="#F5F5F5" BorderBrush="#DDD" BorderThickness="1" CornerRadius="3">
<TextBlock Text="{DynamicResource Results.EmptyState}"
FontSize="12" Foreground="#777" FontStyle="Italic"
TextWrapping="Wrap"/>
</Border>
<!-- Entries list -->
<ListBox Grid.Row="1"
ItemsSource="{Binding History}"
SelectedItem="{Binding SelectedRun, Mode=TwoWay}"
BorderThickness="0"
Background="Transparent"
HorizontalContentAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
<Setter Property="Margin" Value="0,0,0,4"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border Padding="8,6" CornerRadius="3" BorderThickness="1" BorderBrush="#DDD"
Background="#FAFAFA">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Timestamp -->
<TextBlock Grid.Row="0" Grid.Column="0"
Text="{Binding CompletedAt, StringFormat='{}{0:dd/MM HH:mm:ss}'}"
FontFamily="Consolas" FontSize="11" Foreground="#555"/>
<!-- PASS / FAIL / INTERRUPTED pill -->
<Border Grid.Row="0" Grid.Column="1" Padding="5,1" CornerRadius="2"
Margin="4,0,0,0" VerticalAlignment="Center">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#B71C1C"/>
<Style.Triggers>
<DataTrigger Binding="{Binding OverallPassed}" Value="True">
<Setter Property="Background" Value="#2E7D32"/>
</DataTrigger>
<DataTrigger Binding="{Binding Interrupted}" Value="True">
<Setter Property="Background" Value="#FFB020"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Foreground="White" FontSize="10" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{DynamicResource Common.Fail}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding OverallPassed}" Value="True">
<Setter Property="Text" Value="{DynamicResource Common.Pass}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Interrupted}" Value="True">
<Setter Property="Text" Value="{DynamicResource Results.InterruptedLabel}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<!-- Delete (×) button -->
<Button Grid.Row="0" Grid.Column="2"
Content="&#x2715;"
Width="20" Height="20" Margin="4,0,0,0" Padding="0"
FontSize="11" FontWeight="Bold" Foreground="#999"
Background="Transparent" BorderThickness="0"
Cursor="Hand"
ToolTip="{DynamicResource Results.DeleteTooltip}"
Command="{Binding DataContext.RemoveEntryCommand,
RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding Id}"/>
<!-- Pump identity -->
<TextBlock Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3"
FontSize="12" FontWeight="SemiBold" Margin="0,2,0,0"
TextTrimming="CharacterEllipsis">
<Run Text="{Binding PumpModel, Mode=OneWay}"/>
<Run Text=" · "/>
<Run Text="{Binding PumpSerial, Mode=OneWay}"/>
</TextBlock>
<!-- Test names -->
<TextBlock Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"
Text="{Binding TestNames}"
FontSize="11" Foreground="#666" Margin="0,2,0,0"
TextTrimming="CharacterEllipsis"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- Clear session -->
<Button Grid.Row="2"
Content="{DynamicResource Results.ClearSessionButton}"
Command="{Binding ClearHistoryCommand}"
Margin="0,6,0,0" Padding="6,3" HorizontalAlignment="Right"
FontSize="11"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Session-only test-run history list for the Results page.
/// DataContext must be a <see cref="ViewModels.Pages.ResultsPageViewModel"/>.
/// </summary>
public partial class ResultHistoryView : UserControl
{
public ResultHistoryView() => InitializeComponent();
}
}

View File

@@ -41,13 +41,13 @@
</Grid.RowDefinitions>
<Rectangle Fill="{Binding Color, Converter={StaticResource HexToBrush}}"
Stroke="#5D5D5D" StrokeThickness="1"
Width="12" Height="12"
Width="16" Height="10"
HorizontalAlignment="Center" VerticalAlignment="Center"
ToolTip="{Binding Description}"
ToolTipService.InitialShowDelay="150"
ToolTipService.ShowDuration="30000"
SnapsToDevicePixels="True"/>
<TextBlock Grid.Row="1" FontSize="8"
<TextBlock Grid.Row="1" FontSize="12"
Text="{Binding Index}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="DimGray"/>

View File

@@ -0,0 +1,103 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TemperatureControlView"
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"
mc:Ignorable="d"
d:DesignHeight="230" d:DesignWidth="190">
<!--
Temperature control panel: PID setpoint + heater / cooler relay toggles.
DataContext = TemperatureControlViewModel.
-->
<Border Background="#FAFAFA" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4" Padding="8">
<StackPanel>
<TextBlock Text="{DynamicResource Bench.TempControl}"
FontSize="13" FontWeight="SemiBold" Foreground="#333"
Margin="0,0,0,6"/>
<!-- PID setpoint -->
<TextBlock Text="{DynamicResource Bench.Setpoint}" FontSize="10" Foreground="DimGray"/>
<Grid Margin="0,2,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding SetpointText, UpdateSourceTrigger=PropertyChanged}"
FontSize="16" FontFamily="Consolas" Height="26"
VerticalContentAlignment="Center"/>
<TextBlock Grid.Column="1" Text="°C" VerticalAlignment="Center"
Margin="6,0,0,0" Foreground="#555"/>
</Grid>
<TextBlock Text="{DynamicResource Bench.Tolerance}" FontSize="10" Foreground="DimGray"/>
<Grid Margin="0,2,0,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding ToleranceText, UpdateSourceTrigger=PropertyChanged}"
FontSize="13" FontFamily="Consolas" Height="22"
VerticalContentAlignment="Center"/>
<TextBlock Grid.Column="1" Text="± °C" VerticalAlignment="Center"
Margin="6,0,0,0" Foreground="#555"/>
</Grid>
<Button Content="{DynamicResource Bench.ApplySetpoint}" Height="26" FontSize="11"
FontWeight="SemiBold"
Command="{Binding ApplySetpointCommand}"/>
<!-- Heater / Cooler toggles -->
<TextBlock Text="{DynamicResource Bench.DepositHeater}" FontSize="10" Foreground="DimGray" Margin="0,10,0,2"/>
<ToggleButton IsChecked="{Binding IsHeaterOn}" Height="28" FontSize="11" FontWeight="SemiBold">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOff}"/>
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOn}"/>
<Setter Property="Background" Value="#FFB020"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<TextBlock Text="{DynamicResource Bench.DepositCooler}" FontSize="10" Foreground="DimGray" Margin="0,6,0,2"/>
<ToggleButton IsChecked="{Binding IsDepositCoolerOn}" Height="28" FontSize="11" FontWeight="SemiBold">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOff}"/>
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOn}"/>
<Setter Property="Background" Value="#4080FF"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
<TextBlock Text="{DynamicResource Bench.TinCooler}" FontSize="10" Foreground="DimGray" Margin="0,6,0,2"/>
<ToggleButton IsChecked="{Binding IsTinCoolerOn}" Height="28" FontSize="11" FontWeight="SemiBold">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOff}"/>
<Setter Property="Background" Value="LightGray"/>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource Bench.RelayOn}"/>
<Setter Property="Background" Value="#26C200"/>
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
</StackPanel>
</Border>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Bench temperature control panel: PID setpoint input and
/// heater / deposit cooler / T-in cooler relay toggles.
/// </summary>
public partial class TemperatureControlView : UserControl
{
/// <summary>Initializes a new instance of the <see cref="TemperatureControlView"/> control.</summary>
public TemperatureControlView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,91 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TestDoneView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
xmlns:vmp="clr-namespace:HC_APTBS.ViewModels.Pages"
mc:Ignorable="d"
d:DesignHeight="560" d:DesignWidth="900"
Background="#FFEDEDED" Foreground="Black"
d:DataContext="{d:DesignInstance Type=vmp:TestsPageViewModel, IsDesignTimeCreatable=False}">
<!--
Done step of the Tests wizard. DataContext: TestsPageViewModel.
Shows PASS/FAIL banner, embedded ResultDisplayView, and navigation buttons.
-->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Overall pass/fail banner -->
<Border Padding="16,12" Margin="0,0,0,4">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#FFCDD2"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ResultDisplay.OverallPassed}" Value="True">
<Setter Property="Background" Value="#C8E6C9"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="26" FontWeight="Bold" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#B71C1C"/>
<Setter Property="Text" Value="{DynamicResource Test.Done.Failed}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ResultDisplay.OverallPassed}" Value="True">
<Setter Property="Foreground" Value="#2E7D32"/>
<Setter Property="Text" Value="{DynamicResource Test.Done.Passed}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="{Binding ResultDisplay.TestName}"
FontSize="14" FontStyle="Italic"
Foreground="#555"
VerticalAlignment="Bottom"
Margin="16,0,0,4"/>
</StackPanel>
</Border>
<!-- Inline results table -->
<uc:ResultDisplayView Grid.Row="1"
DataContext="{Binding ResultDisplay}"
Margin="4"/>
<!-- Action row -->
<Border Grid.Row="2" Padding="10,8"
BorderBrush="#DDD" BorderThickness="0,1,0,0"
Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0"
Content="{DynamicResource Test.Done.ViewFullResults}"
Command="{Binding ViewFullResultsCommand}"
Padding="14,6" FontSize="12"/>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<Button Content="{DynamicResource Test.Report}"
Command="{Binding Root.GenerateReportCommand}"
Padding="14,6" FontSize="12" Margin="0,0,8,0"/>
<Button Content="{DynamicResource Test.Done.RunAgain}"
Command="{Binding RunAgainCommand}"
Padding="18,6" FontSize="14" FontWeight="Bold"
Background="#1565C0" Foreground="White" BorderThickness="0"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Done step of the Tests wizard — PASS/FAIL banner, results table, Run Again.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.Pages.TestsPageViewModel"/>.
/// </summary>
public partial class TestDoneView : UserControl
{
public TestDoneView()
{
InitializeComponent();
}
}
}

View File

@@ -1,390 +0,0 @@
<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>

View File

@@ -1,17 +0,0 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Interaction logic for TestPanelView.xaml.
/// All logic resides in <see cref="ViewModels.TestPanelViewModel"/>.
/// </summary>
public partial class TestPanelView : UserControl
{
/// <summary>Initialises a new instance of the <see cref="TestPanelView"/> class.</summary>
public TestPanelView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,64 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TestPlanView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
xmlns:vm="clr-namespace:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DesignHeight="520" d:DesignWidth="900"
Background="#FFEDEDED" Foreground="Black"
d:DataContext="{d:DesignInstance Type=vm:TestPanelViewModel, IsDesignTimeCreatable=False}">
<!--
Plan step of the Tests wizard. DataContext: TestPanelViewModel.
The operator picks which test phases to run — no Start/Stop/Report buttons,
those live on the Preconditions step and in the wizard footer.
-->
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:TestSectionViewModel}">
<uc:TestSectionView/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- Toolbar -->
<Border 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>
<CheckBox IsChecked="{Binding ShowOperationValues}"
VerticalAlignment="Center">
<TextBlock Text="{DynamicResource Test.ShowValues}" FontSize="12"/>
</CheckBox>
<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>
<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>
<!-- Test sections -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Tests}" Margin="4"/>
</ScrollViewer>
</Grid>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Plan step of the Tests wizard — phase enable/disable and duration preview.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.TestPanelViewModel"/>.
/// </summary>
public partial class TestPlanView : UserControl
{
public TestPlanView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,173 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TestPreconditionsView"
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:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DesignHeight="520" d:DesignWidth="680"
d:DataContext="{d:DesignInstance Type=vm:TestPreconditionsViewModel, IsDesignTimeCreatable=False}">
<!--
Preconditions checklist (Tests page wizard step 4b).
DataContext: TestPreconditionsViewModel.
Rows auto-refresh as underlying properties change; Start button is disabled
until AllPassed is true.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Border Background="White" BorderBrush="#DDD" BorderThickness="1" CornerRadius="4" Padding="14">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Title -->
<TextBlock Grid.Row="0"
Text="{DynamicResource Test.Precheck.Title}"
FontSize="16" FontWeight="SemiBold" Foreground="#222"
Margin="0,0,0,10"/>
<!-- Auth gate (only when a required test has RequiresAuth=true) -->
<Border Grid.Row="1"
Background="#FFF8E1" BorderBrush="#F0C24A" BorderThickness="1"
CornerRadius="3" Padding="10,8" Margin="0,0,0,10"
Visibility="{Binding IsAuthRequired, Converter={StaticResource BoolToVis}}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" VerticalAlignment="Center">
<TextBlock Text="{DynamicResource Test.Precheck.AuthBanner}"
FontSize="12" FontWeight="SemiBold" Foreground="#7A5A00"/>
<TextBlock Text="{Binding TestAuth.AuthenticatedUser}"
FontSize="11" Foreground="#7A5A00"
Visibility="{Binding TestAuth.IsAuthenticated, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
<Button Grid.Column="1"
Content="{DynamicResource Test.Precheck.AuthButton}"
Command="{Binding TestAuth.AuthenticateCommand}"
Padding="10,4" FontSize="12"/>
</Grid>
</Border>
<!-- Checklist -->
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:PreconditionItemViewModel}">
<Border BorderBrush="#EEE" BorderThickness="0,0,0,1" Padding="0,8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="32"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Status glyph -->
<Border Grid.Column="0" Width="20" Height="20"
CornerRadius="10" VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="0,0,8,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#E74C3C"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSatisfied}" Value="True">
<Setter Property="Background" Value="#26C200"/>
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSatisfied}" Value="False"/>
<Condition Binding="{Binding IsRequired}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="#BBB"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
Foreground="White" FontSize="12" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="✕"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSatisfied}" Value="True">
<Setter Property="Text" Value="✓"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<!-- Label + remediation text -->
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock Text="{Binding Label}" FontSize="13" Foreground="#222"/>
<TextBlock Text="{Binding RemediationText}"
FontSize="11" Foreground="#888">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSatisfied}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
<!-- Fix-it button -->
<Button Grid.Column="2"
Content="{DynamicResource Test.Precheck.FixButton}"
Command="{Binding NavigateToFixCommand}"
Padding="8,3" FontSize="11" Margin="8,0,0,0"
Visibility="{Binding HasRemediation, Converter={StaticResource BoolToVis}}"/>
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
<!-- Start button row -->
<Grid Grid.Row="3" Margin="0,14,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" VerticalAlignment="Center"
FontSize="12" Foreground="#666"
Text="{DynamicResource Test.Precheck.NotReady}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding AllPassed}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Grid.Column="0" VerticalAlignment="Center"
FontSize="12" Foreground="#26C200" FontWeight="SemiBold"
Text="{DynamicResource Test.Precheck.Ready}"
Visibility="{Binding AllPassed, Converter={StaticResource BoolToVis}}"/>
<Button Grid.Column="1"
Content="{DynamicResource Test.StartTest}"
Command="{Binding StartTestCommand}"
Padding="18,6" FontSize="14" FontWeight="Bold"
Background="#26C200" Foreground="White" BorderThickness="0"/>
</Grid>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Preconditions checklist for the Tests page wizard (§4b).
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.TestPreconditionsViewModel"/>.
/// </summary>
public partial class TestPreconditionsView : UserControl
{
public TestPreconditionsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,159 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TestRunningView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
xmlns:vm="clr-namespace:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DesignHeight="640" d:DesignWidth="1000"
Background="#FFEDEDED" Foreground="Black"
d:DataContext="{d:DesignInstance Type=vm:TestPanelViewModel, IsDesignTimeCreatable=False}">
<!--
Running step of the Tests wizard. DataContext: TestPanelViewModel.
Shows the active phase countdown, live phase-card updates, and the flowmeter /
angle visuals. Pause / Retry-phase / Skip-phase buttons are rendered disabled —
their IBenchService wiring is deferred (see docs/gap-test-running-controls.md).
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
<DataTemplate DataType="{x:Type vm:TestSectionViewModel}">
<uc:TestSectionView/>
</DataTemplate>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- Phase header + countdown -->
<RowDefinition Height="*"/> <!-- Sections + live charts -->
<RowDefinition Height="Auto"/> <!-- Control row -->
</Grid.RowDefinitions>
<!-- Phase header + countdown + progress -->
<Border Padding="10,8" BorderBrush="#DDD" BorderThickness="0,0,0,1"
Background="White">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding CurrentPhaseName}"
FontSize="16" FontWeight="SemiBold"
Foreground="#222"/>
<StackPanel Orientation="Horizontal" Margin="0,1,0,0">
<TextBlock Text="{Binding SectionLabel}"
FontSize="11" FontStyle="Italic"
Foreground="#666"/>
<TextBlock Text="{Binding StatusText}"
FontSize="11" FontStyle="Italic"
Foreground="Gray" Margin="8,0,0,0"/>
</StackPanel>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal"
VerticalAlignment="Center">
<TextBlock Text="{Binding PhaseRemainingSeconds, Mode=OneWay}"
FontSize="32" FontFamily="Impact"
Foreground="#1565C0"
VerticalAlignment="Center"/>
<TextBlock Text="s" FontSize="18"
Foreground="#1565C0"
VerticalAlignment="Bottom" Margin="2,0,0,4"/>
<TextBlock Foreground="#999" FontSize="11"
VerticalAlignment="Bottom" Margin="8,0,0,4">
<Run Text="/ "/>
<Run Text="{Binding PhaseTotalSeconds, Mode=OneWay}"/>
<Run Text="s"/>
</TextBlock>
</StackPanel>
<ProgressBar Grid.Row="1" Grid.ColumnSpan="2"
Minimum="0" Maximum="1"
Value="{Binding PhaseProgress, Mode=OneWay}"
Height="6" Margin="0,6,0,0"
Background="#EEE" Foreground="#1565C0"
BorderThickness="0"/>
</Grid>
</Border>
<!-- Main body: test sections on left, live charts on right -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="420"/>
</Grid.ColumnDefinitions>
<!-- Live phase-card list (read-only during run, cards colour themselves) -->
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Tests}" Margin="4"/>
</ScrollViewer>
<!-- Charts + angle display -->
<Grid Grid.Column="1" Margin="4">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<uc:FlowmeterChartView Grid.Row="0"
DataContext="{Binding DataContext.FlowmeterChart.Delivery,
RelativeSource={RelativeSource AncestorType=Window}}"/>
<uc:FlowmeterChartView Grid.Row="1"
DataContext="{Binding DataContext.FlowmeterChart.Over,
RelativeSource={RelativeSource AncestorType=Window}}"
Margin="0,4,0,0"/>
<uc:AngleDisplayView Grid.Row="2"
DataContext="{Binding DataContext.AngleDisplay,
RelativeSource={RelativeSource AncestorType=Window}}"
Margin="0,4,0,0"/>
</Grid>
</Grid>
<!-- Control row -->
<Border Grid.Row="2" Padding="10,8"
BorderBrush="#DDD" BorderThickness="0,1,0,0"
Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" Grid.Column="0">
<Button Content="{DynamicResource Test.Running.Pause}"
IsEnabled="False"
ToolTip="{DynamicResource Test.Running.ComingSoon}"
Padding="12,5" FontSize="12" Margin="0,0,6,0"/>
<Button Content="{DynamicResource Test.Running.Retry}"
IsEnabled="False"
ToolTip="{DynamicResource Test.Running.ComingSoon}"
Padding="12,5" FontSize="12" Margin="0,0,6,0"/>
<Button Content="{DynamicResource Test.Running.Skip}"
IsEnabled="False"
ToolTip="{DynamicResource Test.Running.ComingSoon}"
Padding="12,5" FontSize="12"/>
</StackPanel>
<Button Grid.Column="2"
Content="{DynamicResource Test.Running.Abort}"
Command="{Binding DataContext.TestsPage.AbortCommand,
RelativeSource={RelativeSource AncestorType=Window}}"
Padding="18,6" FontSize="14" FontWeight="Bold"
Background="#C62828" Foreground="White" BorderThickness="0"/>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Running step of the Tests wizard — live phase progress, flowmeter charts, abort.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.TestPanelViewModel"/>.
/// </summary>
public partial class TestRunningView : UserControl
{
public TestRunningView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,91 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.TestSectionView"
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:uc="clr-namespace:HC_APTBS.Views.UserControls"
xmlns:vm="clr-namespace:HC_APTBS.ViewModels"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=vm:TestSectionViewModel, IsDesignTimeCreatable=False}">
<!--
Expander header + horizontal list of phase cards. DataContext: TestSectionViewModel.
Used by TestPlanView and TestRunningView — both show the same structure, differing
only in which card state is currently highlighted.
-->
<UserControl.Resources>
<DataTemplate DataType="{x:Type vm:PhaseCardViewModel}">
<uc:PhaseCardView/>
</DataTemplate>
</UserControl.Resources>
<Expander IsExpanded="{Binding IsExpanded}" Margin="0,2,0,0">
<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"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding TestName}"
FontSize="20" FontFamily="Impact" FontStyle="Italic"
VerticalAlignment="Center" Foreground="Black"
Padding="4,0"/>
<TextBlock Grid.Column="1"
Text="{Binding Description}"
FontSize="14" FontStyle="Italic" FontFamily="Impact"
VerticalAlignment="Bottom" Foreground="Gray"
Padding="8,0,0,3"/>
<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>
<CheckBox Grid.Column="3"
IsChecked="{Binding AllPhasesChecked}"
VerticalAlignment="Center" Margin="8,0"
ToolTip="Enable/disable all phases in this test"/>
</Grid>
</Border>
</Expander.Header>
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Disabled"
Padding="0,4">
<ItemsControl ItemsSource="{Binding Phases}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ScrollViewer>
</Expander>
</UserControl>

View File

@@ -0,0 +1,16 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// One test section — Expander header plus the horizontal row of phase cards.
/// DataContext is expected to be a <see cref="HC_APTBS.ViewModels.TestSectionViewModel"/>.
/// </summary>
public partial class TestSectionView : UserControl
{
public TestSectionView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,113 @@
<UserControl x:Class="HC_APTBS.Views.UserControls.UnlockPanelView"
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"
mc:Ignorable="d"
d:DesignHeight="380" d:DesignWidth="700">
<!--
Pump page §3.e Unlock inline view. DataContext = UnlockProgressViewModel
(exposed from MainViewModel.CurrentUnlockVm). Hidden by the parent when
the selected pump does not require unlock or the VM is null.
-->
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</UserControl.Resources>
<Border Background="#2B2929" BorderBrush="#111" BorderThickness="1"
CornerRadius="4" Padding="18" Margin="6">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <!-- title -->
<RowDefinition Height="Auto"/> <!-- type label -->
<RowDefinition Height="210"/> <!-- ring -->
<RowDefinition Height="Auto"/> <!-- phase text -->
<RowDefinition Height="Auto"/> <!-- progress bar -->
<RowDefinition Height="Auto"/> <!-- result -->
<RowDefinition Height="Auto"/> <!-- buttons -->
</Grid.RowDefinitions>
<TextBlock Grid.Row="0"
Text="{DynamicResource PumpSub.Unlock}"
FontSize="15" FontWeight="SemiBold" Foreground="#EEE"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock Grid.Row="1"
Text="{Binding UnlockTypeLabel, Mode=OneWay}"
FontSize="13" Foreground="#AAA"
HorizontalAlignment="Center" Margin="0,0,0,6"/>
<!-- Progress ring -->
<Grid Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center">
<Ellipse Width="200" Height="200"
Stroke="#4D4D4D" StrokeThickness="10"
Fill="Transparent"/>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{DynamicResource Dialog.Unlock.Progress}"
FontSize="12" Foreground="#888"
HorizontalAlignment="Center" Margin="0,0,0,4"/>
<TextBlock FontSize="60" FontFamily="Courier New"
Foreground="White" HorizontalAlignment="Center">
<TextBlock.Text>
<Binding Path="Progress" Mode="OneWay"
StringFormat="{}{0}%"/>
</TextBlock.Text>
</TextBlock>
<TextBlock Text="{Binding ElapsedTime, Mode=OneWay}"
FontSize="16" FontFamily="Courier New"
Foreground="#CCC" HorizontalAlignment="Center"
Margin="0,2,0,0"/>
</StackPanel>
</Grid>
<TextBlock Grid.Row="3"
Text="{Binding PhaseText, Mode=OneWay}"
FontSize="16" Foreground="White"
HorizontalAlignment="Center" Margin="0,6"/>
<ProgressBar Grid.Row="4"
Value="{Binding Progress, Mode=OneWay}"
Minimum="0" Maximum="100"
Height="12" Margin="12,0"
Foreground="#00EC00" Background="#3D3D3D"/>
<TextBlock Grid.Row="5"
Text="{Binding ResultText, Mode=OneWay}"
FontSize="22" FontWeight="Bold"
HorizontalAlignment="Center" Margin="0,10,0,0"
Visibility="{Binding IsComplete, Converter={StaticResource BoolToVis}}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#FF5858"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSuccess}" Value="True">
<Setter Property="Foreground" Value="#00EC00"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- Only the cancel button is exposed inline; the panel cannot be "closed" here -->
<StackPanel Grid.Row="6" Orientation="Horizontal"
HorizontalAlignment="Center" Margin="0,14,0,0">
<Button Content="{DynamicResource Common.Cancel}"
Command="{Binding CancelCommand}"
Width="110" Height="30"
Foreground="White" FontWeight="Bold">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#FF5858"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="#4D4D4D"/>
<Setter Property="Foreground" Value="#888"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Grid>
</Border>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
namespace HC_APTBS.Views.UserControls
{
/// <summary>
/// Inline unlock panel (Pump page §3.e). DataContext is the
/// shared <c>UnlockProgressViewModel</c> also driving the floating
/// progress dialog.
/// </summary>
public partial class UnlockPanelView : UserControl
{
public UnlockPanelView()
{
InitializeComponent();
}
}
}