feat: developer tools page, auto-test orchestrator, BIP display, tests redesign
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>
This commit is contained in:
249
MainWindow.xaml
249
MainWindow.xaml
@@ -10,8 +10,7 @@
|
||||
xmlns:models="clr-namespace:HC_APTBS.Models"
|
||||
mc:Ignorable="d"
|
||||
Title="{DynamicResource App.Title}"
|
||||
Height="1080" Width="1920"
|
||||
WindowState="Maximized"
|
||||
MinHeight="600" MinWidth="900"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
FontFamily="Ebrima"
|
||||
Closing="OnWindowClosing">
|
||||
@@ -111,7 +110,7 @@
|
||||
</Border>
|
||||
|
||||
<!-- ── Status bar ─────────────────────────────────────────────────── -->
|
||||
<StatusBar DockPanel.Dock="Bottom" Height="24" Background="#FFD0D0D0">
|
||||
<StatusBar DockPanel.Dock="Bottom" Height="24" Background="#FFD0D0D0" Visibility="Collapsed">
|
||||
<StatusBarItem>
|
||||
<TextBlock Text="{Binding CanStatusText}" Margin="4,0"/>
|
||||
</StatusBarItem>
|
||||
@@ -121,100 +120,172 @@
|
||||
</StatusBarItem>
|
||||
</StatusBar>
|
||||
|
||||
<!-- ── Main content: left navigation rail + hidden-tab page host ──── -->
|
||||
<!-- ── Main content: Fluent NavigationView (selector only) + hidden-tab page host ──── -->
|
||||
<Grid>
|
||||
<!-- Inner grid: nav rail (col 0) + page host (col 1) -->
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="180"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!--
|
||||
Nav rail column: primary pages at top, Settings pinned at bottom
|
||||
with a divider. Both ListBoxes bind to SelectedPage; WPF auto-
|
||||
deselects a ListBox whose SelectedValue is not among its items,
|
||||
so clicking Settings clears the top highlight and vice versa.
|
||||
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.
|
||||
-->
|
||||
<Grid Grid.Column="0" Background="#FF2F3440">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<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>
|
||||
|
||||
<ListBox Grid.Row="0"
|
||||
Style="{StaticResource NavRail}"
|
||||
SelectedValuePath="Tag"
|
||||
SelectedValue="{Binding SelectedPage, Mode=TwoWay}">
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Dashboard}">
|
||||
<TextBlock Text="{DynamicResource Nav.Dashboard}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Bench}">
|
||||
<TextBlock Text="{DynamicResource Nav.Bench}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Pump}">
|
||||
<TextBlock Text="{DynamicResource Nav.Pump}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Tests}">
|
||||
<TextBlock Text="{DynamicResource Nav.Tests}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Results}">
|
||||
<TextBlock Text="{DynamicResource Nav.Results}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
</ListBox>
|
||||
<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>
|
||||
|
||||
<Border Grid.Row="1" Height="1" Background="#FF555C6B"
|
||||
Margin="12,4,12,4"/>
|
||||
<!-- 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>
|
||||
|
||||
<ListBox Grid.Row="2"
|
||||
Style="{StaticResource NavRail}"
|
||||
SelectedValuePath="Tag"
|
||||
SelectedValue="{Binding SelectedPage, Mode=TwoWay}">
|
||||
<ListBoxItem Tag="{x:Static vm:AppPage.Settings}">
|
||||
<TextBlock Text="{DynamicResource Nav.Settings}"
|
||||
Foreground="#FFE6E6E6" FontSize="14"/>
|
||||
</ListBoxItem>
|
||||
</ListBox>
|
||||
<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>
|
||||
|
||||
<!--
|
||||
TabItem order must match AppPage enum values:
|
||||
Bench=0, Pump=1, Tests=2, Dashboard=3, Settings=4, Results=5.
|
||||
Nav rail order above is UX-driven (Dashboard first) via Tag binding.
|
||||
-->
|
||||
<TabControl Grid.Column="1"
|
||||
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>
|
||||
</Grid>
|
||||
<uc:UnlockSnackbarView DataContext="{Binding CurrentUnlockVm}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Bottom"
|
||||
Margin="0,0,0,8"/>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
</ui:FluentWindow>
|
||||
|
||||
Reference in New Issue
Block a user