Commit Graph

1456 Commits

Author SHA1 Message Date
Joseph Doherty 45b23476fc fix(dcl): pad List writes to the Galaxy array's full declared size
Even with correct array encoding (30d07b9), Ipsen MoveIn array writes still
hung: the Galaxy MES-receiver arrays are fixed-size SAFEARRAYs (e.g.
MoveInWorkOrderNumbers = SAFEARRAY(VT_BSTR) dimensions:[50]) and MXAccess only
accepts a write that supplies ALL slots. ScadaBridge sent just the N elements
the MES provided (1-2), so the COM write blocked. Verified on the live gateway:
a full-size (50) constructed array writes via WriteBulk in ~34ms; a short one
does not.

RealMxGatewayClient.WriteAsync now, for a list value, reads the tag's current
array to learn its slot count and pads the value to that length with
element-type defaults (empty string / 0 / false / default) — the caller's
values fill slots 0..N-1, the rest are cleared. The PLC reads the valid count
from a separate scalar (MoveInNumberWorkOrders). If the size can't be
determined (read fails / not an array) the value is written unpadded and a
warning is logged. Scalars are unaffected.
2026-06-17 06:34:05 -04:00
Joseph Doherty 30d07b91f4 fix(dcl): write List attributes as real MXAccess arrays (not a stringified List)
The Ipsen MoveIn e2e (after the supervisory-advise fix landed scalar writes)
exposed a second blocker: writes to List-typed attributes
(MoveInWorkOrderNumbers / MoveInPartNumbers, List<string>) hung at the 30s
device-write timeout while scalar writes succeeded.

InstanceActor.HandleSetDataAttribute already decodes a List attribute's
canonical JSON into a typed List<T> before the write (so the DCL can push a
real array), but RealMxGatewayClient.ToMxValue only had scalar cases — a
List<T> fell through to Convert.ToString and wrote the garbage string
"System.Collections.Generic.List`1[System.String]" to the array Galaxy
node, which the gateway's COM write rejected/blocked.

Add IReadOnlyList<bool|int|long|float|double|string|DateTimeOffset|DateTime>
cases that call the client package's typed array encoders
(VT_ARRAY|VT_BSTR etc.); List<DateTime> is mapped to DateTimeOffset. Covers
every element type AttributeValueCodec produces.
2026-06-17 06:07:40 -04:00
Joseph Doherty b5333d0f15 fix(dcl): default MxGateway advises to supervisory when WriteUserId==0
Writes through the MxGateway data connection (e.g. the Ipsen MoveIn flow
writing MES-receiver attributes) hung ~30s and changed nothing, while reads
of the same attributes worked. Root cause: MXAccess only accepts a write on
an item that holds a SUPERVISORY advise; the write path did AddItem +
WriteBulk with no advise and the monitoring subscription used a plain Advise,
so the worker's synchronous COM Write blocked until the gateway command
timeout. (Plain, non-secured writes need no user/login.) Verified live: with a
supervisory advise the write returns ok in ~22ms; without it it does not.

