feat(commons): HandshakePhase + HandshakeResult for the reset-aware handshake (#36)
Claude-Session: https://claude.ai/code/session_01XPsuy2ZZ5Gbyfpt8PrK5BV
This commit is contained in:
@@ -80,6 +80,10 @@ public readonly record struct HandshakeResult(
|
||||
HandshakePhase Phase, string? ErrorMessage);
|
||||
```
|
||||
|
||||
`ResetFailed` (`!Matched && Phase == Reset`) is a computed convenience: script bodies import only
|
||||
`System`, `System.Collections.Generic`, `System.Linq` and `System.Threading.Tasks`, so a script
|
||||
branching on the enum would have to spell `ZB.MOM.WW.ScadaBridge.Commons.Types.HandshakePhase`.
|
||||
|
||||
Semantics of the new overload, in execution order:
|
||||
|
||||
1. **Reset write** — `resetKey = resetValue` through the existing `SetAttribute` path (static or
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Which leg of a reset-aware <c>Attributes.WriteBatchAndWaitAsync</c> handshake a
|
||||
/// <see cref="HandshakeResult"/> describes (Gitea #36, design 2026-09-10 §4).
|
||||
/// </summary>
|
||||
public enum HandshakePhase
|
||||
{
|
||||
/// <summary>The reset write + reset-echo wait that precedes the batch and trigger.</summary>
|
||||
Reset,
|
||||
|
||||
/// <summary>The batch + trigger write and the response wait that follow an observed reset echo.</summary>
|
||||
Response,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of the reset-aware <c>Attributes.WriteBatchAndWaitAsync</c> overload (Gitea #36):
|
||||
/// like <see cref="WaitResult"/>, plus the <see cref="Phase"/> the outcome belongs to so a
|
||||
/// script can report "timed out waiting for the reset echo" separately from "timed out
|
||||
/// waiting for the device to complete". A reset that is never echoed means the batch and
|
||||
/// trigger were NEVER written.
|
||||
/// </summary>
|
||||
/// <param name="Matched">
|
||||
/// <see langword="true"/> when the response attribute reached its target within the timeout
|
||||
/// (which implies the reset echo was observed first).
|
||||
/// </param>
|
||||
/// <param name="Value">The matched value of the phase's attribute; <see langword="null"/> on timeout / error.</param>
|
||||
/// <param name="Quality">The attribute quality at match time; <see langword="null"/> on the non-match paths.</param>
|
||||
/// <param name="TimedOut"><see langword="true"/> when the phase's wait timed out before a match.</param>
|
||||
/// <param name="Phase">The leg this result describes — <see cref="HandshakePhase.Reset"/> only when that leg failed.</param>
|
||||
/// <param name="ErrorMessage">Why the phase did not match, when known; <see langword="null"/> on success.</param>
|
||||
public readonly record struct HandshakeResult(
|
||||
bool Matched,
|
||||
object? Value,
|
||||
string? Quality,
|
||||
bool TimedOut,
|
||||
HandshakePhase Phase,
|
||||
string? ErrorMessage)
|
||||
{
|
||||
/// <summary>
|
||||
/// <see langword="true"/> when the reset leg did not match (timeout or error), i.e. the
|
||||
/// device never echoed the reset value and nothing else was written. Provided so a
|
||||
/// script can branch without spelling the <see cref="HandshakePhase"/> type name, which
|
||||
/// is not in the script imports.
|
||||
/// </summary>
|
||||
public bool ResetFailed => !Matched && Phase == HandshakePhase.Reset;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using ZB.MOM.WW.ScadaBridge.Commons.Types;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Gitea #36: <see cref="HandshakeResult"/> lets a script tell WHICH leg of a reset-aware
|
||||
/// handshake failed without naming the <see cref="HandshakePhase"/> enum (script bodies
|
||||
/// import only System/System.Collections.Generic/System.Linq/System.Threading.Tasks).
|
||||
/// </summary>
|
||||
public class HandshakeResultTests
|
||||
{
|
||||
[Fact]
|
||||
public void ResetFailed_IsTrue_OnlyForAnUnmatchedResetPhase()
|
||||
{
|
||||
var resetTimeout = new HandshakeResult(false, null, null, true, HandshakePhase.Reset, "reset timed out");
|
||||
var responseTimeout = new HandshakeResult(false, null, null, true, HandshakePhase.Response, "response timed out");
|
||||
var matched = new HandshakeResult(true, true, "Good", false, HandshakePhase.Response, null);
|
||||
|
||||
Assert.True(resetTimeout.ResetFailed);
|
||||
Assert.False(responseTimeout.ResetFailed);
|
||||
Assert.False(matched.ResetFailed);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user