initial commit

This commit is contained in:
2026-04-11 12:45:18 +02:00
commit 6e1b929e2f
1246 changed files with 177580 additions and 0 deletions

283
Models/CanBusParameter.cs Normal file
View File

@@ -0,0 +1,283 @@
using System;
using System.Globalization;
using System.Xml.Linq;
namespace HC_APTBS.Models
{
/// <summary>
/// Represents a single logical parameter exchanged over CAN.
/// Each parameter occupies up to two bytes within a CAN frame identified by <see cref="MessageId"/>.
///
/// <para><b>Transfer function (transmit / non-receive):</b><br/>
/// <c>output = ((P1 * Value + P2) / (P3 * Value + P4)) + P5 + P6</c></para>
///
/// <para><b>Inverse transfer function (receive):</b><br/>
/// <c>output = (P2 P3·P5 P4·P6 + P4·Value) / (P1 + P3·P5 + P3·P6 P3·Value)</c></para>
///
/// <para>Set <see cref="DisableCalibration"/> to use the identity transform (P1=1, P2=0, P3=0, P4=1, P5=0, P6=0).</para>
/// </summary>
public class CanBusParameter
{
// ── Frame addressing ─────────────────────────────────────────────────────
/// <summary>CAN message identifier this parameter belongs to.</summary>
public uint MessageId { get; set; }
/// <summary>Index of the high byte within the 8-byte CAN payload.</summary>
public ushort ByteH { get; set; }
/// <summary>Index of the low byte within the 8-byte CAN payload.</summary>
public ushort ByteL { get; set; }
// ── Metadata ─────────────────────────────────────────────────────────────
/// <summary>Human-readable parameter name used for lookup (see <see cref="BenchParameterNames"/>).</summary>
public string Name { get; set; } = string.Empty;
/// <summary>
/// Sensor/encoding type selector. Used by the receive decoder to choose
/// the correct bit-extraction formula (e.g. for temperature and RPM).
/// </summary>
public int Type { get; set; } = 0;
// ── Calibration coefficients (P1P6) ─────────────────────────────────────
/// <summary>Transfer function coefficient P1 (numerator multiplier).</summary>
public double P1 { get; set; }
/// <summary>Transfer function coefficient P2 (numerator offset).</summary>
public double P2 { get; set; }
/// <summary>Transfer function coefficient P3 (denominator multiplier).</summary>
public double P3 { get; set; }
/// <summary>Transfer function coefficient P4 (denominator offset).</summary>
public double P4 { get; set; }
/// <summary>Transfer function coefficient P5 (additive offset 1).</summary>
public double P5 { get; set; }
/// <summary>Transfer function coefficient P6 (additive offset 2).</summary>
public double P6 { get; set; }
/// <summary>
/// When true, the calibration coefficients are set to the identity transform
/// (P1=1, P4=1, all others 0) so the raw value passes through unchanged.
/// </summary>
public bool DisableCalibration { get; set; }
// ── Runtime state ─────────────────────────────────────────────────────────
/// <summary>Current decoded engineering-unit value (updated by the CAN read thread).</summary>
public double Value { get; set; }
/// <summary>
/// True when <see cref="Value"/> has been updated since the last UI refresh tick.
/// Reset to false by the consumer after reading.
/// </summary>
public bool NeedsUpdate { get; set; }
/// <summary>
/// When true this parameter is used in the receive direction and applies the
/// inverse transfer function in <see cref="GetTransformResult"/>.
/// </summary>
public bool IsReceive { get; set; }
/// <summary>
/// Exponential moving average coefficient α ∈ (0, 1].
/// α = 1 means no smoothing (pass-through); smaller values give more smoothing.
/// </summary>
public double Alpha { get; set; } = 1.0;
// ── Convenience alias kept for cross-file compatibility ───────────────────
/// <summary>Alias for <see cref="MessageId"/> — used by legacy call sites.</summary>
public uint ID
{
get => MessageId;
set => MessageId = value;
}
// ── Transfer function ─────────────────────────────────────────────────────
/// <summary>
/// Applies the calibration transfer function to <see cref="Value"/>.
/// </summary>
/// <returns>Calibrated engineering-unit result.</returns>
public double GetTransformResult()
{
if (IsReceive)
{
// Inverse function: maps a measured value back to the raw command value.
return (-P2 - P3 * P5 - P4 * P6 + P4 * Value)
/ (P1 + P3 * P5 + P3 * P6 - P3 * Value);
}
// Forward function: maps a setpoint to the raw CAN integer to transmit.
return ((P1 * Value + P2) / (P3 * Value + P4)) + P5 + P6;
}
/// <summary>
/// Returns the rounded integer value ready to be packed into a CAN frame byte pair.
/// </summary>
public double GetTransmitValue() => GetTransformResult();
/// <summary>
/// Resets calibration coefficients to the identity transform so the raw value
/// passes through <see cref="GetTransformResult"/> unchanged.
/// </summary>
public void SetIdentityCalibration()
{
P1 = 1; P2 = 0; P3 = 0; P4 = 1; P5 = 0; P6 = 0;
DisableCalibration = true;
}
// ── XML serialisation ─────────────────────────────────────────────────────
/// <summary>
/// Deserialises a pump CAN parameter from an XML element inside the
/// <c>&lt;Params&gt;</c> section of a pump definition.
/// </summary>
public static CanBusParameter FromXml(XElement xe)
{
var p = new CanBusParameter
{
Name = xe.Name.LocalName,
MessageId = uint.Parse(xe.Attribute("busid")?.Value ?? "0",
NumberStyles.HexNumber),
ByteH = ushort.Parse(xe.Attribute("byteh")?.Value ?? "0"),
ByteL = ushort.Parse(xe.Attribute("bytel")?.Value ?? "0"),
Type = int.Parse(xe.Attribute("type")?.Value ?? "0"),
IsReceive = !string.Equals(xe.Attribute("send")?.Value, "true",
StringComparison.OrdinalIgnoreCase),
Alpha = ParseDecimal(xe.Attribute("filter")?.Value, 1.0),
DisableCalibration = string.Equals(xe.Attribute("disableparams")?.Value,
"true", StringComparison.OrdinalIgnoreCase)
};
if (p.DisableCalibration)
{
p.SetIdentityCalibration();
}
else
{
p.P1 = ParseDecimal(xe.Attribute("p1")?.Value, 1.0);
p.P2 = ParseDecimal(xe.Attribute("p2")?.Value, 0.0);
p.P3 = ParseDecimal(xe.Attribute("p3")?.Value, 0.0);
p.P4 = ParseDecimal(xe.Attribute("p4")?.Value, 1.0);
p.P5 = ParseDecimal(xe.Attribute("p5")?.Value, 0.0);
p.P6 = ParseDecimal(xe.Attribute("p6")?.Value, 0.0);
}
return p;
}
/// <summary>Parses a decimal string that may use comma or dot as separator.</summary>
private static double ParseDecimal(string? value, double fallback)
{
if (string.IsNullOrEmpty(value)) return fallback;
return double.Parse(value.Replace(',', '.'), CultureInfo.InvariantCulture);
}
/// <summary>Serialises this parameter to an XML element for persistence in bench.xml.</summary>
public XElement ToXml()
{
var elm = new XElement(Name,
new XAttribute("id", MessageId.ToString("X")),
new XAttribute("byteh", ByteH),
new XAttribute("bytel", ByteL),
new XAttribute("filter", Alpha),
new XAttribute("disableparams", DisableCalibration));
if (!DisableCalibration)
{
elm.Add(
new XAttribute("p1", P1),
new XAttribute("p2", P2),
new XAttribute("p3", P3),
new XAttribute("p4", P4),
new XAttribute("p5", P5),
new XAttribute("p6", P6));
}
return elm;
}
}
// ── Well-known parameter name constants ──────────────────────────────────────
/// <summary>String constants for bench CAN parameter names.</summary>
public static class BenchParameterNames
{
public const string Rpm = "RPM";
public const string Counter = "Counter";
public const string BaudRate = "BaudRate";
public const string BenchRpm = "BenchRPM";
public const string BenchCounter = "BenchCounter";
public const string Temp = "BenchTemp";
public const string TempIn = "T-in";
public const string TempOut = "T-out";
public const string Temp4 = "T4";
public const string QDelivery = "QDelivery";
public const string QOver = "QOver";
public const string ElectronicMsg = "ElectronicMsg";
public const string EncoderResolution = "EncoderResolution";
public const string PsgEncoderValue = "PSGEncoderValue";
public const string PsgEncoderWorking = "PSGEncoderWorking";
public const string InjEncoderValue = "InyectorEncoderValue";
public const string InjEncoderWorking = "InyectorEncoderWorking";
public const string ManualEncoderValue = "ManualEncoderValue";
public const string Alarms = "Alarms";
public const string Pressure = "Pressure";
public const string AnalogSensor2 = "AnalogicSensor2";
}
/// <summary>String constants for pump CAN parameter names.</summary>
public static class PumpParameterNames
{
public const string MemoryRequest = "MemoryRequest";
public const string Me = "me";
public const string Fbkw = "FBKW";
public const string PreIn = "mepi";
public const string Rpm = "RPM";
public const string Temp = "Temp";
public const string Tein = "Tein";
public const string TestUnlock = "TestUnlock";
public const string TestImmo = "TestImmo";
public const string Status = "Status";
public const string Empf3 = "Empf3";
}
/// <summary>String constants for K-Line / KWP data dictionary keys.</summary>
public static class KlineKeys
{
public const string PumpId = "pumpID";
public const string SerialNumber = "SerialNumber";
public const string ModelReference = "ModelReference";
public const string PumpControl = "PumpControl";
public const string ModelIndex = "ModelIndex";
public const string DataRecord = "DataRecord";
public const string SwVersion1 = "SWV1";
public const string SwVersion2 = "SWV2";
public const string Dfi = "dfi";
public const string Errors = "ErrorCodes";
public const string Result = "result";
public const string NoErrors = "No fault codes";
public const string ConnectError = "CON ERROR";
}
/// <summary>Relay name constants — mirror the bench XML definition.</summary>
public static class RelayNames
{
public const string Electronic = "Electronic";
public const string OilPump = "OilPump";
public const string DepositCooler = "DepositCooler";
public const string DepositHeater = "DepositHeater";
public const string DirectionRight = "Reserve";
public const string Counter = "Counter";
public const string DirectionLeft = "Direction";
public const string TinCooler = "TinCooler";
public const string Pulse4Signal = "Pulse4Signal";
public const string Flasher = "Flasher";
}
}