When the connection has no MXAccess write-user context (WriteUserId == 0) it
now behaves as a supervisory client: every advise defaults to
AdviseSupervisory — both the monitoring subscription (SubscribeAsync) and the
write path — so one connection can read and write. A supervisory advise still
delivers OnDataChange (the worker treats either advice kind as sufficient for
updates) so monitoring is unaffected, and the worker's UnAdvise tears down
either kind, so unsubscribe is unchanged. AdviseSupervisory is issued as a raw
MxCommandKind.AdviseSupervisory via the session's Invoke (the client package
exposes only plain Advise). The advise runs at most once per handle via a
Lazy<Task> so a concurrent first-time subscribe+write on the same new handle
both await the same advise (neither writes before it completes); a faulted
advise is evicted so the next write retries. Dropped on unsubscribe. A
configured non-zero WriteUserId keeps the prior plain-advise behaviour.
2026-06-17 05:52:30 -04:00
Joseph Doherty d4ec84d5fb fix(inbound): log swallowed scope-creation failure + test scope disposal on script throw 2026-06-16 22:00:10 -04:00
Joseph Doherty daff1446d8 feat(inbound): expose read-only Database helper on InboundScriptContext 2026-06-16 22:00:10 -04:00
Joseph Doherty 16fc62bfa0 test(inbound): add namespace + Query() coverage for InboundDatabaseHelper 2026-06-16 22:00:10 -04:00
Joseph Doherty 90ac746fdc feat(inbound): read-only InboundDatabaseHelper for inbound scripts 2026-06-16 22:00:10 -04:00
Joseph Doherty f63055c296 chore(inbound): task-persistence for Ipsen MES MoveIn plan 2026-06-16 22:00:10 -04:00
Joseph Doherty 6fbf3e71fd docs(inbound): implementation plan for Ipsen MES MoveIn 2026-06-16 22:00:10 -04:00
Joseph Doherty d8ccad6f54 docs(inbound): design for Ipsen MES MoveIn -> reactor MES receiver
SAPID(+side) -> BTDB Machine.SAPID -> Code -> instance; inbound script does
the lookup via a new scoped read-only DB helper, then routes to a new T1
template script that gates on MoveInReadyFlag and writes the MoveIn to the
correct Left/Right MES receiver. -LT deferred.
2026-06-16 22:00:10 -04:00
Joseph Doherty e77e209b8a docs(m4): fix bundle CLI example option names in transport design §13 (--output/--input, not --out/positional) 2026-06-16 20:37:51 -04:00
Joseph Doherty dd545281e6 docs(m4.3): reconcile CLI README + Component-CLI to registered commands/options (document bundle group; fix option drift) 2026-06-16 20:30:40 -04:00
Joseph Doherty 0780c2e49e docs(m4.4): clear stale deferred/no-op markers for shipped features (relay, bundle-import audit, M5 redaction, audit drill-in, Transport CLI, traceability)
- SiteCallAudit/ServiceCollectionExtensions.cs: drop "still deferred" note on relay; point to SiteCallAuditActor where it lives
- Transport/Import/BundleImporter.cs: update "Only LoadAsync implemented" to reflect all three phases shipped
- SiteRuntime/Scripts/AuditingDbCommand.cs: replace two M5-deferred redaction comments with accurate references to AuditLogOptions.PerTargetOverrides
- SiteRuntime/Scripts/ScriptRuntimeContext.cs: replace "M5 will layer redaction" note with accurate description of shipped redactor
- CentralUI/AuditLogPage.razor.cs: replace "Bundle C wires… no-op seam" with accurate description of HandleRowSelected implementation
- docs/plans/2026-05-24-transport-design.md §13: update from "CLI Deferred / not built in v1" to reflect shipped BundleCommands.cs; update Open Questions entry
- docs/plans/2026-05-24-transport.md: convert Out-of-Scope "Do NOT build CLI" reminder to a factual note that it shipped
- docs/plans/2026-05-24-transport.md.tasks.json: flip all 30 tasks from pending → done (entire Transport feature shipped)
2026-06-16 20:30:29 -04:00
Joseph Doherty 13605d3dfd docs(m4.1): reconcile Config-DB AuditLog schema + Commons (AuditEvent/ApiKey/SiteCall/NotificationType) to shipped code 2026-06-16 20:29:16 -04:00
Joseph Doherty c3b046457e docs(m4.2): reconcile InboundAPI (Bearer/audit-timing/type-validation), Security (cookie session, role names), Notification (Email-only, AuditKind vocab) to code 2026-06-16 20:27:43 -04:00
Joseph Doherty 9106efafd8 Merge main (DCL alarm fixes 06ef177..9b78e60) into M3 branch 2026-06-16 20:20:27 -04:00
Joseph Doherty fb5f14e04f docs(m3): document Script Analysis component (#25); reconcile consumer specs + README/CLAUDE component list 2026-06-16 20:05:24 -04:00
Joseph Doherty 069757209a fix(scriptanalysis): M3.6 — full-framework analysis refs close forbidden-type-in-allowed-ns blind spot; pin Process/Stopwatch; fix stale codec test; drop dead ContainsInCode 2026-06-16 20:00:28 -04:00
Joseph Doherty 9b78e6071d fix(dcl): identify MxGateway native alarms by object-relative reference
Surface native (Galaxy/MxGateway) alarms by their object-relative reference
(e.g. "Z28061.HeartbeatTimeoutAlarm") instead of the gateway's full provider
reference ("Galaxy!<area>.<object>.<alarm>"). The area is already preserved in
Category and the object reference is globally unique within the galaxy, so the
full provider prefix added only noise to the alarm identity operators see.

MxGatewayAlarmMapper.MapTransition/MapSnapshot now set SourceReference from
SourceObjectReference, falling back to AlarmFullReference only when the gateway
omits the object reference. +2 mapper tests; full DCL suite green (158).
2026-06-16 19:46:44 -04:00
Joseph Doherty cf935d5744 refactor(centralui): M3.5 ScriptAnalysisService uses shared deny-list + delegates trust verdict 2026-06-16 19:40:03 -04:00
Joseph Doherty 64d6ac7288 refactor(siteruntime): M3.3 ValidateTrustModel delegates to shared ScriptAnalysis + compile-surface parity test 2026-06-16 19:37:50 -04:00
Joseph Doherty 14bd25196a feat(templateengine): M3.2 deploy gate delegates to shared ScriptAnalysis (real compile + authoritative forbidden-API) 2026-06-16 19:36:03 -04:00
Joseph Doherty 784fee7b07 refactor(inboundapi): M3.4 ForbiddenApiChecker delegates to shared ScriptAnalysis validator 2026-06-16 19:35:43 -04:00
Joseph Doherty 361e7f41ba fix(dcl): broadcast SnapshotComplete sentinel to all alarm subscribers
The MxGateway alarm mapper emits the SnapshotComplete framing sentinel with
empty SourceReference/SourceObjectReference. HandleAlarmTransitionReceived
routed every transition by prefix match against the subscriber's source, so
the empty-ref sentinel ('' .StartsWith("<src>.") == false) was dropped for
any specific source. The NativeAlarmActor buffers snapshot conditions and only
flushes them on SnapshotComplete, so statically-active native alarms delivered
only in the initial snapshot (no later live transition) never surfaced.

Broadcast the SnapshotComplete sentinel to all alarm subscribers (bypassing the
source match + type filter) so each NativeAlarmActor's snapshot swap completes.
Adds a regression test using the real empty-ref sentinel against a specific
(prefix) source.
2026-06-16 19:33:41 -04:00
Joseph Doherty 069c0e0b1a fix(scriptanalysis): M3.1 review — Pass 2 self-sufficient descent, pin nested-forbidden + nameof cases, drop dead code 2026-06-16 19:29:59 -04:00
Joseph Doherty 4f2b17ce6d feat(scriptanalysis): M3.1 shared trust validator + compiler + compile surfaces + tests 2026-06-16 19:18:39 -04:00
Joseph Doherty 0cc8642cfa docs(m3): implementation plan + tasks for shared ScriptAnalysis consolidation 2026-06-16 19:09:12 -04:00
Joseph Doherty 8e99f22b24 docs(m3): design — shared ScriptAnalysis project consolidating the 4 trust-model analyzers 2026-06-16 19:07:32 -04:00
Joseph Doherty 06ef1779bd fix(dcl): deliver initial-read seed value after subscription registration
DataConnectionActor seeded a tag's initial value by Tell-ing TagValueReceived
from HandleSubscribe's background task, which runs BEFORE HandleSubscribeCompleted
registers the instance's tags in _subscriptionsByInstance. HandleTagValueReceived's
fan-out then found no subscriber and dropped the value. A tag that soon gets a
data-change notification recovers, but a STATIC tag (e.g. an idle MES field that
never changes) was left Uncertain forever — the dropped seed was its only value.

Seeds now ride back on SubscribeCompleted and are delivered after registration,
reusing HandleTagValueReceived's generation guard, fan-out and quality accounting.
+1 regression test (DCL026).
2026-06-16 18:42:28 -04:00
Joseph Doherty 33af948651 Merge feature/native-typed-json: native-typed JSON for List attribute values + data normalization
List values now encode as native-typed JSON ([10,20], [true,false], ISO dates;
strings stay quoted) via AttributeValueCodec; Decode reads both native and the
earlier array-of-strings form for every element type. Already-persisted old-form
data is normalized on the fly: idempotent central startup normalizer
(ListValueNormalizer), active site-SQLite normalization on InstanceActor
override-load, and normalize-on-import in the bundle importer. Instance-override
writes now stamp ElementDataType (#93/M3). Full solution 0/0; feature-targeted
tests green. Plan: docs/plans/2026-06-16-native-typed-json.md.
2026-06-16 18:36:07 -04:00
Joseph Doherty dc9f31537a docs: record final-review follow-ups (deployed-snapshot normalization gap I-1; CLI native-form help example) 2026-06-16 18:34:34 -04:00
Joseph Doherty c53b621b85 docs: mark native-typed JSON feature complete; update Component-Commons codec note
NJ-6: full solution builds 0/0; feature-targeted tests green (Commons codec 38,
TemplateEngine InstanceService 17, ConfigDB normalizer 8, Transport serializer 12,
SiteRuntime InstanceActor 47). Component-Commons now describes the native-typed
List encoding + read-both decode + the three normalization paths. #93/M3 folded in.
2026-06-16 18:27:10 -04:00
Joseph Doherty feeae1371e fix(multivalue): NJ-3/NJ-4/NJ-5 review fixes
- NJ-3: widen per-row catch to Exception (an STJ encode failure can't abort startup); drop dead null-guard already excluded by the SQL filter
- NJ-4: capture logger/instanceName in locals for the fire-and-forget normalize continuation (match the sibling pattern in this actor)
- NJ-5: emit a warn-log when a malformed List value is imported verbatim; thread an optional ILogger<BundleImporter> to the sync re-import site
2026-06-16 18:25:42 -04:00
Joseph Doherty f4b101b532 feat(db): idempotent startup normalizer rewriting List values to native JSON 2026-06-16 17:50:19 -04:00
Joseph Doherty e3d804a1a6 feat(transport): normalize List attribute values to native JSON on import 2026-06-16 17:50:05 -04:00
Joseph Doherty 5841cec958 feat(siteruntime): normalize old-form List static overrides to native JSON on load 2026-06-16 17:49:21 -04:00
Joseph Doherty bf80ca1388 test(commons): NJ-1 review — backward-compat decode tests for old-form Float/DateTime + assert DateTime list is quoted-string array 2026-06-16 17:38:57 -04:00
Joseph Doherty abe8832e9e feat(template): stamp ElementDataType on instance attribute overrides
Set existingOverride.ElementDataType and newOverride.ElementDataType from
templateAttr.ElementDataType in both the update and create branches of
SetAttributeOverrideAsync, so the persisted InstanceAttributeOverride row
always carries the element type for later central normalizer use (#93/M3).
2026-06-16 17:33:15 -04:00
Joseph Doherty 180d55482b feat(commons): native-typed JSON for List values; Decode reads both forms 2026-06-16 17:32:40 -04:00
Joseph Doherty 69f7c526d0 docs: implementation plan for native-typed JSON List values + normalization
6 tasks (NJ-1..NJ-6): native codec + read-both decode; stamp override
ElementDataType (#93/M3); idempotent central startup normalizer; site
override-load normalization; normalize-on-import; integration + docs.
2026-06-16 17:13:14 -04:00
Joseph Doherty d312dfb139 fix(management): honor DisableLogin on the Basic-Auth CLI surfaces
DisableLogin only swapped the cookie auth scheme (AutoLoginAuthenticationHandler),
which covers the interactive UI. The CLI authenticates POST /management, the audit
REST endpoints, and the SignalR debug-stream hub with HTTP Basic, and each ran its
own hardcoded Basic->LDAP check that ignored DisableLogin. In a login-disabled (e.g.
no-LDAP) deployment that locked the CLI out: every call returned 401 AUTH_FAILED.

Add ManagementAuthenticator, which centralizes the management/CLI auth flow:
when ScadaBridge:Security:Auth:DisableLogin is true it synthesizes the same dev
principal as AutoLoginAuthenticationHandler (configured user, all roles, system-wide)
and bypasses Basic->LDAP; otherwise the unchanged Basic->LDAP flow runs. Wired into
ManagementEndpoints (delegates), AuditEndpoints (delegates), and DebugStreamHub
(bypass branch). +6 unit tests; ManagementService.Tests green (140).
2026-06-16 17:12:17 -04:00
Joseph Doherty 91b1aa1275 docs: design for native-typed JSON List attribute values + data normalization
Encode emits native-typed JSON ([10,20], [true,false], ISO dates); Decode reads
both old (array-of-strings) and new forms. Existing data normalized via an
idempotent central MS SQL startup normalizer, active site SQLite normalization in
the InstanceActor override-load path, and normalize-on-import for bundles.
Approved via brainstorming (Approach B, thorough).
2026-06-16 17:08:38 -04:00
Joseph Doherty cdf0a199cb Merge feature/multivalue-attribute: structured multi-value (List) attributes
First-class DataType.List (homogeneous list of a scalar ElementDataType) round-tripping
through authoring, flatten, site runtime, OPC UA read+write, gRPC streaming, validation,
management API, CLI, Transport bundles, and Central UI (TemplateEdit + InstanceConfigure).

Canonical AttributeValueCodec (JSON, invariant culture); in-memory typed List<T> vs
persisted/streamed JSON; idempotent migration; element type fixed by base. 255
feature-targeted tests; full solution builds 0/0. Plan: docs/plans/2026-06-16-multivalue-attribute.md.
2026-06-16 16:51:36 -04:00
Joseph Doherty 94be5e813b fix(siteruntime): decode List value to typed array before DCL write (OPC UA array write path) 2026-06-16 16:48:28 -04:00
Joseph Doherty 734c161383 docs: mark multi-value (List) attribute feature complete; document DataType.List + ElementDataType in Component-Commons
MV-15 integration checkpoint: full solution builds 0/0; feature-targeted tests
green across Commons, TemplateEngine, SiteRuntime, DataConnectionLayer,
Communication, Transport, ManagementService, CLI, CentralUI (255 tests).
2026-06-16 16:34:56 -04:00
Joseph Doherty ca9ee5ea2a fix(ui): MV-14 review — surface SetAttributeOverride failures in InstanceConfigure save loop (no false success toast) 2026-06-16 16:32:28 -04:00
Joseph Doherty 100540b153 fix(multivalue): MV-11/MV-13 review nits — correct CLI attribute-delete README synopsis; explicit Disabled + dead-branch cleanup in TemplateEdit list editor 2026-06-16 16:27:44 -04:00
Joseph Doherty ae2e1efb1c feat(ui): List attribute override editor in InstanceConfigure
When overriding a List attribute, render the shared AttributeListEditor
(whole-list replacement; element type fixed by the base, shown read-only via
ShowElementType=false) instead of the single-line input. Loading an existing
override decodes its JSON into rows (malformed -> empty); saving encodes rows to
canonical JSON with a pre-submit Decode round-trip guard surfacing element
errors inline. Clearing removes the InstanceAttributeOverride row
(repository-direct, mirroring native-alarm-source overrides). Non-List override
UX unchanged.
2026-06-16 16:25:58 -04:00
Joseph Doherty ba7331e67c feat(ui): List attribute editor in TemplateEdit 2026-06-16 16:20:08 -04:00
Joseph Doherty 85db4571b2 feat(cli): --element-type and JSON --value for List attributes 2026-06-16 16:18:08 -04:00