feat: redesign bench calibration (factor/offset), add Ttank/P2 displays, fix sensor calibration
- Replace P1-P6 rational transfer function with factor/offset model for bench params - Add explicit rx/tx direction flags in bench XML configuration - Add T.Tank (BenchTemp) and P2 (AnalogSensor2) to temperature/pressure display - Apply SensorConfiguration calibration to pressure channels, fix empty sensors.xml fallback - Add live value labels to flowmeter charts - Hide pump live values and PSG encoder standalone label - Add K-Line connection state model, improve KWP service and status displays - Restructure .claude/skills into subdirectory format Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,10 @@ namespace HC_APTBS.ViewModels
|
||||
{
|
||||
_kwp = kwpService;
|
||||
_config = configService;
|
||||
|
||||
// Update the slider and LCD display in real time when the DFI is
|
||||
// read during a full K-Line read (PumpIdentificationViewModel flow).
|
||||
_kwp.DfiRead += (dfi) => SetDfi(dfi);
|
||||
}
|
||||
|
||||
// ── DFI display ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace HC_APTBS.ViewModels
|
||||
// ── Services ──────────────────────────────────────────────────────────────
|
||||
|
||||
private readonly ICanService _can;
|
||||
private readonly IKwpService _kwp;
|
||||
private readonly IBenchService _bench;
|
||||
private readonly IConfigurationService _config;
|
||||
private readonly IPdfService _pdf;
|
||||
@@ -96,6 +97,7 @@ namespace HC_APTBS.ViewModels
|
||||
IAppLogger logger)
|
||||
{
|
||||
_can = canService;
|
||||
_kwp = kwpService;
|
||||
_bench = benchService;
|
||||
_config = configService;
|
||||
_pdf = pdfService;
|
||||
@@ -129,6 +131,10 @@ namespace HC_APTBS.ViewModels
|
||||
_can.PumpLivenessChanged += alive =>
|
||||
App.Current.Dispatcher.Invoke(() => IsPumpConnected = alive);
|
||||
|
||||
// K-Line session state → indicator
|
||||
_kwp.KLineStateChanged += state =>
|
||||
App.Current.Dispatcher.Invoke(() => KLineState = state);
|
||||
|
||||
// Bench service events
|
||||
_bench.TestStarted += OnTestStarted;
|
||||
_bench.TestFinished += OnTestFinished;
|
||||
@@ -276,15 +282,21 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>Auxiliary temperature T4 (°C).</summary>
|
||||
[ObservableProperty] private double _temp4;
|
||||
|
||||
/// <summary>Oil tank temperature (°C).</summary>
|
||||
[ObservableProperty] private double _benchTemp;
|
||||
|
||||
/// <summary>Fuel delivery measurement Q-delivery (cc/stroke).</summary>
|
||||
[ObservableProperty] private double _qDelivery;
|
||||
|
||||
/// <summary>Fuel overflow/pilot measurement Q-over (cc/stroke).</summary>
|
||||
[ObservableProperty] private double _qOver;
|
||||
|
||||
/// <summary>Bench oil pressure (bar).</summary>
|
||||
/// <summary>Bench oil pressure P1 (bar), sensor-calibrated.</summary>
|
||||
[ObservableProperty] private double _pressure;
|
||||
|
||||
/// <summary>Analogue sensor 2 pressure P2 (bar), sensor-calibrated.</summary>
|
||||
[ObservableProperty] private double _pressure2;
|
||||
|
||||
/// <summary>PSG encoder position value.</summary>
|
||||
[ObservableProperty] private double _psgEncoderValue;
|
||||
|
||||
@@ -316,8 +328,8 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>True when oil circulation has been detected.</summary>
|
||||
[ObservableProperty] private bool _isOilCirculating;
|
||||
|
||||
/// <summary>True when a K-Line session is active.</summary>
|
||||
[ObservableProperty] private bool _isKLineConnected;
|
||||
/// <summary>Current K-Line session state (Disconnected / Connected / Failed).</summary>
|
||||
[ObservableProperty] private KLineConnectionState _kLineState = KLineConnectionState.Disconnected;
|
||||
|
||||
// ── Test status ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -500,11 +512,17 @@ namespace HC_APTBS.ViewModels
|
||||
TempIn = _bench.ReadBenchParameter(BenchParameterNames.TempIn);
|
||||
TempOut = _bench.ReadBenchParameter(BenchParameterNames.TempOut);
|
||||
Temp4 = _bench.ReadBenchParameter(BenchParameterNames.Temp4);
|
||||
BenchTemp = _bench.ReadBenchParameter(BenchParameterNames.Temp);
|
||||
QDelivery = _bench.ReadBenchParameter(BenchParameterNames.QDelivery);
|
||||
QOver = _bench.ReadBenchParameter(BenchParameterNames.QOver);
|
||||
Pressure = _bench.ReadBenchParameter(BenchParameterNames.Pressure);
|
||||
PsgEncoderValue = _bench.ReadBenchParameter(BenchParameterNames.PsgEncoderValue);
|
||||
|
||||
// Apply analogue sensor calibration for pressure channels.
|
||||
double rawP1 = _bench.ReadBenchParameter(BenchParameterNames.Pressure);
|
||||
Pressure = _config.Settings.Sensors.TryGetValue(1, out var s1) ? s1.GetValueFromRaw(rawP1) : rawP1;
|
||||
double rawP2 = _bench.ReadBenchParameter(BenchParameterNames.AnalogSensor2);
|
||||
Pressure2 = _config.Settings.Sensors.TryGetValue(2, out var s2) ? s2.GetValueFromRaw(rawP2) : rawP2;
|
||||
|
||||
// Feed the angle display with all three encoder channels + status.
|
||||
AngleDisplay.Update(
|
||||
PsgEncoderValue,
|
||||
|
||||
@@ -47,6 +47,18 @@ namespace HC_APTBS.ViewModels
|
||||
ProgressPercent = pct;
|
||||
ProgressMessage = msg;
|
||||
});
|
||||
|
||||
// Start loading the pump as soon as the identifier is read from ROM,
|
||||
// before the full K-Line read completes.
|
||||
_kwp.PumpIdentified += (pumpId) => App.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
KlinePumpId = pumpId;
|
||||
AutoSelectPumpByKlineId(pumpId);
|
||||
});
|
||||
|
||||
// Track K-Line session state for Disconnect button enable/disable.
|
||||
_kwp.KLineStateChanged += state =>
|
||||
App.Current.Dispatcher.Invoke(() => KLineState = state);
|
||||
}
|
||||
|
||||
// ── Pump selection ────────────────────────────────────────────────────────
|
||||
@@ -106,8 +118,14 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>True while a K-Line read is in progress.</summary>
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(ReadKlineCommand))]
|
||||
[NotifyCanExecuteChangedFor(nameof(DisconnectKLineCommand))]
|
||||
private bool _isReading;
|
||||
|
||||
/// <summary>Current K-Line session state for Disconnect button enable/disable.</summary>
|
||||
[ObservableProperty]
|
||||
[NotifyCanExecuteChangedFor(nameof(DisconnectKLineCommand))]
|
||||
private KLineConnectionState _kLineState = KLineConnectionState.Disconnected;
|
||||
|
||||
/// <summary>DFI calibration angle read from ECU EEPROM.</summary>
|
||||
[ObservableProperty] private string _klineDfi = "-";
|
||||
|
||||
@@ -182,7 +200,10 @@ namespace HC_APTBS.ViewModels
|
||||
{
|
||||
KlineDfi = dfi ?? "-";
|
||||
KlineErrors = errors ?? string.Empty;
|
||||
KlinePumpId = pumpId ?? string.Empty;
|
||||
// Preserve the value set by the PumpIdentified event if the
|
||||
// dictionary entry is empty (e.g. read failed after ident).
|
||||
if (!string.IsNullOrEmpty(pumpId))
|
||||
KlinePumpId = pumpId;
|
||||
KlineSerialNumber = serial ?? string.Empty;
|
||||
KlineModelRef = modelRef ?? string.Empty;
|
||||
KlineModelIndex = modelIndex ?? string.Empty;
|
||||
@@ -193,12 +214,8 @@ namespace HC_APTBS.ViewModels
|
||||
KlineConnectError = connectErr ?? string.Empty;
|
||||
});
|
||||
|
||||
// Auto-select pump from K-Line pump ID — matches old source behaviour
|
||||
// where OnPumpConnectClick would call LoadPump(pumpId) after reading.
|
||||
if (result == "1" && !string.IsNullOrEmpty(pumpId))
|
||||
{
|
||||
AutoSelectPumpByKlineId(pumpId);
|
||||
}
|
||||
// Pump auto-selection now happens via the PumpIdentified event
|
||||
// mid-read, so there is no need to call AutoSelectPumpByKlineId here.
|
||||
|
||||
// Attach K-Line info to the (now possibly auto-selected) pump.
|
||||
if (CurrentPump != null)
|
||||
@@ -212,6 +229,22 @@ namespace HC_APTBS.ViewModels
|
||||
|
||||
private bool CanReadKline() => !IsReading;
|
||||
|
||||
/// <summary>Closes the persistent K-Line session.</summary>
|
||||
[RelayCommand(CanExecute = nameof(CanDisconnectKLine))]
|
||||
private void DisconnectKLine()
|
||||
{
|
||||
try
|
||||
{
|
||||
_kwp.Disconnect();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(LogId, $"DisconnectKLine: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDisconnectKLine() => !IsReading && KLineState == KLineConnectionState.Connected;
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace HC_APTBS.ViewModels
|
||||
/// <summary>Chart title label.</summary>
|
||||
[ObservableProperty] private string _title = string.Empty;
|
||||
|
||||
/// <summary>Most recent sample value, displayed as a numeric label alongside the chart.</summary>
|
||||
[ObservableProperty] private double _currentValue;
|
||||
|
||||
/// <summary>Series array bound to the CartesianChart.</summary>
|
||||
public ISeries[] Series { get; }
|
||||
|
||||
@@ -88,6 +91,7 @@ namespace HC_APTBS.ViewModels
|
||||
public void AddValue(double value)
|
||||
{
|
||||
_values.Add(value);
|
||||
CurrentValue = value;
|
||||
if (_values.Count > _maxSamples)
|
||||
_values.RemoveAt(0);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,9 @@ namespace HC_APTBS.ViewModels
|
||||
get => _isActive;
|
||||
set => SetProperty(ref _isActive, value);
|
||||
}
|
||||
|
||||
/// <summary>Zero-based bit position (0–15) shown as a label beneath the indicator.</summary>
|
||||
public int Index { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -73,7 +76,7 @@ namespace HC_APTBS.ViewModels
|
||||
public StatusDisplayViewModel()
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
Bits.Add(new BitIndicatorViewModel { Color = "#26C200", Description = $"Bit {i}" });
|
||||
Bits.Add(new BitIndicatorViewModel { Color = "#26C200", Description = $"Bit {i}", Index = i });
|
||||
}
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user