review(Core.Scripting.Abstractions): refresh stale Phase7 labels + document {{equip}}

First review at 7286d320. Five Low doc fixes (BadNodeIdUnknown comment parity, three stale
Phase7 labels -> design-doc cites, {{equip}} token doc on GetTag/SetVirtualTag). Deadband
NaN/negative-tolerance (004) + a stale docs path (007) left Open.
This commit is contained in:
Joseph Doherty
2026-06-19 11:06:56 -04:00
parent 38c48a009c
commit 145b06bec9
4 changed files with 227 additions and 10 deletions
@@ -11,9 +11,11 @@ namespace ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;
/// order or drive cascade behavior.
/// </summary>
/// <remarks>
/// Per Phase 7 plan Shape A decision, alarm scripts are one-script-per-alarm
/// returning <c>bool</c>. They read any tag they want but should not write
/// anything (the owning alarm's state is tracked by the engine, not the script).
/// Per the scripting design (see
/// <c>docs/v2/implementation/phase-7-scripting-and-alarming.md</c>, decision row 4),
/// alarm scripts are one-script-per-alarm returning <c>bool</c>. They read any tag
/// they want but should not write anything (the owning alarm's state is tracked by the
/// engine, not the script).
/// </remarks>
public sealed class AlarmPredicateContext : ScriptContext
{
@@ -40,10 +42,10 @@ public sealed class AlarmPredicateContext : ScriptContext
public override DataValueSnapshot GetTag(string path)
{
if (string.IsNullOrWhiteSpace(path))
return new DataValueSnapshot(null, 0x80340000u, null, _clock());
return new DataValueSnapshot(null, 0x80340000u /* BadNodeIdUnknown */, null, _clock());
return _readCache.TryGetValue(path, out var v)
? v
: new DataValueSnapshot(null, 0x80340000u, null, _clock());
: new DataValueSnapshot(null, 0x80340000u /* BadNodeIdUnknown */, null, _clock());
}
/// <summary>Rejects virtual tag writes for pure predicate semantics.</summary>
@@ -6,9 +6,10 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Scripting;
/// <summary>
/// The API user scripts see as the global <c>ctx</c>. Abstract — concrete subclasses
/// (e.g. <c>VirtualTagScriptContext</c>, <c>AlarmScriptContext</c>) plug in the
/// actual tag-backend + logger + virtual-tag writer for each evaluation. Phase 7 plan
/// decision #6: scripts can read any tag, write only to virtual tags, and have no
/// other .NET reach — no HttpClient, no File, no Process, no reflection.
/// actual tag-backend + logger + virtual-tag writer for each evaluation. Plan decision
/// #6 (see <c>docs/v2/plan.md</c>): scripts can read any tag, write only to virtual
/// tags, and have no other .NET reach — no HttpClient, no File, no Process, no
/// reflection.
/// </summary>
/// <remarks>
/// <para>
@@ -39,6 +40,11 @@ public abstract class ScriptContext
/// publish by <c>DependencyExtractor</c>. This is intentional: the static
/// dependency set is required for the change-driven scheduler to subscribe to the
/// right upstream tags at load time.
/// <para>
/// The reserved <c>{{equip}}</c> token may appear in the path literal; it is
/// substituted with the owning equipment's tag-base prefix at deployment time.
/// See <c>docs/ScriptEditor.md</c> — Equipment-relative tag paths.
/// </para>
/// </remarks>
/// <param name="path">The literal tag path to read.</param>
public abstract DataValueSnapshot GetTag(string path);
@@ -52,7 +58,8 @@ public abstract class ScriptContext
/// <remarks>
/// Path rules identical to <see cref="GetTag"/> — literal only, dependency
/// extractor tracks the write targets so the engine knows what downstream
/// subscribers to notify.
/// subscribers to notify. The <c>{{equip}}</c> token is supported in the same way
/// as for <see cref="GetTag"/>.
/// </remarks>
/// <param name="path">The literal tag path to write to.</param>
/// <param name="value">The value to write to the virtual tag.</param>
@@ -18,7 +18,7 @@ namespace ZB.MOM.WW.OtOpcUa.Core.VirtualTags;
/// constructs a fresh context for every run — cheap because the constructor
/// just captures references — so scripts can't cache mutable state across runs
/// via <c>ctx</c>. Mutable state across runs is a future decision (e.g. a
/// dedicated <c>ctx.Memory</c> dictionary); not in scope for Phase 7.
/// dedicated <c>ctx.Memory</c> dictionary); not currently in scope.
/// </para>
/// <para>
/// The <see cref="Now"/> clock is injectable so tests can pin time