- 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>
136 lines
5.4 KiB
C#
136 lines
5.4 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.ObjectModel;
|
||
using CommunityToolkit.Mvvm.ComponentModel;
|
||
using HC_APTBS.Models;
|
||
|
||
namespace HC_APTBS.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// Represents the state of a single bit indicator in the 16-bit pump status word display.
|
||
/// </summary>
|
||
public sealed class BitIndicatorViewModel : ObservableObject
|
||
{
|
||
[System.Runtime.CompilerServices.CompilerGenerated]
|
||
private string _color = "#26C200";
|
||
|
||
/// <summary>HTML hex colour for the indicator background (e.g. "#26C200" = green, "#FF1E1E" = red).</summary>
|
||
public string Color
|
||
{
|
||
get => _color;
|
||
set => SetProperty(ref _color, value);
|
||
}
|
||
|
||
[System.Runtime.CompilerServices.CompilerGenerated]
|
||
private string _description = string.Empty;
|
||
|
||
/// <summary>Tooltip / label for this bit's current state.</summary>
|
||
public string Description
|
||
{
|
||
get => _description;
|
||
set => SetProperty(ref _description, value);
|
||
}
|
||
|
||
[System.Runtime.CompilerServices.CompilerGenerated]
|
||
private bool _isActive;
|
||
|
||
/// <summary>True when this bit is set in the status word.</summary>
|
||
public bool IsActive
|
||
{
|
||
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>
|
||
/// ViewModel for the StatusDisplay user control.
|
||
///
|
||
/// <para>
|
||
/// Maintains a 16-element collection of <see cref="BitIndicatorViewModel"/> objects,
|
||
/// one per bit of the pump status word. Call <see cref="UpdateStatusWord"/> whenever
|
||
/// a new value arrives from the CAN bus.
|
||
/// </para>
|
||
/// </summary>
|
||
public sealed partial class StatusDisplayViewModel : ObservableObject
|
||
{
|
||
// ── Properties ────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>Title shown above the bit display (e.g. "Table 2 – Status").</summary>
|
||
[ObservableProperty] private string _title = "STATUS";
|
||
|
||
/// <summary>16 bit indicators for bits 0–15 of the current status word.</summary>
|
||
public ObservableCollection<BitIndicatorViewModel> Bits { get; } = new();
|
||
|
||
/// <summary>
|
||
/// Fired when a bit that is flagged as an error transitions to active.
|
||
/// The argument is the bit position (0-based).
|
||
/// </summary>
|
||
public event Action<int>? ErrorBitDetected;
|
||
|
||
// ── Constructor ───────────────────────────────────────────────────────────
|
||
|
||
/// <summary>Initialises the collection with 16 green indicator placeholders.</summary>
|
||
public StatusDisplayViewModel()
|
||
{
|
||
for (int i = 0; i < 16; i++)
|
||
Bits.Add(new BitIndicatorViewModel { Color = "#26C200", Description = $"Bit {i}", Index = i });
|
||
}
|
||
|
||
// ── Public API ────────────────────────────────────────────────────────────
|
||
|
||
/// <summary>
|
||
/// Updates all 16 bit indicators from a <see cref="PumpStatusDefinition"/> and a live value.
|
||
/// Must be called on the UI thread.
|
||
/// </summary>
|
||
/// <param name="statusDefinition">Definition describing what each bit means.</param>
|
||
/// <param name="rawValue">Integer value from the pump CAN status parameter.</param>
|
||
public void UpdateStatusWord(PumpStatusDefinition statusDefinition, int rawValue)
|
||
{
|
||
if (statusDefinition == null) return;
|
||
|
||
Title = $"Table {statusDefinition.Id} – {statusDefinition.Name}";
|
||
var bits = new BitArray(new[] { rawValue });
|
||
|
||
foreach (var statusBit in statusDefinition.Bits)
|
||
{
|
||
int index = statusBit.Bit;
|
||
if (index < 0 || index >= 16) continue;
|
||
|
||
bool isSet = index < bits.Length && bits[index];
|
||
var indicator = Bits[index];
|
||
indicator.IsActive = isSet;
|
||
|
||
// Find the matching state definition and apply its colour/description.
|
||
foreach (var val in statusBit.Values)
|
||
{
|
||
int expectedState = isSet ? 1 : 0;
|
||
if (val.State == expectedState)
|
||
{
|
||
indicator.Color = "#" + val.Color;
|
||
indicator.Description = val.Description;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Notify the bench service / main VM of error bits.
|
||
if (isSet && statusBit.Enabled)
|
||
ErrorBitDetected?.Invoke(index);
|
||
}
|
||
}
|
||
|
||
/// <summary>Resets all indicators to the default green / inactive state.</summary>
|
||
public void Reset()
|
||
{
|
||
for (int i = 0; i < Bits.Count; i++)
|
||
{
|
||
Bits[i].IsActive = false;
|
||
Bits[i].Color = "#26C200";
|
||
Bits[i].Description = $"Bit {i}";
|
||
}
|
||
}
|
||
}
|
||
}
|