Bundles several feature streams that have been iterating on the working tree: - Developer Tools page (Debug-only via DEVELOPER_TOOLS symbol): hosts the identification card, manual KWP write + transaction log, ROM/EEPROM dump card with progress banner and completion message, persisted custom-commands library, persisted EEPROM passwords library. New service primitives: IKwpService.SendRawCustomAsync / ReadEepromAsync / ReadRomEepromAsync. Persistence mirrors the Clients XML pattern in two new files (custom_commands.xml, eeprom_passwords.xml). - Auto-test orchestrator (IAutoTestOrchestrator + AutoTestState): linear K-Line read -> unlock -> bench-on -> test sequence with snackbar UI and progress dialog VM, gated on dashboard alarms. - BIP-STATUS display: BipDisplayViewModel + BipDisplayView, RAM read at 0x0106 via IKwpService.ReadBipStatusAsync; status definitions in BipStatusDefinition. - Tests page redesign: TestSectionCard + PhaseTileView replacing the old TestPlanView/TestRunningView/TestDoneView/TestPreconditionsView/ TestSectionView controls and their VMs. - Pump command sliders: Fluent thick-track style with overhang thumb, click-anywhere-and-drag, mouse-wheel adjustment. - Window startup: app.manifest declares PerMonitorV2 DPI awareness, MainWindow installs a WM_GETMINMAXINFO hook in OnSourceInitialized and maximizes there (after the hook is in place) so the app fits the work area exactly on any display configuration. - Misc: PercentToPixelsConverter, seed_aliases.py one-shot pump-alias importer, tools/Import-BipStatus.ps1, kline_eeprom_spec.md and dump-functions reference docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
292 lines
16 KiB
XML
292 lines
16 KiB
XML
<ui:FluentWindow x:Class="HC_APTBS.MainWindow"
|
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
xmlns:ui="http://schemas.lepo.co/wpfui/2022/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"
|
|
xmlns:uc="clr-namespace:HC_APTBS.Views.UserControls"
|
|
xmlns:pages="clr-namespace:HC_APTBS.Views.Pages"
|
|
xmlns:models="clr-namespace:HC_APTBS.Models"
|
|
mc:Ignorable="d"
|
|
Title="{DynamicResource App.Title}"
|
|
MinHeight="600" MinWidth="900"
|
|
WindowStartupLocation="CenterScreen"
|
|
FontFamily="Ebrima"
|
|
Closing="OnWindowClosing">
|
|
|
|
<DockPanel>
|
|
|
|
<!-- ── WPF-UI custom title bar (replaces OS chrome) ──────────────────── -->
|
|
<ui:TitleBar DockPanel.Dock="Top"
|
|
Title="{DynamicResource App.Title}"
|
|
ShowMaximize="True"
|
|
ShowMinimize="True"/>
|
|
|
|
<!-- ── Persistent app header: pump identification + connection state ──── -->
|
|
<Border DockPanel.Dock="Top" Background="Transparent"
|
|
BorderBrush="#999" BorderThickness="0,0,0,1" Visibility="Collapsed">
|
|
<Grid Margin="4,2">
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="*"/>
|
|
<ColumnDefinition Width="Auto"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!-- Pump selector + K-Line ECU info -->
|
|
<uc:PumpIdentificationView DataContext="{Binding PumpIdentification}"/>
|
|
|
|
<!-- Connection indicators + CAN connect/disconnect -->
|
|
<StackPanel Grid.Column="1" Orientation="Horizontal"
|
|
VerticalAlignment="Center" Margin="8,0,0,0">
|
|
<TextBlock Text="{DynamicResource Status.Label}" VerticalAlignment="Center"
|
|
FontSize="10" Margin="0,0,6,0"/>
|
|
|
|
<Border Width="54" Margin="2,0">
|
|
<Border.Style>
|
|
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsCanConnected}" Value="True">
|
|
<Setter Property="Background" Value="#26C200"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<TextBlock Text="{DynamicResource Status.Can}" HorizontalAlignment="Center"
|
|
FontSize="10" Padding="2"/>
|
|
</Border>
|
|
|
|
<Border Width="54" Margin="2,0">
|
|
<Border.Style>
|
|
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsBenchConnected}" Value="True">
|
|
<Setter Property="Background" Value="#26C200"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<TextBlock Text="{DynamicResource Status.Bench}" HorizontalAlignment="Center"
|
|
FontSize="10" Padding="2"/>
|
|
</Border>
|
|
|
|
<Border Width="54" Margin="2,0">
|
|
<Border.Style>
|
|
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding IsPumpConnected}" Value="True">
|
|
<Setter Property="Background" Value="#26C200"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<TextBlock Text="{DynamicResource Status.Pump}" HorizontalAlignment="Center"
|
|
FontSize="10" Padding="2"/>
|
|
</Border>
|
|
|
|
<Border Width="54" Margin="2,0,8,0">
|
|
<Border.Style>
|
|
<Style TargetType="Border" BasedOn="{StaticResource ConnIndicator}">
|
|
<Style.Triggers>
|
|
<DataTrigger Binding="{Binding KLineState}" Value="{x:Static models:KLineConnectionState.Connected}">
|
|
<Setter Property="Background" Value="#26C200"/>
|
|
</DataTrigger>
|
|
<DataTrigger Binding="{Binding KLineState}" Value="{x:Static models:KLineConnectionState.Failed}">
|
|
<Setter Property="Background" Value="#FF3333"/>
|
|
</DataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</Border.Style>
|
|
<TextBlock Text="{DynamicResource Status.KLine}" HorizontalAlignment="Center"
|
|
FontSize="10" Padding="2"/>
|
|
</Border>
|
|
|
|
<Button Content="{DynamicResource Bench.ConnectCan}" Width="110" Margin="4,0,4,0"
|
|
Command="{Binding ConnectCanCommand}"/>
|
|
<Button Content="{DynamicResource Bench.DisconnectCan}" Width="120"
|
|
Command="{Binding DisconnectCanCommand}"
|
|
IsEnabled="{Binding IsCanConnected}"/>
|
|
</StackPanel>
|
|
</Grid>
|
|
</Border>
|
|
|
|
<!-- ── Status bar ─────────────────────────────────────────────────── -->
|
|
<StatusBar DockPanel.Dock="Bottom" Height="24" Background="#FFD0D0D0" Visibility="Collapsed">
|
|
<StatusBarItem>
|
|
<TextBlock Text="{Binding CanStatusText}" Margin="4,0"/>
|
|
</StatusBarItem>
|
|
<Separator/>
|
|
<StatusBarItem>
|
|
<TextBlock Text="{Binding VerboseStatus}" Margin="4,0"/>
|
|
</StatusBarItem>
|
|
</StatusBar>
|
|
|
|
<!-- ── Main content: Fluent NavigationView (selector only) + hidden-tab page host ──── -->
|
|
<Grid>
|
|
<Grid.ColumnDefinitions>
|
|
<ColumnDefinition Width="Auto"/>
|
|
<ColumnDefinition Width="*"/>
|
|
</Grid.ColumnDefinitions>
|
|
|
|
<!--
|
|
NavigationView used as a pure selector — page hosting stays with
|
|
the TabControl in column 1. PaneDisplayMode=Left gives the
|
|
standard Fluent expanded rail with icon + text per item and
|
|
animated selection indicator.
|
|
-->
|
|
<ui:NavigationView x:Name="NavView"
|
|
Grid.Column="0"
|
|
PaneDisplayMode="Left"
|
|
OpenPaneLength="180"
|
|
IsBackButtonVisible="Collapsed"
|
|
IsPaneToggleVisible="False"
|
|
ItemTemplate="{StaticResource BoxyNavItemTemplate}">
|
|
<ui:NavigationView.MenuItems>
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Dashboard}"
|
|
Tag="{x:Static vm:AppPage.Dashboard}"
|
|
TargetPageTag="Dashboard">
|
|
<ui:NavigationViewItem.Icon>
|
|
<ui:SymbolIcon Symbol="Home28"/>
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Bench}"
|
|
Tag="{x:Static vm:AppPage.Bench}"
|
|
TargetPageTag="Bench">
|
|
<ui:NavigationViewItem.Icon>
|
|
<ui:SymbolIcon Symbol="ArrowSyncCircle24"/>
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Pump}"
|
|
Tag="{x:Static vm:AppPage.Pump}"
|
|
TargetPageTag="Pump">
|
|
<ui:NavigationViewItem.Icon >
|
|
<ui:SymbolIcon Symbol="PulseSquare24" />
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Tests}"
|
|
Tag="{x:Static vm:AppPage.Tests}"
|
|
TargetPageTag="Tests">
|
|
<ui:NavigationViewItem.Icon>
|
|
<ui:SymbolIcon Symbol="BeakerEdit24"/>
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Results}"
|
|
Tag="{x:Static vm:AppPage.Results}"
|
|
TargetPageTag="Results">
|
|
<ui:NavigationViewItem.Icon>
|
|
<ui:SymbolIcon Symbol="DocumentTable24"/>
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
|
|
</ui:NavigationView.MenuItems>
|
|
|
|
<ui:NavigationView.FooterMenuItems>
|
|
<ui:NavigationViewItem Content="{DynamicResource Nav.Settings}"
|
|
Tag="{x:Static vm:AppPage.Settings}"
|
|
TargetPageTag="Settings">
|
|
<ui:NavigationViewItem.Icon>
|
|
<ui:SymbolIcon Symbol="Wrench24"/>
|
|
</ui:NavigationViewItem.Icon>
|
|
</ui:NavigationViewItem>
|
|
</ui:NavigationView.FooterMenuItems>
|
|
</ui:NavigationView>
|
|
|
|
<!-- Page host: hidden-tab TabControl + bottom-snackbar overlays -->
|
|
<Grid Grid.Column="1">
|
|
<!--
|
|
TabItem order must match AppPage enum values:
|
|
Bench=0, Pump=1, Tests=2, Dashboard=3, Settings=4, Results=5.
|
|
-->
|
|
<TabControl x:Name="PageTabs"
|
|
Style="{StaticResource HiddenTabsTabControl}"
|
|
SelectedIndex="{Binding SelectedPage, Converter={StaticResource EnumToInt}}">
|
|
<TabItem>
|
|
<pages:BenchPage DataContext="{Binding BenchPage}"/>
|
|
</TabItem>
|
|
<TabItem>
|
|
<pages:PumpPage DataContext="{Binding PumpPage}"/>
|
|
</TabItem>
|
|
<TabItem>
|
|
<pages:TestsPage DataContext="{Binding TestsPage}"/>
|
|
</TabItem>
|
|
<TabItem>
|
|
<pages:DashboardPage DataContext="{Binding DashboardPage}"/>
|
|
</TabItem>
|
|
<TabItem>
|
|
<pages:SettingsPage DataContext="{Binding SettingsPage}"/>
|
|
</TabItem>
|
|
<TabItem>
|
|
<pages:ResultsPage DataContext="{Binding ResultsPage}"/>
|
|
</TabItem>
|
|
</TabControl>
|
|
|
|
<uc:UnlockSnackbarView DataContext="{Binding CurrentUnlockVm}"
|
|
HorizontalAlignment="Center"
|
|
VerticalAlignment="Bottom"
|
|
Margin="0,0,0,8"/>
|
|
<uc:AutoTestSnackbarView DataContext="{Binding AutoTestProgress}"
|
|
HorizontalAlignment="Center"
|
|
VerticalAlignment="Bottom"
|
|
Margin="0,0,0,8"/>
|
|
|
|
<!-- Dashboard CAN connect/disconnect snackbar (page-bottom, matches other snackbars) -->
|
|
<Border DataContext="{Binding DashboardPage.Devices.Transition}"
|
|
Visibility="{Binding Converter={StaticResource NullToVis}}"
|
|
Style="{StaticResource SnackbarShell}"
|
|
HorizontalAlignment="Center" VerticalAlignment="Bottom"
|
|
MinWidth="340" MaxWidth="640"
|
|
Margin="0,0,0,8" Padding="16,10">
|
|
<DockPanel LastChildFill="True">
|
|
<Grid DockPanel.Dock="Left" Width="22" Height="22" Margin="0,0,10,0">
|
|
<ui:ProgressRing IsIndeterminate="True" Width="18" Height="18"
|
|
Visibility="{Binding IsBusy, Converter={StaticResource BoolToVis}}"/>
|
|
<ui:SymbolIcon Symbol="CheckmarkCircle24" FontSize="18"
|
|
Foreground="{DynamicResource SystemFillColorSuccessBrush}">
|
|
<ui:SymbolIcon.Style>
|
|
<Style TargetType="ui:SymbolIcon">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding IsBusy}" Value="False"/>
|
|
<Condition Binding="{Binding IsSuccess}" Value="True"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ui:SymbolIcon.Style>
|
|
</ui:SymbolIcon>
|
|
<ui:SymbolIcon Symbol="ErrorCircle24" FontSize="18"
|
|
Foreground="{DynamicResource SystemFillColorCriticalBrush}">
|
|
<ui:SymbolIcon.Style>
|
|
<Style TargetType="ui:SymbolIcon">
|
|
<Setter Property="Visibility" Value="Collapsed"/>
|
|
<Style.Triggers>
|
|
<MultiDataTrigger>
|
|
<MultiDataTrigger.Conditions>
|
|
<Condition Binding="{Binding IsBusy}" Value="False"/>
|
|
<Condition Binding="{Binding IsSuccess}" Value="False"/>
|
|
</MultiDataTrigger.Conditions>
|
|
<Setter Property="Visibility" Value="Visible"/>
|
|
</MultiDataTrigger>
|
|
</Style.Triggers>
|
|
</Style>
|
|
</ui:SymbolIcon.Style>
|
|
</ui:SymbolIcon>
|
|
</Grid>
|
|
<TextBlock Text="{Binding Message}"
|
|
VerticalAlignment="Center"
|
|
FontSize="13"
|
|
TextTrimming="CharacterEllipsis"
|
|
Foreground="{DynamicResource TextFillColorPrimaryBrush}"/>
|
|
</DockPanel>
|
|
</Border>
|
|
</Grid>
|
|
</Grid>
|
|
</DockPanel>
|
|
</ui:FluentWindow>
|