Deploy gate: Heidenhain LSV2 driver live commissioning on a real control #829

Closed
opened 2026-11-11 00:06:00 -05:00 by dohertj2 · 4 comments
Owner

#803 shipped the read-only LSV2 driver proven against an in-process simulator and replayed iTNC 530 golden vectors only. On real hardware (set HEIDENHAIN_LSV2_HOST, OTOPCUA_HEIDENHAIN_INSPECT_PASSWORD): run the LiveIntegration tests; browse/read through a deployed server + OPC UA client; confirm write refusal, outage/recovery, traffic under load and retained evidence. Open questions: control families beyond iTNC 530, successful R_MB PLC memory decoding (denied on all four Java-tested controls), axis/tool units, timer selector (30–34) semantics, DNC/DATA permissions per control.

#803 shipped the read-only LSV2 driver proven against an in-process simulator and replayed iTNC 530 golden vectors only. On real hardware (set `HEIDENHAIN_LSV2_HOST`, `OTOPCUA_HEIDENHAIN_INSPECT_PASSWORD`): run the LiveIntegration tests; browse/read through a deployed server + OPC UA client; confirm write refusal, outage/recovery, traffic under load and retained evidence. Open questions: control families beyond iTNC 530, successful `R_MB` PLC memory decoding (denied on all four Java-tested controls), axis/tool units, timer selector (30–34) semantics, DNC/DATA permissions per control.
dohertj2 added the type:deploy-gatepriority:p2blocked labels 2026-11-11 00:06:00 -05:00
Author
Owner

2026-11-11 live gate (read-only, owner-approved) through a deployed server on four production iTNC 530 controls: browse/read PASS, writes refused (56/56, none sent), outage/recovery PASS via a proxy, and load ≈5 req/s per control. #867 (Double tool leaves) is fixed and re-verified live. Still unproven: other control families, R_MB, axis/tool units, timers and DATA. Evidence: docs/drivers/evidence/heidenhain-live-2026-11-11.md (pushed).

2026-11-11 live gate (read-only, owner-approved) through a deployed server on four production iTNC 530 controls: browse/read PASS, writes refused (56/56, none sent), outage/recovery PASS via a proxy, and load ≈5 req/s per control. #867 (Double tool leaves) is fixed and re-verified live. Still unproven: other control families, R_MB, axis/tool units, timers and DATA. Evidence: docs/drivers/evidence/heidenhain-live-2026-11-11.md (pushed).
Author
Owner

Re-run of the 2026-11-11 gate (docs/drivers/evidence/heidenhain-live-2026-11-11.md) on the current image. It confirms that record. New here: the LiveIntegration tool-type test failed on Hermle49 (R_RI 51 unsupported) and is fixed in 0821e27c; outage tested as a blackhole (docker pause) as well as a hard drop; and the load was measured with both hot-pair nodes running (2 sessions per control, ~9.6 req/s total), where 11-11 stopped the partner to keep one session.

Live gate run 2026-11-12/13 — four real iTNC 530 controls (read-only)

Controls from the ignitionoee 0.20.3 validation (docs/recipes/evidence/heidenhain-final-0.20.3-2026-11-03), reached over the Mac's VPN. INSPECT login with an empty password only; nothing was written to any control; no controller settings touched.

Control Endpoint NC / PLC
Hermle49 10.201.29.225:19000 340490 03 SP10 / iC30V6.1_2SP3
Hermle59 10.201.29.226:19000 340490 04 SP10 / iC30v7.1_1
Hermle70 10.201.22.225:19000 606420 03 SP4 / iC22_V20.1_2SP2
Hermle29 10.201.22.215:19000 606420 03 SP4 / iC22_V20.1_2SP2

