feat(heidenhain): native read-only LSV2 driver + in-process simulator (#803)

Native LSV2 TCP transport (default port 19000; no Java/Python/vendor SDK), ported from
the ignitionoee v0.20.3 reference driver:

- Lsv2ReadOnlyPolicy: payload-aware outbound allowlist (A_LG INSPECT/DNC/DATA, R_VR, R_PR,
  R_RI 22-28/30-34/51, bounded R_MB, R_DP), applied in Lsv2Codec.Frame before any byte is
  written; no public raw-command API.
- Lsv2Client: serialized transactions, one monotonic deadline per transaction, EOF/oversize/
  unexpected/trickle close the client; Close never waits on the gate.
- Lsv2Data / HeidenhainBindingCodec: reference decoders, R_PR byte-capacity + element-index
  rules, next-area cap, DATA Boolean 00/01/FF, strict non-truncating coercion.
- HeidenhainSession: per-batch wire cache, refusal suppression, provisional DNC denial retry,
  alarm cursor (57 = end, overflow = BadOutOfRange), capability strings.
- HeidenhainDeviceRuntime: one loop per control, generation-guarded publication, all-node
  BadNoCommunication on comms loss, bounded exponential reconnect.
- HeidenhainDriver: cached ReadAsync, push subscriptions deduped on (value,status), static
  catalog discovery (no connect), host status, diagnostics, skipped-tag report. No IWritable.
- Probe (INSPECT login + version) and factory registered in DriverFactoryBootstrap (probe on
  admin nodes too); DriverTypeNames.Heidenhain.

Tests: 159 unit tests against an in-process LSV2 simulator incl. the Java fixtures' captured
iTNC 530 replies as golden vectors; env-gated LiveIntegration category for real hardware.
This commit is contained in:
Joseph Doherty
2026-11-10 23:08:30 -05:00
parent 308b1b3b56
commit d50ee809e3
38 changed files with 5205 additions and 0 deletions
@@ -24,6 +24,7 @@ using CalculationProbe = Driver.Calculation.CalculationDriverProbe;
using SqlProbe = Driver.Sql.SqlDriverProbe;
using MqttProbe = Driver.Mqtt.MqttDriverProbe;
using MTConnectProbe = Driver.MTConnect.MTConnectDriverProbe;
using HeidenhainProbe = Driver.Heidenhain.HeidenhainDriverProbe;
/// <summary>
/// Wires every cross-platform driver assembly's <c>Register(registry, loggerFactory)</c>
@@ -195,6 +196,11 @@ public static class DriverFactoryBootstrap
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDriverProbe, SqlProbe>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDriverProbe, MqttProbe>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDriverProbe, MTConnectProbe>());
// Factory-built so the probe resolves secret: INSPECT password references through the node's resolver
// (registered on every node by AddZbSecrets; optional so a test graph without one still builds — a
// secret: ref then fails closed with a clear probe message rather than sending the literal).
services.TryAddEnumerable(ServiceDescriptor.Singleton<IDriverProbe, HeidenhainProbe>(
sp => new HeidenhainProbe(sp.GetService<ISecretResolver>())));
return services;
}
@@ -253,6 +259,9 @@ public static class DriverFactoryBootstrap
// tier either — SubscribeAsync returns synchronously after starting the pump on a background
// task, so the Tier A 5s Subscribe budget never covers the stream's lifetime.
Driver.MTConnect.MTConnectDriverFactoryExtensions.Register(registry, loggerFactory);
// Tier A (default): managed TCP only, no native SDK. Read-only by construction (no IWritable; LSV2
// outbound allowlist). The resolver backs secret: password references, resolved per login.
Driver.Heidenhain.HeidenhainDriverFactoryExtensions.Register(registry, loggerFactory, secretResolver);
Driver.OpcUaClient.OpcUaClientDriverFactoryExtensions.Register(registry, loggerFactory, secretResolver);
Driver.S7.S7DriverFactoryExtensions.Register(registry, loggerFactory);
Driver.Sql.SqlDriverFactoryExtensions.Register(registry, loggerFactory);
@@ -85,6 +85,7 @@
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Modbus\ZB.MOM.WW.OtOpcUa.Driver.Modbus.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Mqtt\ZB.MOM.WW.OtOpcUa.Driver.Mqtt.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.MTConnect\ZB.MOM.WW.OtOpcUa.Driver.MTConnect.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Heidenhain\ZB.MOM.WW.OtOpcUa.Driver.Heidenhain.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.S7\ZB.MOM.WW.OtOpcUa.Driver.S7.csproj"/>
<ProjectReference Include="..\..\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Sql\ZB.MOM.WW.OtOpcUa.Driver.Sql.csproj"/>