feat/scripted-alarm-shelve-routing #421
Reference in New Issue
Block a user
Delete Branch "feat/scripted-alarm-shelve-routing"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
76d35d18568f5cd85Driver.S7-001: Timer (T{n}) / Counter (C{n}) addresses parsed cleanly but the read path had no S7DataType or decode case for them, so a Timer/Counter tag passed fail-fast init and then threw a misleading type-mismatch on every read. InitializeAsync now runs RejectUnsupportedTagAddresses, throwing a clear NotSupportedException ("not yet supported", echoing tag name + address) so the config error fails fast at init. Driver.S7-006: ShutdownAsync cancelled the probe/poll CTSs but did not await the fire-and-forget loop tasks before DisposeAsync disposed _gate, letting a loop iteration mid-semaphore race a disposed object. The probe task is now tracked in _probeTask and each poll task in SubscriptionState.PollTask; ShutdownAsync cancels every CTS, awaits Task.WhenAll of those handles with a bounded 5 s DrainTimeout, then disposes the CTSs and gate. Task.Run is passed CancellationToken.None so the handle is always awaitable. Driver.S7-007: a PUT/GET-disabled fault (permanent misconfiguration) was mapped identically to a transient PlcException — both BadDeviceFailure + Degraded. ReadAsync/WriteAsync now split the catch via an IsAccessDenied filter (S7.Net exposes no typed code for AccessingObjectNotAllowed, so the inner-exception chain is inspected for the "not allowed" marker). Access-denied now maps to BadNotSupported and Faulted with a config-alert message pointing at the TIA Portal PUT/GET toggle; genuine device faults stay BadDeviceFailure. Driver.S7-011: S7Driver ignored driverConfigJson on Initialize/Reinitialize, so a config change delivered through ReinitializeAsync (the only Core-initiated in-process recovery path) was silently discarded. Config parsing was factored into S7DriverFactoryExtensions.ParseOptions; InitializeAsync now re-parses driverConfigJson and rebuilds _options whenever the document has a real body. An empty / placeholder document keeps the constructor options. Adds S7DriverCodeReviewFixTests covering Timer/Counter rejection, config-json application on Initialize/Reinitialize, and shutdown-drain with active subscriptions. All 68 S7 driver tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Add engine-level tests covering the six gaps identified in the finding: (1) timed-shelve auto-expiry driven via injectable clock + RunShelvingCheckForTest hook so timer tests are deterministic; (2) ConfirmAsync, TimedShelveAsync/UnshelveAsync round-trip, EnableAsync engine methods exercised end-to-end; (3) OnEvent subscriber-throws isolation — engine state advances and stays operational after a subscriber throws; (4) IAlarmStateStore.SaveAsync failure leaves in-memory state unchanged (locks in the persist-before-update invariant from finding-007); (5) second LoadAsync does not leak the old timer (regression for finding-002); (6) AreInputsReady cold-start guard correctly blocks on Bad/missing inputs and allows Uncertain-quality inputs through. Expose RunShelvingCheckForTest() internal method on ScriptedAlarmEngine to support deterministic timer tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>DependencyExtractor.VisitInvocationExpression now additionally checks that the member-access receiver is the identifier "ctx" before treating a GetTag / SetVirtualTag call as a ScriptContext dependency. This prevents spurious dependencies when a script defines a local helper type with a matching method name and calls it as other.GetTag("X"). Test Ignores_member_access_GetTag_on_non_ctx_receiver added. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Fix two resource-management bugs in StartDeployWatcher / BuildDefaultHierarchySource: (a) Replace the discarded `_ = StartAsync(...)` with an explicit task variable that surfaces any synchronous InvalidOperationException (called-twice guard) rather than silently swallowing it. (b) Change both StartDeployWatcher and BuildDefaultHierarchySource to use ??= on _ownedRepositoryClient so the first client created (by whichever path runs first) is reused by the second path, preventing a second GalaxyRepositoryClient from being created and the first from leaking past the driver's lifetime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>The Admin-003 fix gated every SignalR hub with [Authorize], but the server-side Blazor HubConnection clients had no way to authenticate: the browser's HttpOnly auth cookie is not reachable from the interactive circuit, so every hub negotiate returned 401 and the Admin live-update feature was non-functional app-wide (silently degraded on Hosts/ScriptLog, fatal on the cluster pages). Introduce a token-based hub auth path: - HubTokenService mints/validates short-lived tokens using ASP.NET Core Data Protection (the same primitive that protects the auth cookie — no signing-key management, no new packages). Tokens carry the user's name + roles. - HubTokenAuthenticationHandler is a custom "HubToken" auth scheme that reads the token from the Authorization: Bearer header (negotiate) or the access_token query parameter (WebSocket upgrade). - The "HubClients" authorization policy runs both the cookie and HubToken schemes; the hub endpoints use RequireAuthorization("HubClients"). - AdminHubConnectionFactory builds hub connections with an AccessTokenProvider that mints a fresh token for the circuit's authenticated user on every (re)connect. All six hub-consuming pages now resolve connections through it. Hub negotiate now returns 200 and the WebSocket upgrades (101); live updates work. The best-effort try/catch guards added previously are kept as defence. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>- Core.ScriptedAlarms-003: emit OnEvent OUTSIDE _evalGate by collecting pending emissions during the gate-held section and flushing them after release; eliminates re-entrancy deadlock the docs already promised. - Core.ScriptedAlarms-006: track every fire-and-forget Reevaluate / ShelvingCheck task in _inFlight; Dispose drains the set so the engine no longer races store writes against teardown. - Core.ScriptedAlarms-008: store comments as ImmutableList<AlarmComment> so AppendComment is O(log n) instead of O(n). - Core.ScriptedAlarms-010: document the deliberate input-quality asymmetry (Uncertain drives the predicate, renders {?} in the message) in docs/ScriptedAlarms.md and on MessageTemplate.Resolve remarks. - Core.ScriptedAlarms-011: propagate the no-op reason through TransitionResult.NoOp(state, reason) and log it from ScriptedAlarmEngine.ApplyAsync. - Core.ScriptedAlarms-009 (Won't Fix per recommendation): documented the per-evaluation dictionary allocation in docs/v2/Galaxy.Performance.md with a mitigation path if a future soak surfaces pressure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>- Driver.Historian.Wonderware-004: ToHistorianEvent synthesises a fresh Guid when the upstream EventId is unparseable and logs the substitution instead of writing the historian with Guid.Empty. - Driver.Historian.Wonderware-005: GetHealthSnapshot derives the connection-open booleans from the active-node fields so the snapshot is self-consistent without depending on the secondary lock. - Driver.Historian.Wonderware-007: SID-mismatch branch in PipeServer now sends a HelloAck { Accepted=false, RejectReason } so the client sees a symmetric rejection. - Driver.Historian.Wonderware-008: classify StartQuery failures — connection-class codes drop the connection, query-class codes throw QueryClassStartQueryException so the IPC layer surfaces Success=false. - Driver.Historian.Wonderware-010: RequestTimeoutSeconds now enforced via BuildRequestCts linked to the caller's CancellationToken. - Driver.Historian.Wonderware-011: refreshed XML docs to describe the current sidecar / named-pipe architecture (Galaxy.Host / Proxy references reframed as historical context). - Driver.Historian.Wonderware-012: pinned the previously-uncovered HistorianDataSource behaviours with five new test files; also removed the stale empty tests/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Tests directory. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>Pull request closed