From e343006f458198fe019ab91e763ee6346ebf9619 Mon Sep 17 00:00:00 2001 From: LucianoDev Date: Sat, 11 Apr 2026 14:24:59 +0200 Subject: [PATCH] feat: restore bench section UI with controls, PID RPM ramp, flowmeter charts, and fix CAN IDs Restore the full bench control panel from the old source with MVVM architecture: - Two-column left panel layout: bench info displays (RPM with target/voltage, temps, pressures, Q-flow, pump live values) and user commands (direction toggle, start/stop with RPM popup and quick-select buttons, oil pump toggle, turn downcounter with CAN send) - PID RPM ramp controller (BenchPidController) with bumpless startup, anti-windup, and derivative-on-measurement for smooth motor speed transitions - Real-time flowmeter charts (LiveChartsCore) for Q-Delivery and Q-Over with tolerance band overlays - Bench/pump CAN liveness detection in PcanAdapter (receive-only IDs) - K-Line connection status indicator (placeholder) - Periodic relay bitmask sender (~21ms) and ElectronicMsg keepalive start on CAN connect, pump sender starts immediately on pump load Fix critical CAN message ID bug: default bench XML values were incorrectly converted from old source (decimal-notation hex parsed as actual hex digits, e.g. "10" -> "A" instead of keeping "10" which parses as 0x10). Corrected all IDs to match hardware: 0x10, 0x11, 0x13, 0x14, 0x15, 0x50, 0x51, 0x55. Co-Authored-By: Claude Opus 4.6 (1M context) --- Infrastructure/Pcan/PcanAdapter.cs | 73 ++++ MainWindow.xaml | 356 ++++++++++++------ Services/IBenchService.cs | 36 ++ Services/ICanService.cs | 24 ++ Services/Impl/BenchPidController.cs | 204 ++++++++++ Services/Impl/BenchService.cs | 159 +++++++- Services/Impl/ConfigurationService.cs | 56 +-- ViewModels/BenchControlViewModel.cs | 193 ++++++++++ ViewModels/FlowmeterChartViewModel.cs | 51 +++ ViewModels/MainViewModel.cs | 70 +++- ViewModels/SingleFlowChartViewModel.cs | 122 ++++++ Views/UserControls/FlowmeterChartView.xaml | 23 ++ Views/UserControls/FlowmeterChartView.xaml.cs | 16 + 13 files changed, 1242 insertions(+), 141 deletions(-) create mode 100644 Services/Impl/BenchPidController.cs create mode 100644 ViewModels/BenchControlViewModel.cs create mode 100644 ViewModels/FlowmeterChartViewModel.cs create mode 100644 ViewModels/SingleFlowChartViewModel.cs create mode 100644 Views/UserControls/FlowmeterChartView.xaml create mode 100644 Views/UserControls/FlowmeterChartView.xaml.cs diff --git a/Infrastructure/Pcan/PcanAdapter.cs b/Infrastructure/Pcan/PcanAdapter.cs index a18247b..9370174 100644 --- a/Infrastructure/Pcan/PcanAdapter.cs +++ b/Infrastructure/Pcan/PcanAdapter.cs @@ -48,11 +48,27 @@ namespace HC_APTBS.Infrastructure.Pcan private AutoResetEvent? _receiveEvent; private volatile bool _stopRead = true; + // ── Liveness tracking ──────────────────────────────────────────────────── + + private const int LivenessTimeoutMs = 500; + private HashSet _benchMessageIds = new(); + private HashSet _pumpMessageIds = new(); + private DateTime _lastBenchFrameUtc = DateTime.MinValue; + private DateTime _lastPumpFrameUtc = DateTime.MinValue; + private bool _benchAlive; + private bool _pumpAlive; + // ── ICanService ────────────────────────────────────────────────────────── /// public event Action? StatusChanged; + /// + public event Action? BenchLivenessChanged; + + /// + public event Action? PumpLivenessChanged; + /// public TPCANStatus CurrentStatus { get; private set; } = TPCANStatus.PCAN_ERROR_OK; @@ -186,6 +202,18 @@ namespace HC_APTBS.Infrastructure.Pcan } } + /// + public void RegisterBenchMessageIds(IReadOnlyCollection ids) + { + _benchMessageIds = new HashSet(ids); + } + + /// + public void RegisterPumpMessageIds(IReadOnlyCollection ids) + { + _pumpMessageIds = new HashSet(ids); + } + // ── ICanService: transmit ───────────────────────────────────────────────── /// @@ -289,6 +317,9 @@ namespace HC_APTBS.Infrastructure.Pcan EmitStatusChanged(status); } + // Check liveness timeouts. + CheckLivenessTimeout(); + // Configurable polling interval to avoid pegging the CPU. // Typical value: 2–50 ms depending on operational phase. Thread.Sleep(2); @@ -336,6 +367,27 @@ namespace HC_APTBS.Infrastructure.Pcan if (!snapshot.TryGetValue(frame.ID, out var parameters)) return; + // Track liveness for bench and pump frame groups. + var now = DateTime.UtcNow; + if (_benchMessageIds.Contains(frame.ID)) + { + _lastBenchFrameUtc = now; + if (!_benchAlive) + { + _benchAlive = true; + BenchLivenessChanged?.Invoke(true); + } + } + if (_pumpMessageIds.Contains(frame.ID)) + { + _lastPumpFrameUtc = now; + if (!_pumpAlive) + { + _pumpAlive = true; + PumpLivenessChanged?.Invoke(true); + } + } + byte[] data = frame.DATA; foreach (var param in parameters) @@ -472,6 +524,27 @@ namespace HC_APTBS.Infrastructure.Pcan return raw; } + /// + /// Checks if bench or pump frame reception has timed out and fires + /// liveness events on transition from alive to dead. + /// + private void CheckLivenessTimeout() + { + var now = DateTime.UtcNow; + + if (_benchAlive && (now - _lastBenchFrameUtc).TotalMilliseconds > LivenessTimeoutMs) + { + _benchAlive = false; + BenchLivenessChanged?.Invoke(false); + } + + if (_pumpAlive && (now - _lastPumpFrameUtc).TotalMilliseconds > LivenessTimeoutMs) + { + _pumpAlive = false; + PumpLivenessChanged?.Invoke(false); + } + } + // ── IIR low-pass filter ─────────────────────────────────────────────────── /// diff --git a/MainWindow.xaml b/MainWindow.xaml index 0045d47..dceda2d 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -106,9 +106,15 @@ ══════════════════════════════════════════════════════════════ --> - + + + + + + + - + @@ -116,6 +122,7 @@ + @@ -161,11 +168,25 @@ + + + + + + + - - + +