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