Files
lmxopcua/src/Core/ZB.MOM.WW.OtOpcUa.Core.Scripting.Abstractions/ScriptContext.cs
T

87 lines
4.4 KiB
C#

using Serilog;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
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.
/// </summary>
/// <remarks>
/// <para>
/// Every member on this type MUST be serializable in the narrow sense that
/// <c>DependencyExtractor</c> can recognize tag-access call sites from the
/// script AST. Method names used from scripts are locked — renaming
/// <see cref="GetTag"/> or <see cref="SetVirtualTag"/> is a breaking change for every
/// authored script and the dependency extractor must update in lockstep.
/// </para>
/// <para>
/// New helpers (<see cref="Now"/>, <see cref="Deadband"/>) are additive: adding a
/// method doesn't invalidate existing scripts. Do not remove or rename without a
/// plan-level decision + migration for authored scripts.
/// </para>
/// </remarks>
public abstract class ScriptContext
{
/// <summary>
/// Read a tag's current value + quality + source timestamp. Path syntax is
/// <c>Enterprise/Site/Area/Line/Equipment/TagName</c> (forward-slash delimited,
/// matching the Equipment-namespace browse tree). Returns a
/// <see cref="DataValueSnapshot"/> so scripts branch on quality without a second
/// call.
/// </summary>
/// <remarks>
/// <paramref name="path"/> MUST be a string literal in the script source — dynamic
/// paths (variables, concatenation, method-returned strings) are rejected at
/// 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.
/// </remarks>
/// <param name="path">The literal tag path to read.</param>
public abstract DataValueSnapshot GetTag(string path);
/// <summary>
/// Write a value to a virtual tag. Operator scripts cannot write to driver-sourced
/// tags — the OPC UA dispatch in <c>DriverNodeManager</c> rejects that separately
/// per ADR-002 with <c>BadUserAccessDenied</c>. This method is the only write path
/// virtual tags have.
/// </summary>
/// <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.
/// </remarks>
/// <param name="path">The literal tag path to write to.</param>
/// <param name="value">The value to write to the virtual tag.</param>
public abstract void SetVirtualTag(string path, object? value);
/// <summary>
/// Current UTC timestamp. Prefer this over <see cref="DateTime.UtcNow"/> in
/// scripts so the harness can supply a deterministic clock for tests.
/// </summary>
public abstract DateTime Now { get; }
/// <summary>
/// Per-script Serilog logger. Output lands in the dedicated <c>scripts-*.log</c>
/// sink with structured property <c>ScriptName</c> = the script's configured name.
/// Use at error level to surface problems; main <c>opcua-*.log</c> receives a
/// companion WARN entry so operators see script errors in the primary log.
/// </summary>
public abstract ILogger Logger { get; }
/// <summary>
/// Deadband helper — returns <c>true</c> when <paramref name="current"/> differs
/// from <paramref name="previous"/> by more than <paramref name="tolerance"/>.
/// Useful for alarm predicates that shouldn't flicker on small noise. Pure
/// function; no side effects.
/// </summary>
/// <param name="current">The current value to check.</param>
/// <param name="previous">The previous value to compare against.</param>
/// <param name="tolerance">The minimum difference threshold for a change to be detected.</param>
public static bool Deadband(double current, double previous, double tolerance)
=> Math.Abs(current - previous) > tolerance;
}