fix(scripts): legacy WriteBatchAndWaitAsync waits for the run's own reset echo (ScadaBridge#36)
When the run reset the response flag via SetAttribute before the six-argument handshake, wait for that reset to echo (shared timeout) before writing batch + trigger, so the previous run's cached CompleteFlag can't satisfy it. Covers the unported DB-owned MES/receiver scripts. Pending reset consumed per handshake. Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
@@ -212,3 +212,32 @@ fast-path, and the trigger was not written until the device echoed the reset 751
|
||||
response wait is dominated by the Device script's own two data writes plus the subscription
|
||||
round-trip. The optional negative control (planting a stale `True` with the OPC UA tool) was
|
||||
skipped: `infra/tools/opcua_tool.py` needs the `asyncua` module, which is not installed here.
|
||||
|
||||
## 11. Addendum 2026-11-10 — implicit reset echo on the six-argument overload
|
||||
|
||||
The database-owned MES/Delmia scripts (13, 14, 36; receiver scripts 15, 16, 25, 26, 37, 38) were
|
||||
never ported onto the nine-argument overload (§9 / register row 119), so every unported run still
|
||||
had the §1 race. §9's "existing scripts behave identically" is therefore relaxed for **exactly
|
||||
one** shape — the defect itself:
|
||||
|
||||
- `ScriptRuntimeContext` records each successful `SetAttribute` of the run (scope-resolved name →
|
||||
encoded value, last write wins).
|
||||
- The six-argument `WriteBatchAndWait`, when THIS run wrote `responseKey` to a value other than
|
||||
`responseValue` (the script's own `CompleteFlag = false` reset), first waits event-driven for
|
||||
that value to echo and only then writes batch + trigger — the same ordering §2 requires and the
|
||||
reset-aware overload enforces. The pending write is consumed by that handshake (and by the
|
||||
reset-aware overload's reset leg), so a second handshake in the same run without a new reset is
|
||||
unchanged.
|
||||
- The echo wait **shares** the call's `timeout` with the response wait (unlike the additive
|
||||
nine-argument form), so an unported script can never run past the deadline it was sized for; a
|
||||
reset that never echoes returns `false` with nothing written and logs a warning.
|
||||
- A call with no prior reset write in the run, and the edge-triggered question of §2, are
|
||||
unchanged. `WaitAsync` used as a handshake is NOT gated: there the trigger is written before the
|
||||
wait, so a device flipping inside one sampling interval would never report the reset (§2).
|
||||
|
||||
Tests: `HandshakeResetEchoTests` — `LegacyOverload_AfterTheScriptsOwnReset_*` (held until the
|
||||
echo / no echo ⇒ false and no batch), `LegacyOverload_ResetIsConsumedByTheHandshakeThatObservedIt`,
|
||||
`ResetAwareOverload_ConsumesItsReset_SoALaterLegacyHandshakeIsNotGated`; the cached-true
|
||||
legacy test still passes unchanged (no reset in that run). Porting the scripts to the nine-argument
|
||||
overload is still recommended (phase-aware result, explicit reset timeout) but is no longer needed
|
||||
to close the race; dropping their `Task.Delay(1000)` remains a script edit.
|
||||
|
||||
Reference in New Issue
Block a user