feat(siteruntime): event-driven Attributes.WaitAsync attribute-change helper
Adds InstanceActor one-shot waiter registry (fast-path + change-match + scheduled timeout self-eviction), threads per-script timeout token through ScriptRuntimeContext, and exposes Attributes.WaitAsync(value|predicate, timeout). Replaces handshake busy-poll. Implements spec docs/plans/2026-06-17-waitfor-attribute-change-helper-spec.md §3-§5; §6 routed variant + WaitForAsync + quality-only mode deferred.
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
namespace ZB.MOM.WW.ScadaBridge.Commons.Messages.Instance;
|
||||
|
||||
/// <summary>
|
||||
/// Request to wait, event-driven, until an attribute reaches a value (or any
|
||||
/// value satisfying a predicate), bounded by a timeout — the backing protocol for
|
||||
/// the script-facing <c>Attributes.WaitAsync</c> helper.
|
||||
///
|
||||
/// <para>
|
||||
/// <b>Site-local only.</b> The optional <see cref="Predicate"/> is a non-serializable
|
||||
/// in-process delegate, so this message MUST flow only within a single site node's
|
||||
/// actor system (script execution → Instance Actor). It is never sent across the
|
||||
/// ClusterClient / gRPC boundary. The value-equality form (<see cref="TargetValueEncoded"/>)
|
||||
/// would serialize, but the routed/inbound variant is deliberately out of scope here.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="CorrelationId">Per-wait correlation id; keys the waiter registry and the timeout self-message.</param>
|
||||
/// <param name="InstanceName">The instance this wait targets.</param>
|
||||
/// <param name="AttributeName">The attribute to watch — already scope-resolved by the accessor.</param>
|
||||
/// <param name="TargetValueEncoded">
|
||||
/// The codec-encoded target value (<c>AttributeValueCodec.Encode(target)</c>). A
|
||||
/// match compares the codec-encoded form of the current value against this string.
|
||||
/// When both this and <see cref="Predicate"/> are null the wait matches on ANY change.
|
||||
/// </param>
|
||||
/// <param name="Predicate">
|
||||
/// Site-local predicate tested against the raw (decoded) current value. Mutually
|
||||
/// exclusive with <see cref="TargetValueEncoded"/> — null when the encoded target is used.
|
||||
/// </param>
|
||||
/// <param name="Timeout">How long to wait before self-evicting with a timeout reply.</param>
|
||||
/// <param name="OccurredAtUtc">When the request was issued (UTC).</param>
|
||||
public record WaitForAttributeRequest(
|
||||
string CorrelationId,
|
||||
string InstanceName,
|
||||
string AttributeName,
|
||||
string? TargetValueEncoded,
|
||||
Func<object?, bool>? Predicate,
|
||||
TimeSpan Timeout,
|
||||
DateTimeOffset OccurredAtUtc);
|
||||
|
||||
/// <summary>
|
||||
/// Reply to a <see cref="WaitForAttributeRequest"/>. Exactly one of
|
||||
/// <see cref="Matched"/> / <see cref="TimedOut"/> is set on the happy paths;
|
||||
/// <see cref="ErrorMessage"/> is populated only on the defensive cap-exceeded path.
|
||||
/// </summary>
|
||||
/// <param name="CorrelationId">Echoes the request's correlation id.</param>
|
||||
/// <param name="Matched">True when the attribute reached the target/predicate within the timeout.</param>
|
||||
/// <param name="Value">The matched value (null on timeout / error).</param>
|
||||
/// <param name="Quality">The attribute quality at match time (empty on timeout / error).</param>
|
||||
/// <param name="TimedOut">True when the timeout fired before a match.</param>
|
||||
/// <param name="ErrorMessage">Non-null only when the wait was refused (e.g. per-instance waiter cap exceeded).</param>
|
||||
public record WaitForAttributeResponse(
|
||||
string CorrelationId,
|
||||
bool Matched,
|
||||
object? Value,
|
||||
string Quality,
|
||||
bool TimedOut,
|
||||
string? ErrorMessage = null);
|
||||
|
||||
/// <summary>
|
||||
/// Internal self-message scheduled by the Instance Actor to fire a waiter's
|
||||
/// timeout. Site-local only; never crosses a cluster boundary.
|
||||
/// </summary>
|
||||
/// <param name="CorrelationId">The waiter whose timeout fired.</param>
|
||||
public record WaitForAttributeTimeout(string CorrelationId);
|
||||
Reference in New Issue
Block a user