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) <noreply@anthropic.com>
This commit is contained in:
204
Services/Impl/BenchPidController.cs
Normal file
204
Services/Impl/BenchPidController.cs
Normal file
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace HC_APTBS.Services.Impl
|
||||
{
|
||||
/// <summary>
|
||||
/// PID controller for smooth RPM ramping on the test bench motor.
|
||||
/// Runs a background loop that reads actual RPM, computes the PID output,
|
||||
/// and writes the corresponding motor voltage via a callback.
|
||||
///
|
||||
/// <para>
|
||||
/// All values are normalized to 0–1.0 internally. The output is scaled back
|
||||
/// to the configured voltage range before calling the write delegate.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class BenchPidController : IDisposable
|
||||
{
|
||||
// ── Gains ────────────────────────────────────────────────────────────────
|
||||
|
||||
private readonly double _kp;
|
||||
private readonly double _ki;
|
||||
private readonly double _kd;
|
||||
|
||||
// ── Ranges ───────────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>Maximum process variable (RPM).</summary>
|
||||
private readonly double _pvMax;
|
||||
|
||||
/// <summary>Maximum output variable (voltage).</summary>
|
||||
private readonly double _outMax;
|
||||
|
||||
// ── Delegates ────────────────────────────────────────────────────────────
|
||||
|
||||
private readonly Func<double> _readActualRpm;
|
||||
private readonly Func<double> _readTargetRpm;
|
||||
private readonly Action<double> _sendVoltage;
|
||||
|
||||
// ── Loop state ───────────────────────────────────────────────────────────
|
||||
|
||||
private readonly int _intervalMs;
|
||||
private CancellationTokenSource? _cts;
|
||||
private Task? _loopTask;
|
||||
|
||||
// ── PID state ────────────────────────────────────────────────────────────
|
||||
|
||||
private double _errSum;
|
||||
private double _lastPv;
|
||||
private Stopwatch _sw = new();
|
||||
|
||||
/// <summary>True when the PID loop is actively running.</summary>
|
||||
public bool IsRunning { get; private set; }
|
||||
|
||||
// ── Constructor ──────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new PID controller for bench RPM control.
|
||||
/// </summary>
|
||||
/// <param name="kp">Proportional gain.</param>
|
||||
/// <param name="ki">Integral gain.</param>
|
||||
/// <param name="kd">Derivative gain.</param>
|
||||
/// <param name="intervalMs">Loop interval in milliseconds.</param>
|
||||
/// <param name="pvMax">Maximum process variable (RPM). Minimum is always 0.</param>
|
||||
/// <param name="outMax">Maximum output (voltage). Minimum is always 0.</param>
|
||||
/// <param name="readActualRpm">Delegate to read the current bench RPM.</param>
|
||||
/// <param name="readTargetRpm">Delegate to read the desired RPM setpoint.</param>
|
||||
/// <param name="sendVoltage">Delegate to write the output voltage to the CAN bus.</param>
|
||||
public BenchPidController(
|
||||
double kp, double ki, double kd, int intervalMs,
|
||||
double pvMax, double outMax,
|
||||
Func<double> readActualRpm,
|
||||
Func<double> readTargetRpm,
|
||||
Action<double> sendVoltage)
|
||||
{
|
||||
_kp = kp;
|
||||
_ki = ki;
|
||||
_kd = kd;
|
||||
_intervalMs = Math.Max(1, intervalMs);
|
||||
_pvMax = pvMax;
|
||||
_outMax = outMax;
|
||||
_readActualRpm = readActualRpm;
|
||||
_readTargetRpm = readTargetRpm;
|
||||
_sendVoltage = sendVoltage;
|
||||
}
|
||||
|
||||
// ── Public API ───────────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// Starts the PID loop with bumpless transfer from the given initial conditions.
|
||||
/// If already running, stops first before restarting.
|
||||
/// </summary>
|
||||
/// <param name="initialVoltage">Current motor voltage (for bumpless startup).</param>
|
||||
/// <param name="initialRpm">Current actual RPM (for bumpless startup).</param>
|
||||
public void Start(double initialVoltage, double initialRpm)
|
||||
{
|
||||
if (IsRunning) Stop();
|
||||
|
||||
// Bumpless startup: pre-initialize integral sum so the first output
|
||||
// matches the current voltage, avoiding a step response.
|
||||
double normalizedOutput = Scale(initialVoltage, 0, _outMax, 0, 1.0);
|
||||
_errSum = _ki != 0 ? normalizedOutput / _ki : 0;
|
||||
_lastPv = Scale(Clamp(initialRpm, 0, _pvMax), 0, _pvMax, 0, 1.0);
|
||||
_sw = Stopwatch.StartNew();
|
||||
|
||||
IsRunning = true;
|
||||
_cts = new CancellationTokenSource();
|
||||
var ct = _cts.Token;
|
||||
|
||||
_loopTask = Task.Run(async () =>
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(_intervalMs));
|
||||
try
|
||||
{
|
||||
while (IsRunning && await timer.WaitForNextTickAsync(ct))
|
||||
{
|
||||
Compute();
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}, ct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the PID loop and sends 0 V to the motor.
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
if (!IsRunning) return;
|
||||
IsRunning = false;
|
||||
_cts?.Cancel();
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
_sendVoltage(0);
|
||||
}
|
||||
|
||||
// ── PID core ─────────────────────────────────────────────────────────────
|
||||
|
||||
private void Compute()
|
||||
{
|
||||
double pv = _readActualRpm();
|
||||
double sp = _readTargetRpm();
|
||||
|
||||
// Clamp and normalize to 0–1.0
|
||||
pv = Scale(Clamp(pv, 0, _pvMax), 0, _pvMax, 0, 1.0);
|
||||
sp = Scale(Clamp(sp, 0, _pvMax), 0, _pvMax, 0, 1.0);
|
||||
|
||||
double err = sp - pv;
|
||||
|
||||
// Time delta in seconds
|
||||
double dt = _sw.Elapsed.TotalSeconds;
|
||||
_sw.Restart();
|
||||
if (dt <= 0) dt = _intervalMs / 1000.0;
|
||||
|
||||
// P term
|
||||
double pTerm = _kp * err;
|
||||
|
||||
// I term (with anti-windup: only integrate when PV is in-range)
|
||||
double partialSum = _errSum;
|
||||
double iTerm = 0;
|
||||
if (pv >= 0 && pv <= 1.0)
|
||||
{
|
||||
partialSum = _errSum + dt * err;
|
||||
iTerm = _ki * partialSum;
|
||||
}
|
||||
|
||||
// D term (derivative-on-measurement, not on error)
|
||||
double dTerm = 0;
|
||||
if (dt > 0)
|
||||
dTerm = _kd * (pv - _lastPv) / dt;
|
||||
|
||||
// Combine, clamp, scale to output voltage range
|
||||
double output = Clamp(pTerm + iTerm + dTerm, 0, 1.0);
|
||||
double voltage = Scale(output, 0, 1.0, 0, _outMax);
|
||||
|
||||
_sendVoltage(voltage);
|
||||
|
||||
// Update state
|
||||
_errSum = partialSum;
|
||||
_lastPv = pv;
|
||||
}
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
private static double Clamp(double value, double min, double max)
|
||||
=> value > max ? max : value < min ? min : value;
|
||||
|
||||
private static double Scale(double value, double fromMin, double fromMax, double toMin, double toMax)
|
||||
{
|
||||
if (Math.Abs(fromMax - fromMin) < 1e-12) return toMin;
|
||||
double pct = (value - fromMin) / (fromMax - fromMin);
|
||||
return toMin + pct * (toMax - toMin);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
IsRunning = false;
|
||||
_cts?.Cancel();
|
||||
_cts?.Dispose();
|
||||
_cts = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,18 @@ namespace HC_APTBS.Services.Impl
|
||||
private CancellationTokenSource? _electronicMsgCts;
|
||||
private volatile bool _electronicMsgActive;
|
||||
|
||||
// Periodic relay bitmask sender
|
||||
private const int RelaySendIntervalMs = 21;
|
||||
private Task? _relaySendTask;
|
||||
private CancellationTokenSource? _relaySendCts;
|
||||
private volatile bool _relaySendActive;
|
||||
|
||||
// RPM PID ramp controller
|
||||
private BenchPidController? _pidController;
|
||||
private double _lastTargetRpm;
|
||||
private double _lastCommandVoltage;
|
||||
private double _pidTargetRpm;
|
||||
|
||||
// ── Events ────────────────────────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -78,6 +90,8 @@ namespace HC_APTBS.Services.Impl
|
||||
public event Action<string, bool>? PhaseCompleted;
|
||||
/// <inheritdoc/>
|
||||
public event Action<string, double>? PumpControlValueSet;
|
||||
/// <inheritdoc/>
|
||||
public event Action? RpmCommandSent;
|
||||
|
||||
// ── Constructor ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -96,6 +110,12 @@ namespace HC_APTBS.Services.Impl
|
||||
/// <inheritdoc/>
|
||||
public bool IsAutoDfiEnabled => _autoDfiEnabled;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double LastTargetRpm => _lastTargetRpm;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public double LastCommandVoltage => _lastCommandVoltage;
|
||||
|
||||
// ── IBenchService: active pump ────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -161,14 +181,93 @@ namespace HC_APTBS.Services.Impl
|
||||
double voltage = RpmVoltageRelation.VoltageForRpm(
|
||||
(int)safeRpm, _config.Settings.Relations);
|
||||
|
||||
// Write the voltage value into the RPM parameter and transmit.
|
||||
SetParameter(BenchParameterNames.Rpm, voltage);
|
||||
SendParameters(_config.Bench.ParametersByName.TryGetValue(
|
||||
BenchParameterNames.Rpm, out var rpmParam) ? rpmParam.MessageId : 0x0A);
|
||||
SendRpmVoltage(voltage);
|
||||
|
||||
_lastTargetRpm = safeRpm;
|
||||
RpmCommandSent?.Invoke();
|
||||
_log.Debug(LogId, $"SetRpm({safeRpm}) → voltage={voltage:F3}V");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StartRpmPid(double targetRpm)
|
||||
{
|
||||
// Stop any existing PID loop.
|
||||
_pidController?.Stop();
|
||||
|
||||
double safeRpm = Math.Min(targetRpm, _config.Settings.SecurityRpmLimit);
|
||||
_lastTargetRpm = safeRpm;
|
||||
_pidTargetRpm = safeRpm;
|
||||
|
||||
if (safeRpm <= 0)
|
||||
{
|
||||
SendRpmVoltage(0);
|
||||
RpmCommandSent?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Send initial voltage from lookup table (open-loop jump).
|
||||
double initialVoltage = RpmVoltageRelation.VoltageForRpm(
|
||||
(int)safeRpm, _config.Settings.Relations);
|
||||
SendRpmVoltage(initialVoltage);
|
||||
RpmCommandSent?.Invoke();
|
||||
|
||||
// Step 2: Calculate approach delay based on RPM distance.
|
||||
double actualRpm = ReadBenchParameter(BenchParameterNames.BenchRpm);
|
||||
int delaySec = (int)(Math.Abs(safeRpm - actualRpm) * 0.004 + 0.7);
|
||||
|
||||
// Step 3: Create PID controller and start after delay.
|
||||
var settings = _config.Settings;
|
||||
_pidController = new BenchPidController(
|
||||
settings.PidP, settings.PidI, settings.PidD, settings.PidLoopMs,
|
||||
settings.MaxRpm, settings.VoltageForMaxRpm,
|
||||
() => ReadBenchParameter(BenchParameterNames.BenchRpm),
|
||||
() => _pidTargetRpm,
|
||||
SendRpmVoltage);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(delaySec * 1000);
|
||||
if (_pidTargetRpm > 0)
|
||||
_pidController.Start(initialVoltage, actualRpm);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(LogId, $"StartRpmPid delay task: {ex.Message}");
|
||||
}
|
||||
});
|
||||
|
||||
_log.Info(LogId, $"StartRpmPid({safeRpm}) → initial voltage={initialVoltage:F3}V, PID delay={delaySec}s");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StopRpmPid()
|
||||
{
|
||||
_pidTargetRpm = 0;
|
||||
_pidController?.Stop();
|
||||
_pidController?.Dispose();
|
||||
_pidController = null;
|
||||
_lastTargetRpm = 0;
|
||||
SendRpmVoltage(0);
|
||||
RpmCommandSent?.Invoke();
|
||||
_log.Info(LogId, "StopRpmPid: PID stopped, 0V sent.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a voltage value to the RPM CAN parameter and transmits it.
|
||||
/// Used as the PID output callback and by <see cref="SetRpm"/>.
|
||||
/// </summary>
|
||||
private void SendRpmVoltage(double volts)
|
||||
{
|
||||
if (volts < 0) volts = 0;
|
||||
_lastCommandVoltage = volts;
|
||||
|
||||
SetParameter(BenchParameterNames.Rpm, volts);
|
||||
SendParameters(_config.Bench.ParametersByName.TryGetValue(
|
||||
BenchParameterNames.Rpm, out var rpmParam) ? rpmParam.MessageId : 0x10);
|
||||
}
|
||||
|
||||
// ── IBenchService: temperature ────────────────────────────────────────────
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -193,6 +292,58 @@ namespace HC_APTBS.Services.Impl
|
||||
TransmitRelayMask(relay.MessageId);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void SendInitialRelayState()
|
||||
{
|
||||
// Collect all distinct relay message IDs and transmit each one.
|
||||
var sent = new HashSet<uint>();
|
||||
foreach (var relay in _config.Bench.Relays.Values)
|
||||
{
|
||||
if (sent.Add(relay.MessageId))
|
||||
TransmitRelayMask(relay.MessageId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StartRelaySender()
|
||||
{
|
||||
if (_relaySendActive) return;
|
||||
_relaySendActive = true;
|
||||
|
||||
_relaySendCts = new CancellationTokenSource();
|
||||
var ct = _relaySendCts.Token;
|
||||
|
||||
_relaySendTask = Task.Run(async () =>
|
||||
{
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(RelaySendIntervalMs));
|
||||
try
|
||||
{
|
||||
while (_relaySendActive && await timer.WaitForNextTickAsync(ct))
|
||||
{
|
||||
var sent = new HashSet<uint>();
|
||||
foreach (var relay in _config.Bench.Relays.Values)
|
||||
{
|
||||
if (sent.Add(relay.MessageId))
|
||||
TransmitRelayMask(relay.MessageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
}, ct);
|
||||
|
||||
_log.Debug(LogId, "Relay sender started.");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void StopRelaySender()
|
||||
{
|
||||
_relaySendActive = false;
|
||||
_relaySendCts?.Cancel();
|
||||
_relaySendCts?.Dispose();
|
||||
_relaySendCts = null;
|
||||
_log.Debug(LogId, "Relay sender stopped.");
|
||||
}
|
||||
|
||||
private void TransmitRelayMask(uint messageId)
|
||||
{
|
||||
// Collect all relays mapped to this CAN message ID and pack their bits.
|
||||
|
||||
@@ -577,38 +577,38 @@ namespace HC_APTBS.Services.Impl
|
||||
/// <summary>Returns the factory-default bench parameter XML used when bench.xml is absent.</summary>
|
||||
private static string DefaultBenchXml() => @"<?xml version=""1.0"" encoding=""utf-8""?>
|
||||
<Bench>
|
||||
<RPM id=""A"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<Counter id=""B"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<BaudRate id=""37"" byteh=""0"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<BenchRPM id=""D"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" />
|
||||
<BenchCounter id=""D"" byteh=""3"" bytel=""2"" filter=""1"" disableparams=""true"" />
|
||||
<BenchTemp id=""E"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T-in id=""E"" byteh=""3"" bytel=""2"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T-out id=""E"" byteh=""5"" bytel=""4"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T4 id=""E"" byteh=""7"" bytel=""6"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<RPM id=""10"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<Counter id=""11"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<BaudRate id=""55"" byteh=""0"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<BenchRPM id=""13"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""true"" />
|
||||
<BenchCounter id=""13"" byteh=""3"" bytel=""2"" filter=""1"" disableparams=""true"" />
|
||||
<BenchTemp id=""14"" byteh=""1"" bytel=""0"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T-in id=""14"" byteh=""3"" bytel=""2"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T-out id=""14"" byteh=""5"" bytel=""4"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<T4 id=""14"" byteh=""7"" bytel=""6"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""10"" p5=""-20"" p6=""0"" />
|
||||
<QDelivery id=""8"" byteh=""5"" bytel=""3"" filter=""0.01"" disableparams=""false"" p1=""0"" p2=""2.03"" p3=""1E-06"" p4=""0"" p5=""0"" p6=""0"" />
|
||||
<QOver id=""8"" byteh=""2"" bytel=""0"" filter=""0.11"" disableparams=""false"" p1=""0"" p2=""0.51"" p3=""1E-06"" p4=""0"" p5=""0"" p6=""0"" />
|
||||
<PSGEncoderValue id=""32"" byteh=""4"" bytel=""5"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<PSGEncoderWorking id=""32"" byteh=""7"" bytel=""7"" filter=""1"" disableparams=""true"" />
|
||||
<InyectorEncoderValue id=""32"" byteh=""2"" bytel=""3"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<InyectorEncoderWorking id=""32"" byteh=""6"" bytel=""6"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<ManualEncoderValue id=""32"" byteh=""0"" bytel=""1"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<EncoderResolution id=""33"" byteh=""6"" bytel=""7"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<ElectronicMsg id=""33"" byteh=""0"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<PSGEncoderValue id=""50"" byteh=""4"" bytel=""5"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<PSGEncoderWorking id=""50"" byteh=""7"" bytel=""7"" filter=""1"" disableparams=""true"" />
|
||||
<InyectorEncoderValue id=""50"" byteh=""2"" bytel=""3"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<InyectorEncoderWorking id=""50"" byteh=""6"" bytel=""6"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<ManualEncoderValue id=""50"" byteh=""0"" bytel=""1"" filter=""1"" disableparams=""false"" p1=""1"" p2=""0"" p3=""0"" p4=""1"" p5=""0"" p6=""0"" />
|
||||
<EncoderResolution id=""51"" byteh=""6"" bytel=""7"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<ElectronicMsg id=""51"" byteh=""0"" bytel=""0"" filter=""1"" disableparams=""true"" send=""true"" />
|
||||
<Alarms id=""8"" byteh=""7"" bytel=""6"" filter=""1"" disableparams=""true"" />
|
||||
<Pressure id=""D"" byteh=""4"" bytel=""5"" filter=""1"" disableparams=""true"" />
|
||||
<AnalogicSensor2 id=""D"" byteh=""6"" bytel=""7"" filter=""1"" disableparams=""true"" />
|
||||
<Pressure id=""13"" byteh=""4"" bytel=""5"" filter=""1"" disableparams=""true"" />
|
||||
<AnalogicSensor2 id=""13"" byteh=""6"" bytel=""7"" filter=""1"" disableparams=""true"" />
|
||||
<Reles>
|
||||
<Rele name=""Electronic"" id=""F"" bit=""0"" />
|
||||
<Rele name=""OilPump"" id=""F"" bit=""4"" />
|
||||
<Rele name=""DepositCooler"" id=""F"" bit=""8"" />
|
||||
<Rele name=""DepositHeater"" id=""F"" bit=""12"" />
|
||||
<Rele name=""Reserve"" id=""F"" bit=""16"" />
|
||||
<Rele name=""Counter"" id=""F"" bit=""20"" />
|
||||
<Rele name=""Direction"" id=""F"" bit=""24"" />
|
||||
<Rele name=""TinCooler"" id=""F"" bit=""28"" />
|
||||
<Rele name=""Pulse4Signal"" id=""F"" bit=""32"" />
|
||||
<Rele name=""Flasher"" id=""F"" bit=""44"" />
|
||||
<Rele name=""Electronic"" id=""15"" bit=""0"" />
|
||||
<Rele name=""OilPump"" id=""15"" bit=""4"" />
|
||||
<Rele name=""DepositCooler"" id=""15"" bit=""8"" />
|
||||
<Rele name=""DepositHeater"" id=""15"" bit=""12"" />
|
||||
<Rele name=""Reserve"" id=""15"" bit=""16"" />
|
||||
<Rele name=""Counter"" id=""15"" bit=""20"" />
|
||||
<Rele name=""Direction"" id=""15"" bit=""24"" />
|
||||
<Rele name=""TinCooler"" id=""15"" bit=""28"" />
|
||||
<Rele name=""Pulse4Signal"" id=""15"" bit=""32"" />
|
||||
<Rele name=""Flasher"" id=""15"" bit=""44"" />
|
||||
</Reles>
|
||||
</Bench>";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user