using Serilog;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Core.Scripting;
namespace ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;
///
/// subclass for alarm predicate evaluation. Reads from
/// the engine's shared tag cache (driver + virtual tags), writes are rejected —
/// predicates must be side-effect free so their output doesn't depend on evaluation
/// order or drive cascade behavior.
///
///
/// Per Phase 7 plan Shape A decision, alarm scripts are one-script-per-alarm
/// returning bool. They read any tag they want but should not write
/// anything (the owning alarm's state is tracked by the engine, not the script).
///
public sealed class AlarmPredicateContext : ScriptContext
{
private readonly IReadOnlyDictionary _readCache;
private readonly Func _clock;
/// Initializes a new instance of the class.
/// The cached read values from tags.
/// The logger for diagnostics.
/// Optional custom clock for testing.
public AlarmPredicateContext(
IReadOnlyDictionary readCache,
ILogger logger,
Func? clock = null)
{
_readCache = readCache ?? throw new ArgumentNullException(nameof(readCache));
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
_clock = clock ?? (() => DateTime.UtcNow);
}
/// Gets a tag value from the read cache.
/// The tag path to retrieve.
///
public override DataValueSnapshot GetTag(string path)
{
if (string.IsNullOrWhiteSpace(path))
return new DataValueSnapshot(null, 0x80340000u, null, _clock());
return _readCache.TryGetValue(path, out var v)
? v
: new DataValueSnapshot(null, 0x80340000u, null, _clock());
}
/// Rejects virtual tag writes for pure predicate semantics.
/// The virtual tag path.
/// The value to write.
///
public override void SetVirtualTag(string path, object? value)
{
// Predicates must be pure — writing from an alarm script couples alarm state to
// virtual-tag state in a way that's near-impossible to reason about. Rejected
// at runtime with a clear message; operators see it in the scripts-*.log.
throw new InvalidOperationException(
"Alarm predicate scripts cannot write to virtual tags. Move the write logic " +
"into a virtual tag whose value the alarm predicate then reads.");
}
///
public override DateTime Now => _clock();
///
public override ILogger Logger { get; }
}