docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public members surfaced by commentchecker — resolves 5,847 of 5,869 issues (99.6%) across three /fixdocs passes.
This commit is contained in:
@@ -44,6 +44,9 @@ public sealed record AlarmConditionState(
|
||||
ImmutableList<AlarmComment> Comments)
|
||||
{
|
||||
/// <summary>Initial-load state for a newly registered alarm — everything in the "no-event" position.</summary>
|
||||
/// <param name="alarmId">The unique identifier for the alarm.</param>
|
||||
/// <param name="nowUtc">The current UTC timestamp.</param>
|
||||
/// <returns>A fresh AlarmConditionState with all fields initialized to default/inactive values.</returns>
|
||||
public static AlarmConditionState Fresh(string alarmId, DateTime nowUtc) => new(
|
||||
AlarmId: alarmId,
|
||||
Enabled: AlarmEnabledState.Enabled,
|
||||
|
||||
@@ -20,6 +20,10 @@ public sealed class AlarmPredicateContext : ScriptContext
|
||||
private readonly IReadOnlyDictionary<string, DataValueSnapshot> _readCache;
|
||||
private readonly Func<DateTime> _clock;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="AlarmPredicateContext"/> class.</summary>
|
||||
/// <param name="readCache">The cached read values from tags.</param>
|
||||
/// <param name="logger">The logger for diagnostics.</param>
|
||||
/// <param name="clock">Optional custom clock for testing.</param>
|
||||
public AlarmPredicateContext(
|
||||
IReadOnlyDictionary<string, DataValueSnapshot> readCache,
|
||||
ILogger logger,
|
||||
@@ -30,6 +34,9 @@ public sealed class AlarmPredicateContext : ScriptContext
|
||||
_clock = clock ?? (() => DateTime.UtcNow);
|
||||
}
|
||||
|
||||
/// <summary>Gets a tag value from the read cache.</summary>
|
||||
/// <param name="path">The tag path to retrieve.</param>
|
||||
/// <inheritdoc />
|
||||
public override DataValueSnapshot GetTag(string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
@@ -39,6 +46,10 @@ public sealed class AlarmPredicateContext : ScriptContext
|
||||
: new DataValueSnapshot(null, 0x80340000u, null, _clock());
|
||||
}
|
||||
|
||||
/// <summary>Rejects virtual tag writes for pure predicate semantics.</summary>
|
||||
/// <param name="path">The virtual tag path.</param>
|
||||
/// <param name="value">The value to write.</param>
|
||||
/// <inheritdoc />
|
||||
public override void SetVirtualTag(string path, object? value)
|
||||
{
|
||||
// Predicates must be pure — writing from an alarm script couples alarm state to
|
||||
@@ -49,7 +60,9 @@ public sealed class AlarmPredicateContext : ScriptContext
|
||||
"into a virtual tag whose value the alarm predicate then reads.");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DateTime Now => _clock();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ILogger Logger { get; }
|
||||
}
|
||||
|
||||
@@ -15,9 +15,27 @@ namespace ZB.MOM.WW.OtOpcUa.Core.ScriptedAlarms;
|
||||
/// </remarks>
|
||||
public interface IAlarmStateStore
|
||||
{
|
||||
/// <summary>Loads the alarm state for a specific alarm identifier.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The alarm state if found; otherwise null.</returns>
|
||||
Task<AlarmConditionState?> LoadAsync(string alarmId, CancellationToken ct);
|
||||
|
||||
/// <summary>Loads all alarm states from the store.</summary>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A collection of all alarm states.</returns>
|
||||
Task<IReadOnlyList<AlarmConditionState>> LoadAllAsync(CancellationToken ct);
|
||||
|
||||
/// <summary>Saves an alarm state to the store.</summary>
|
||||
/// <param name="state">The alarm state to save.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task representing the save operation.</returns>
|
||||
Task SaveAsync(AlarmConditionState state, CancellationToken ct);
|
||||
|
||||
/// <summary>Removes an alarm state from the store.</summary>
|
||||
/// <param name="alarmId">The alarm identifier to remove.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>A task representing the remove operation.</returns>
|
||||
Task RemoveAsync(string alarmId, CancellationToken ct);
|
||||
}
|
||||
|
||||
@@ -27,18 +45,22 @@ public sealed class InMemoryAlarmStateStore : IAlarmStateStore
|
||||
private readonly ConcurrentDictionary<string, AlarmConditionState> _map
|
||||
= new(StringComparer.Ordinal);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<AlarmConditionState?> LoadAsync(string alarmId, CancellationToken ct)
|
||||
=> Task.FromResult(_map.TryGetValue(alarmId, out var v) ? v : null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<AlarmConditionState>> LoadAllAsync(CancellationToken ct)
|
||||
=> Task.FromResult<IReadOnlyList<AlarmConditionState>>(_map.Values.ToArray());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveAsync(AlarmConditionState state, CancellationToken ct)
|
||||
{
|
||||
_map[state.AlarmId] = state;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RemoveAsync(string alarmId, CancellationToken ct)
|
||||
{
|
||||
_map.TryRemove(alarmId, out _);
|
||||
|
||||
@@ -43,6 +43,9 @@ public static class MessageTemplate
|
||||
/// "Input-quality policy" section in <c>docs/ScriptedAlarms.md</c>.
|
||||
/// (Core.ScriptedAlarms-010)
|
||||
/// </remarks>
|
||||
/// <param name="template">The template string with {path} tokens.</param>
|
||||
/// <param name="resolveTag">A function to resolve tag values by path.</param>
|
||||
/// <returns>The resolved template string with tokens replaced or marked as unresolvable.</returns>
|
||||
public static string Resolve(string template, Func<string, DataValueSnapshot?> resolveTag)
|
||||
{
|
||||
if (string.IsNullOrEmpty(template)) return template ?? string.Empty;
|
||||
@@ -60,6 +63,8 @@ public static class MessageTemplate
|
||||
}
|
||||
|
||||
/// <summary>Enumerate the token paths the template references. Used at publish time to validate references exist.</summary>
|
||||
/// <param name="template">The template string to extract tokens from.</param>
|
||||
/// <returns>A list of all token paths found in the template.</returns>
|
||||
public static IReadOnlyList<string> ExtractTokenPaths(string? template)
|
||||
{
|
||||
if (string.IsNullOrEmpty(template)) return Array.Empty<string>();
|
||||
|
||||
@@ -32,6 +32,10 @@ public static class Part9StateMachine
|
||||
/// branch-stack increment when a new active arrives while prior active is
|
||||
/// still un-acked, and shelving suppression.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="predicateTrue">Whether the predicate is true.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyPredicate(
|
||||
AlarmConditionState current,
|
||||
bool predicateTrue,
|
||||
@@ -86,6 +90,11 @@ public static class Part9StateMachine
|
||||
}
|
||||
|
||||
/// <summary>Operator acknowledges the currently-active transition.</summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user who is acknowledging.</param>
|
||||
/// <param name="comment">An optional comment.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyAcknowledge(
|
||||
AlarmConditionState current,
|
||||
string user,
|
||||
@@ -112,6 +121,11 @@ public static class Part9StateMachine
|
||||
}
|
||||
|
||||
/// <summary>Operator confirms the cleared transition. Part 9 requires confirm after clear for retain-flag alarms.</summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user who is confirming.</param>
|
||||
/// <param name="comment">An optional comment.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyConfirm(
|
||||
AlarmConditionState current,
|
||||
string user,
|
||||
@@ -137,6 +151,13 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Confirmed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a one-shot shelving action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user applying the shelving.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyOneShotShelve(
|
||||
AlarmConditionState current, string user, DateTime nowUtc)
|
||||
{
|
||||
@@ -154,6 +175,14 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Shelved);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a timed shelving action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user applying the shelving.</param>
|
||||
/// <param name="unshelveAtUtc">The UTC time at which the alarm should be unshelved.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyTimedShelve(
|
||||
AlarmConditionState current, string user, DateTime unshelveAtUtc, DateTime nowUtc)
|
||||
{
|
||||
@@ -172,6 +201,13 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Shelved);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an unshelving action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user applying the unshelving.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyUnshelve(AlarmConditionState current, string user, DateTime nowUtc)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(user)) throw new ArgumentException("User required.", nameof(user));
|
||||
@@ -188,6 +224,13 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Unshelved);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an enable action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user enabling the alarm.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyEnable(AlarmConditionState current, string user, DateTime nowUtc)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(user)) throw new ArgumentException("User required.", nameof(user));
|
||||
@@ -204,6 +247,13 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Enabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies a disable action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user disabling the alarm.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyDisable(AlarmConditionState current, string user, DateTime nowUtc)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(user)) throw new ArgumentException("User required.", nameof(user));
|
||||
@@ -220,6 +270,14 @@ public static class Part9StateMachine
|
||||
return new TransitionResult(next, EmissionKind.Disabled);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies an add comment action.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="user">The user adding the comment.</param>
|
||||
/// <param name="text">The comment text.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyAddComment(
|
||||
AlarmConditionState current, string user, string text, DateTime nowUtc)
|
||||
{
|
||||
@@ -236,6 +294,9 @@ public static class Part9StateMachine
|
||||
/// the (possibly unshelved) state + emission hint so the engine knows to
|
||||
/// publish an Unshelved event at the right moment.
|
||||
/// </summary>
|
||||
/// <param name="current">The current alarm condition state.</param>
|
||||
/// <param name="nowUtc">The current UTC time.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult ApplyShelvingCheck(AlarmConditionState current, DateTime nowUtc)
|
||||
{
|
||||
if (current.Shelving.Kind != ShelvingKind.Timed) return TransitionResult.None(current);
|
||||
@@ -284,7 +345,19 @@ public static class Part9StateMachine
|
||||
/// </remarks>
|
||||
public sealed record TransitionResult(AlarmConditionState State, EmissionKind Emission, string? NoOpReason = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a transition result with no change and no emission.
|
||||
/// </summary>
|
||||
/// <param name="state">The alarm condition state.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult None(AlarmConditionState state) => new(state, EmissionKind.None);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a transition result indicating a no-op operation with a reason.
|
||||
/// </summary>
|
||||
/// <param name="state">The alarm condition state.</param>
|
||||
/// <param name="reason">The reason for the no-op.</param>
|
||||
/// <returns>The transition result.</returns>
|
||||
public static TransitionResult NoOp(AlarmConditionState state, string reason)
|
||||
=> new(state, EmissionKind.None, reason);
|
||||
}
|
||||
|
||||
@@ -96,6 +96,7 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// deterministic upstream push. Anything more involved should snapshot a
|
||||
/// copy under the gate. (Core.ScriptedAlarms-013.)
|
||||
/// </remarks>
|
||||
/// <param name="alarmId">The alarm identifier to look up.</param>
|
||||
internal IReadOnlyDictionary<string, DataValueSnapshot>? TryGetScratchReadCacheForTest(string alarmId)
|
||||
=> _scratchByAlarmId.TryGetValue(alarmId, out var s) ? s.ReadCache : null;
|
||||
|
||||
@@ -111,6 +112,7 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// for reference-identity assertions on a quiesced engine.
|
||||
/// (Core.ScriptedAlarms-013.)
|
||||
/// </remarks>
|
||||
/// <param name="alarmId">The alarm identifier to look up.</param>
|
||||
internal AlarmPredicateContext? TryGetScratchContextForTest(string alarmId)
|
||||
=> _scratchByAlarmId.TryGetValue(alarmId, out var s) ? s.Context : null;
|
||||
private readonly ConcurrentDictionary<string, DataValueSnapshot> _valueCache
|
||||
@@ -133,6 +135,15 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
private readonly HashSet<Task> _inFlight = [];
|
||||
private readonly object _inFlightLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new ScriptedAlarmEngine with the provided dependencies.
|
||||
/// </summary>
|
||||
/// <param name="upstream">The upstream tag source for reading tag values.</param>
|
||||
/// <param name="store">The alarm state store for persistence.</param>
|
||||
/// <param name="loggerFactory">The factory for creating alarm loggers.</param>
|
||||
/// <param name="engineLogger">The logger for engine-level diagnostics.</param>
|
||||
/// <param name="clock">Optional function providing the current UTC time; defaults to DateTime.UtcNow.</param>
|
||||
/// <param name="scriptTimeout">Optional timeout for script execution; defaults to the evaluator's default timeout.</param>
|
||||
public ScriptedAlarmEngine(
|
||||
ITagUpstreamSource upstream,
|
||||
IAlarmStateStore store,
|
||||
@@ -152,6 +163,7 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// <summary>Raised for every emission the Part9StateMachine produces that the engine should publish.</summary>
|
||||
public event EventHandler<ScriptedAlarmEvent>? OnEvent;
|
||||
|
||||
/// <summary>Gets the collection of loaded alarm identifiers.</summary>
|
||||
public IReadOnlyCollection<string> LoadedAlarmIds => _alarms.Keys.ToArray();
|
||||
|
||||
/// <summary>
|
||||
@@ -161,6 +173,8 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// the store (falling back to Fresh for first-load alarms), and recomputes
|
||||
/// ActiveState per Phase 7 plan decision #14 (startup recovery).
|
||||
/// </summary>
|
||||
/// <param name="definitions">The alarm definitions to load.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public async Task LoadAsync(IReadOnlyList<ScriptedAlarmDefinition> definitions, CancellationToken ct)
|
||||
{
|
||||
if (_disposed) throw new ObjectDisposedException(nameof(ScriptedAlarmEngine));
|
||||
@@ -291,33 +305,71 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// Current persisted state for <paramref name="alarmId"/>. Returns null for
|
||||
/// unknown alarm. Mainly used for diagnostics + the Admin UI status page.
|
||||
/// </summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
public AlarmConditionState? GetState(string alarmId)
|
||||
=> _alarms.TryGetValue(alarmId, out var s) ? s.Condition : null;
|
||||
|
||||
/// <summary>Gets the current persisted state for all loaded alarms.</summary>
|
||||
public IReadOnlyCollection<AlarmConditionState> GetAllStates()
|
||||
=> _alarms.Values.Select(a => a.Condition).ToArray();
|
||||
|
||||
/// <summary>Acknowledges the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the acknowledgment.</param>
|
||||
/// <param name="comment">An optional comment to attach to the acknowledgment.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task AcknowledgeAsync(string alarmId, string user, string? comment, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyAcknowledge(cur, user, comment, _clock()));
|
||||
|
||||
/// <summary>Confirms the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the confirmation.</param>
|
||||
/// <param name="comment">An optional comment to attach to the confirmation.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task ConfirmAsync(string alarmId, string user, string? comment, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyConfirm(cur, user, comment, _clock()));
|
||||
|
||||
/// <summary>Applies a one-shot shelve to the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the shelve operation.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task OneShotShelveAsync(string alarmId, string user, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyOneShotShelve(cur, user, _clock()));
|
||||
|
||||
/// <summary>Applies a timed shelve to the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the shelve operation.</param>
|
||||
/// <param name="unshelveAtUtc">The UTC time at which the shelve will automatically expire.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task TimedShelveAsync(string alarmId, string user, DateTime unshelveAtUtc, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyTimedShelve(cur, user, unshelveAtUtc, _clock()));
|
||||
|
||||
/// <summary>Removes any shelve from the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the unshelve operation.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task UnshelveAsync(string alarmId, string user, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyUnshelve(cur, user, _clock()));
|
||||
|
||||
/// <summary>Enables the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the enable operation.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task EnableAsync(string alarmId, string user, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyEnable(cur, user, _clock()));
|
||||
|
||||
/// <summary>Disables the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user performing the disable operation.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task DisableAsync(string alarmId, string user, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyDisable(cur, user, _clock()));
|
||||
|
||||
/// <summary>Adds a comment to the specified alarm on behalf of the given user.</summary>
|
||||
/// <param name="alarmId">The alarm identifier.</param>
|
||||
/// <param name="user">The user adding the comment.</param>
|
||||
/// <param name="text">The comment text.</param>
|
||||
/// <param name="ct">The cancellation token.</param>
|
||||
public Task AddCommentAsync(string alarmId, string user, string text, CancellationToken ct)
|
||||
=> ApplyAsync(alarmId, ct, cur => Part9StateMachine.ApplyAddComment(cur, user, text, _clock()));
|
||||
|
||||
@@ -369,6 +421,8 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// so driver-side dispatch isn't blocked; the background task is tracked so
|
||||
/// <see cref="Dispose"/> can drain it. (Core.ScriptedAlarms-006)
|
||||
/// </summary>
|
||||
/// <param name="path">The upstream tag path that changed.</param>
|
||||
/// <param name="value">The new data value snapshot.</param>
|
||||
internal void OnUpstreamChange(string path, DataValueSnapshot value)
|
||||
{
|
||||
_valueCache[path] = value;
|
||||
@@ -658,6 +712,7 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
"ScriptedAlarmEngine not loaded. Call LoadAsync first.");
|
||||
}
|
||||
|
||||
/// <summary>Disposes the engine, cleaning up resources and waiting for in-flight background tasks.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
@@ -729,9 +784,17 @@ public sealed class ScriptedAlarmEngine : IDisposable
|
||||
/// </remarks>
|
||||
private sealed class AlarmScratch
|
||||
{
|
||||
/// <summary>Gets the read cache dictionary containing current upstream tag values.</summary>
|
||||
public Dictionary<string, DataValueSnapshot> ReadCache { get; }
|
||||
/// <summary>Gets the predicate evaluation context.</summary>
|
||||
public AlarmPredicateContext Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new AlarmScratch with the specified inputs, logger, and clock.
|
||||
/// </summary>
|
||||
/// <param name="inputs">The set of input tag paths this alarm references.</param>
|
||||
/// <param name="logger">The logger for this alarm's diagnostics.</param>
|
||||
/// <param name="clock">Function providing the current UTC time.</param>
|
||||
public AlarmScratch(IReadOnlySet<string> inputs, ILogger logger, Func<DateTime> clock)
|
||||
{
|
||||
// Pre-size to the expected input count so the first refill doesn't pay the
|
||||
@@ -768,6 +831,14 @@ public sealed record ScriptedAlarmEvent(
|
||||
/// </summary>
|
||||
public interface ITagUpstreamSource
|
||||
{
|
||||
/// <summary>Reads a tag value synchronously.</summary>
|
||||
/// <param name="path">The tag path to read.</param>
|
||||
/// <returns>A data value snapshot containing the tag value and status.</returns>
|
||||
DataValueSnapshot ReadTag(string path);
|
||||
|
||||
/// <summary>Subscribes to upstream tag changes.</summary>
|
||||
/// <param name="path">The tag path to observe.</param>
|
||||
/// <param name="observer">Callback invoked when the tag value changes.</param>
|
||||
/// <returns>A subscription handle that removes the observer when disposed.</returns>
|
||||
IDisposable SubscribeTag(string path, Action<string, DataValueSnapshot> observer);
|
||||
}
|
||||
|
||||
@@ -31,14 +31,18 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
|
||||
= new(StringComparer.Ordinal);
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="ScriptedAlarmSource"/> class.</summary>
|
||||
/// <param name="engine">The scripted alarm engine to expose.</param>
|
||||
public ScriptedAlarmSource(ScriptedAlarmEngine engine)
|
||||
{
|
||||
_engine = engine ?? throw new ArgumentNullException(nameof(engine));
|
||||
_engine.OnEvent += OnEngineEvent;
|
||||
}
|
||||
|
||||
/// <summary>Occurs when an alarm event is raised by the engine.</summary>
|
||||
public event EventHandler<AlarmEventArgs>? OnAlarmEvent;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IAlarmSubscriptionHandle> SubscribeAlarmsAsync(
|
||||
IReadOnlyList<string> sourceNodeIds, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -49,6 +53,7 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
|
||||
return Task.FromResult<IAlarmSubscriptionHandle>(handle);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task UnsubscribeAlarmsAsync(IAlarmSubscriptionHandle handle, CancellationToken cancellationToken)
|
||||
{
|
||||
if (handle is null) throw new ArgumentNullException(nameof(handle));
|
||||
@@ -56,6 +61,7 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task AcknowledgeAsync(
|
||||
IReadOnlyList<AlarmAcknowledgeRequest> acknowledgements, CancellationToken cancellationToken)
|
||||
{
|
||||
@@ -104,6 +110,7 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Releases resources used by the <see cref="ScriptedAlarmSource"/>.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
@@ -114,7 +121,10 @@ public sealed class ScriptedAlarmSource : IAlarmSource, IDisposable
|
||||
|
||||
private sealed class SubscriptionHandle : IAlarmSubscriptionHandle
|
||||
{
|
||||
/// <summary>Initializes a new instance of the <see cref="SubscriptionHandle"/> class.</summary>
|
||||
/// <param name="id">The diagnostic ID for this subscription handle.</param>
|
||||
public SubscriptionHandle(string id) { DiagnosticId = id; }
|
||||
/// <summary>Gets the diagnostic ID that uniquely identifies this subscription.</summary>
|
||||
public string DiagnosticId { get; }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user