1. Category=LiveIntegration (direct driver), one control at a time

  • All four: INSPECT identity, R_PR layout (WORD base 64, 10000 bytes), driver end-to-end status poll pass.
  • R_RI 22/23/24/26 answer on all four; 25 (overrides) refused code 23 → AccessDenied on all four.
  • R_RI 51 (tool) answers on 59/70/29 (24 bytes) → Tool/Number Int64, Tool/Index/AxisCode Int32 (#867 confirmed on hardware). Hermle49 refuses 51 with code 24 → Unsupported; the driver publishes BadNotSupported. The tool-type test asserted >0 Good and failed there — fixed to skip in 0821e27c.

2. Deployed server + OPC UA client (docker-dev, SITE-A pair, opc.tcp://localhost:4842)

One Heidenhain driver, 4 devices × 18 fixed leaves + one ALARM:* condition, authored straight into ConfigDb and deployed via the headless API. (The deploy gate refused my first attempt — HeidenhainTagDataTypeMismatch, Tag.DataType Float64 must be Double — correct behaviour, authoring error.)

  • Browse: ns=2;s=heid/<Control>/<Leaf> tree + the AnyAlarm condition's Part 9 children, all four controls.
  • Read (all 76 + condition fields): Controller/Status/Program/Axes(arrays)/Tool/Alarms(arrays)/Capabilities Good with live values (e.g. Hermle59 ProgramLine advancing 56506→59207, ToolLength 198.1454); Overrides/Feed BadUserAccessDenied on all four; Hermle49 Tool/* BadNotSupported.
  • Write refusal: anonymous write to Hermle59/Model and Hermle59/ToolNumber → BadNotWritable, value unchanged. (An authenticated encrypted session couldn't be opened — the CLI client cert isn't trusted by the rig; the node AccessLevel refusal applies to every user anyway.)
  • Outage / recovery (Hermle59 behind a TCP proxy on the rig network; other three direct):
    • Blackhole (docker pause proxy): detected at the 5 s transaction deadline; every remote leaf BadNoCommunication, Capabilities/Execution = CommunicationLost, alarm condition quality BadNoCommunication (state kept); other controls stayed Good. Bounded backoff (connect attempts at +6/+7/+9/+10/+16 s); recovered Good 18 s after unpause, alarm list readable again.
    • Hard drop (docker kill proxy, name unresolvable): same Bad fan-out within 8 s; recovered 6 s after restart.
  • Traffic: both SITE-A nodes (hot pair) each hold their own LSV2 session — 2 sessions per control. Measured at the proxy with default polling (1 s status / 2 s alarms / 10 s slow, no PLC/DATA bindings): 288 requests/min per session (~4.8/s), ~2.9 KB up / ~10.4 KB down per minute, identical per node and steady over 2 min ⇒ ~9.6 req/s per control from one server pair. Note against the controls' connection limits (ignitionoee saw 2 controls report connection limits).

Cleanup: rows deleted, cleanup deploy sealed on all 6 nodes, driver disposed on both nodes, proxy removed.

Still open

  • Controls beyond iTNC 530; raw R_MB decoding (no PLC/DATA bindings exercised here); DATA role; axis/tool units vs the HMI; timer selectors 30–34; per-control DNC/DATA permissions; load with a large binding set; production deploy.
> **Re-run of the 2026-11-11 gate** (`docs/drivers/evidence/heidenhain-live-2026-11-11.md`) on the current image. It confirms that record. New here: the LiveIntegration tool-type test failed on Hermle49 (R_RI 51 unsupported) and is fixed in `0821e27c`; outage tested as a blackhole (`docker pause`) as well as a hard drop; and the load was measured with **both** hot-pair nodes running (2 sessions per control, ~9.6 req/s total), where 11-11 stopped the partner to keep one session. ## Live gate run 2026-11-12/13 — four real iTNC 530 controls (read-only) Controls from the ignitionoee 0.20.3 validation (`docs/recipes/evidence/heidenhain-final-0.20.3-2026-11-03`), reached over the Mac's VPN. INSPECT login with an empty password only; nothing was written to any control; no controller settings touched. | Control | Endpoint | NC / PLC | |---|---|---| | Hermle49 | 10.201.29.225:19000 | 340490 03 SP10 / iC30V6.1_2SP3 | | Hermle59 | 10.201.29.226:19000 | 340490 04 SP10 / iC30v7.1_1 | | Hermle70 | 10.201.22.225:19000 | 606420 03 SP4 / iC22_V20.1_2SP2 | | Hermle29 | 10.201.22.215:19000 | 606420 03 SP4 / iC22_V20.1_2SP2 | ### 1. `Category=LiveIntegration` (direct driver), one control at a time - All four: INSPECT identity, `R_PR` layout (WORD base 64, 10000 bytes), driver end-to-end status poll **pass**. - `R_RI` 22/23/24/26 answer on all four; **25 (overrides) refused code 23 → AccessDenied on all four**. - `R_RI 51` (tool) answers on 59/70/29 (24 bytes) → `Tool/Number` Int64, `Tool/Index`/`AxisCode` Int32 (**#867 confirmed on hardware**). **Hermle49 refuses 51 with code 24 → Unsupported**; the driver publishes `BadNotSupported`. The tool-type test asserted >0 Good and failed there — fixed to skip in `0821e27c`. ### 2. Deployed server + OPC UA client (docker-dev, SITE-A pair, `opc.tcp://localhost:4842`) One `Heidenhain` driver, 4 devices × 18 fixed leaves + one `ALARM:*` condition, authored straight into ConfigDb and deployed via the headless API. (The deploy gate refused my first attempt — `HeidenhainTagDataTypeMismatch`, Tag.DataType `Float64` must be `Double` — correct behaviour, authoring error.) - **Browse:** `ns=2;s=heid/<Control>/<Leaf>` tree + the `AnyAlarm` condition's Part 9 children, all four controls. - **Read (all 76 + condition fields):** Controller/Status/Program/Axes(arrays)/Tool/Alarms(arrays)/Capabilities Good with live values (e.g. Hermle59 `ProgramLine` advancing 56506→59207, `ToolLength` 198.1454); `Overrides/Feed` `BadUserAccessDenied` on all four; Hermle49 Tool/* `BadNotSupported`. - **Write refusal:** anonymous write to `Hermle59/Model` and `Hermle59/ToolNumber` → **`BadNotWritable`**, value unchanged. (An authenticated encrypted session couldn't be opened — the CLI client cert isn't trusted by the rig; the node AccessLevel refusal applies to every user anyway.) - **Outage / recovery** (Hermle59 behind a TCP proxy on the rig network; other three direct): - *Blackhole* (`docker pause` proxy): detected at the 5 s transaction deadline; every remote leaf `BadNoCommunication`, `Capabilities/Execution` = `CommunicationLost`, alarm condition quality `BadNoCommunication` (state kept); other controls stayed Good. Bounded backoff (connect attempts at +6/+7/+9/+10/+16 s); **recovered Good 18 s after unpause**, alarm list readable again. - *Hard drop* (`docker kill` proxy, name unresolvable): same Bad fan-out within 8 s; **recovered 6 s after restart**. - **Traffic:** both SITE-A nodes (hot pair) each hold their own LSV2 session — **2 sessions per control**. Measured at the proxy with default polling (1 s status / 2 s alarms / 10 s slow, no PLC/DATA bindings): **288 requests/min per session** (~4.8/s), ~2.9 KB up / ~10.4 KB down per minute, identical per node and steady over 2 min ⇒ ~9.6 req/s per control from one server pair. Note against the controls' connection limits (ignitionoee saw 2 controls report connection limits). Cleanup: rows deleted, cleanup deploy sealed on all 6 nodes, driver disposed on both nodes, proxy removed. ### Still open - Controls beyond iTNC 530; raw `R_MB` decoding (no PLC/DATA bindings exercised here); DATA role; axis/tool units vs the HMI; timer selectors 30–34; per-control DNC/DATA permissions; load with a large binding set; production deploy.
Author
Owner

Pushed a4ce417d: best-effort coverage of the non-iTNC items from pyLSV2 (MIT) as the reference, since we have no other control families. Read-only boundary unchanged (no new request, login or selector).

  • Control families: Controller/Family (ITnc530/Tnc/Tnc7/Lathe/MillPlus/LegacyTnc/Unknown) classified from R_VR; DATA gated to iTNC 530; simulator profiles per family. Non-iTNC rows are per pyLSV2, hardware-unproven (docs §Control families).
  • Runtime info: every R_RI decoder matches pyLSV2 byte-for-byte (no decode bug); additive Status/ExecutionStateName, Status/ProgramStateName, Tool/AxisName; timers 30–34 documented as lifetime seconds counters per pyLSV2 (still off by default); refusals 52/72/58/56/57 map to specific statuses.
  • PLC/DATA: FF-true bits, INPUT_DWORD/OUTPUT_DWORD, pyLSV2 address correspondence. R_MB answer: pyLSV2 reads PLC memory only after a PLCDEBUG login, a write-capable role (also used for machine-parameter writes) → incompatible with this read-only driver; raw R_MB stays refused, DATA:/PLC/memory/... is the read-only path.
  • New live test Real_control_derived_name_leaves passed on Hermle49/59/70/29 (Family ITnc530, Automatic/Idle, axis Z; Hermle49 axis BadNotSupported).

Still hardware-gated: anything non-iTNC, units vs HMI, timers on a real control, C_CC buffer negotiation and R_RI 20/21 (both outside the allowlist).

Pushed `a4ce417d`: best-effort coverage of the non-iTNC items from pyLSV2 (MIT) as the reference, since we have no other control families. Read-only boundary unchanged (no new request, login or selector). - **Control families**: `Controller/Family` (ITnc530/Tnc/Tnc7/Lathe/MillPlus/LegacyTnc/Unknown) classified from R_VR; DATA gated to iTNC 530; simulator profiles per family. Non-iTNC rows are *per pyLSV2, hardware-unproven* (docs §Control families). - **Runtime info**: every R_RI decoder matches pyLSV2 byte-for-byte (no decode bug); additive `Status/ExecutionStateName`, `Status/ProgramStateName`, `Tool/AxisName`; timers 30–34 documented as lifetime seconds counters per pyLSV2 (still off by default); refusals 52/72/58/56/57 map to specific statuses. - **PLC/DATA**: FF-true bits, `INPUT_DWORD`/`OUTPUT_DWORD`, pyLSV2 address correspondence. **R_MB answer:** pyLSV2 reads PLC memory only after a `PLCDEBUG` login, a write-capable role (also used for machine-parameter writes) → incompatible with this read-only driver; raw R_MB stays refused, `DATA:/PLC/memory/...` is the read-only path. - New live test `Real_control_derived_name_leaves` passed on Hermle49/59/70/29 (Family ITnc530, Automatic/Idle, axis Z; Hermle49 axis BadNotSupported). Still hardware-gated: anything non-iTNC, units vs HMI, timers on a real control, C_CC buffer negotiation and R_RI 20/21 (both outside the allowlist).
Author
Owner

Hardware evidence from the ignitionoee CR capture (2026-11-13), and acceptance.

ignitionoee 6d2aec40 (docs/recipes/evidence/heidenhain-runstate-sample-2026-11-13/) took a 7 h read-only OPC UA sample (04:51–11:50Z, every 5 s, 17 leaves) of the 34 Heidenhain controls on CR's production Ignition gateway. That driver is the Java LSV2 implementation this driver was ported from. It has the same read set, refusal mapping and state codes. Findings compared with this driver:

Finding on real controls This driver Code change?
26 iTNC 530 (NC 340422 / 340490 / 606420): ProgramState 0 coincides with axis motion in 11,865 of 11,869 sample pairs; 7 = idle, no motion Publishes raw R_RI 26 Int32 + ProgramStateName from the pyLSV2 table (0 Started … 7 Idle) No. The capture confirms the table.
Program end goes 0 → 7 directly, never through 2 Finished; 1/4/5/6 not seen in 7 h Names only; there is no state logic No
5 TNC 426/430 (NC 280476): status/program/axes/alarms BadNotSupported, capability Unsupported Unknown telegram / wrong parameter → BadNotSupported + Unsupported, suppressed until reconnect No. The capture confirms the behaviour on a second control family.
Overrides (R_RI 25) BadUserAccessDenied on every iTNC (INSPECT) NoPrivilege / NoLogin → BadUserAccessDenied / AccessDenied No
ExecutionState 5 Undefined on idle machines Raw code + Undefined name No
Capability reads Unknown until the feature's first poll CapabilityState returns Unknown until the first attempt No. Same behaviour, by design.
Comms loss → BadNoCommunication, recovered on reconnect (Z-18985 dropped and came back during the capture) Quality-only comms loss No
Alarm classes 1 / 7 / 8 present, alarm count > 0 during ~28 % of cutting Alarms are labels; #839/#853 No (see #853)

Accepted per owner decision 2026-11-13: the hardware evidence does not change the driver's code. The remaining items in the description — R_MB (needs PLCDEBUG, deliberately excluded), axis/tool units, timer 30–34 semantics, other control families and per-control DNC/DATA matrices — stay documented as known limits in docs/drivers/Heidenhain.md and do not block acceptance. Closing.

**Hardware evidence from the ignitionoee CR capture (2026-11-13), and acceptance.** ignitionoee `6d2aec40` (`docs/recipes/evidence/heidenhain-runstate-sample-2026-11-13/`) took a 7 h read-only OPC UA sample (04:51–11:50Z, every 5 s, 17 leaves) of the 34 Heidenhain controls on CR's production Ignition gateway. That driver is the Java LSV2 implementation this driver was ported from. It has the same read set, refusal mapping and state codes. Findings compared with this driver: | Finding on real controls | This driver | Code change? | |---|---|---| | 26 iTNC 530 (NC 340422 / 340490 / 606420): `ProgramState` 0 coincides with axis motion in 11,865 of 11,869 sample pairs; 7 = idle, no motion | Publishes raw `R_RI 26` Int32 + `ProgramStateName` from the pyLSV2 table (0 Started … 7 Idle) | No. The capture confirms the table. | | Program end goes 0 → 7 directly, never through 2 Finished; 1/4/5/6 not seen in 7 h | Names only; there is no state logic | No | | 5 TNC 426/430 (NC 280476): status/program/axes/alarms `BadNotSupported`, capability `Unsupported` | Unknown telegram / wrong parameter → `BadNotSupported` + `Unsupported`, suppressed until reconnect | No. The capture confirms the behaviour on a second control family. | | Overrides (`R_RI 25`) `BadUserAccessDenied` on every iTNC (INSPECT) | NoPrivilege / NoLogin → `BadUserAccessDenied` / `AccessDenied` | No | | `ExecutionState` 5 Undefined on idle machines | Raw code + `Undefined` name | No | | Capability reads `Unknown` until the feature's first poll | `CapabilityState` returns `Unknown` until the first attempt | No. Same behaviour, by design. | | Comms loss → `BadNoCommunication`, recovered on reconnect (Z-18985 dropped and came back during the capture) | Quality-only comms loss | No | | Alarm classes 1 / 7 / 8 present, alarm count > 0 during ~28 % of cutting | Alarms are labels; #839/#853 | No (see #853) | **Accepted per owner decision 2026-11-13: the hardware evidence does not change the driver's code.** The remaining items in the description — `R_MB` (needs PLCDEBUG, deliberately excluded), axis/tool units, timer 30–34 semantics, other control families and per-control DNC/DATA matrices — stay documented as known limits in `docs/drivers/Heidenhain.md` and do not block acceptance. Closing.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#829