Commit Graph

1006 Commits

Author SHA1 Message Date
Joseph Doherty a54c7a9366 feat(historian-gateway): HistorianEvent->HistoricalEvent mapper (+ clamped severity)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 16:32:38 -04:00
Joseph Doherty c7296d7458 feat(historian-gateway): sample/aggregate->DataValueSnapshot + quality mapper (Wonderware parity)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 16:32:38 -04:00
Joseph Doherty 3226b87818 feat(historian-gateway): DriverDataType->HistorianDataType mapper + write-gap fallbacks (matrix-guarded)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 16:32:38 -04:00
Joseph Doherty c822a6b196 feat(historian-gateway): HistoryAggregateType->RetrievalMode mapper (matrix-guarded)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 16:32:38 -04:00
Joseph Doherty a98fc46d26 feat(historian-gateway): scaffold Gateway driver project + consume client package
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
2026-06-26 16:18:50 -04:00
Joseph Doherty 04159fd716 test(otopcua): ConfigComposer->ParseComposition DeviceHost round-trip (follow-up E) 2026-06-26 15:39:40 -04:00
Joseph Doherty 0074f37a64 test(otopcua): tighten multi-device collapse assertion + clear warn-state on removal (follow-up E) 2026-06-26 15:16:59 -04:00
Joseph Doherty 50f08635ec feat(otopcua): multi-device-per-driver FixedTree partition (follow-up E) 2026-06-26 15:00:11 -04:00
Joseph Doherty 51721df563 refactor(otopcua): extract authored-only send helper + empty-authored dropped-path test (follow-up D) 2026-06-26 14:44:26 -04:00
Joseph Doherty 05c820795a perf(otopcua): one SetDesiredSubscriptions per driver per redeploy (follow-up D) 2026-06-26 14:30:16 -04:00
Joseph Doherty cde16063d9 test(otopcua): negative + convergence coverage for rebind re-trigger (follow-up C) 2026-06-26 14:18:01 -04:00
Joseph Doherty 533671487e feat(otopcua): re-trigger discovery on config-unchanged rebind (follow-up C) 2026-06-26 14:06:50 -04:00
Joseph Doherty adcd7b57c1 feat(otopcua): driver-level equipment resolution + per-equipment discovered-plan cache (follow-up E) 2026-06-26 13:33:21 -04:00
Joseph Doherty cb7ce7f171 feat(otopcua): EquipmentNode carries DriverInstanceId/DeviceId/DeviceHost (follow-up E projection) 2026-06-26 13:07:31 -04:00
Joseph Doherty e7d5ebe956 fix(otopcua): cancel pending rediscover timer on TriggerRediscovery + test hardening (follow-up C) 2026-06-26 12:57:08 -04:00
Joseph Doherty f7358bf4fd feat(otopcua): DriverInstanceActor.TriggerRediscovery message (follow-up C) 2026-06-26 12:45:26 -04:00
Joseph Doherty a1a655e6c9 test(otopcua): Once re-discovery reruns one pass per reconnect + comment tidy (follow-up B) 2026-06-26 12:38:52 -04:00
Joseph Doherty ce34816a50 feat(otopcua): DriverInstanceActor honors RediscoverPolicy (Never/Once/UntilStable) (follow-up B) 2026-06-26 12:32:28 -04:00
Joseph Doherty efbdaf853c feat(otopcua): set Modbus/S7/Galaxy re-discovery policy to Once + Once-branch test (follow-up B) 2026-06-26 12:26:28 -04:00
Joseph Doherty a378b572af feat(otopcua): add ITagDiscovery.RediscoverPolicy + per-driver assignments (follow-up B) 2026-06-26 12:18:44 -04:00
Joseph Doherty c2c368dcec feat(otopcua): make FixedTree re-discovery per-pass timeout injectable (follow-up A) 2026-06-26 12:12:47 -04:00
Joseph Doherty 25ccd25b6b test(otopcua): assert exact discovered NodeId in the e2e 2026-06-26 09:04:26 -04:00
Joseph Doherty 5104540e32 test(otopcua): cover discovered-node rebind drop + clarify re-apply scope 2026-06-26 09:01:01 -04:00
Joseph Doherty 1aa13ebd27 test(otopcua): end-to-end discovered-node injection + value flow 2026-06-26 08:46:28 -04:00
Joseph Doherty 0788bad145 feat(otopcua): re-inject discovered nodes after address-space rebuild 2026-06-26 08:36:52 -04:00
Joseph Doherty b1e4fba792 fix(otopcua): skip redundant discovered-node re-apply + guard tests 2026-06-26 08:28:42 -04:00
Joseph Doherty 21298ec1b2 fix(otopcua): resume discovery on actor context + bound/harden re-discovery 2026-06-26 08:19:12 -04:00
Joseph Doherty b9b8d3d389 feat(otopcua): inject discovered nodes into the equipment projection on connect 2026-06-26 07:59:01 -04:00
Joseph Doherty cf6b1abf4c feat(otopcua): re-run driver discovery on reconnect 2026-06-26 07:44:28 -04:00
Joseph Doherty 51634cca38 feat(otopcua): driver-instance post-connect bounded re-discovery 2026-06-26 07:40:24 -04:00
Joseph Doherty 34fc304712 fix(otopcua): guard discovered-injection equipment id + cover deferred forwarding 2026-06-26 07:27:09 -04:00
Joseph Doherty ccf93fc029 feat(otopcua): OpcUaPublishActor handles discovered-node materialisation 2026-06-26 07:24:22 -04:00
Joseph Doherty 598cdfad5a feat(otopcua): applier pass to materialise discovered nodes idempotently 2026-06-26 07:16:36 -04:00
Joseph Doherty f8406d348c fix(otopcua): report NodeAdded model-change outside the node Lock 2026-06-26 07:06:43 -04:00
Joseph Doherty 93f7586590 fix(otopcua): guard root-level discovered var parent + tighten mapper 2026-06-26 06:59:34 -04:00
Joseph Doherty 33b0e639a5 feat(otopcua): GeneralModelChangeEvent(NodeAdded) for runtime node adds 2026-06-26 06:55:52 -04:00
Joseph Doherty d7a0da5ea1 feat(otopcua): map discovered nodes under an equipment subfolder 2026-06-26 06:47:18 -04:00
Joseph Doherty da55c6913d feat(otopcua): capturing address-space builder for driver discovery 2026-06-26 06:39:18 -04:00
Joseph Doherty 235b8b8e6d fix(focas): serialize per-device wire I/O + bound reads; tolerate AdminUI config formats
Equipment tags were stuck at Bad_WaitingForInitialData on the deployed driver: the equipment poll, fixed-tree loop, probe and recycle shared one FOCAS/2 socket with no serialization, and the steady-state read had no timeout — concurrent reads collided and a stalled read hung forever, never overwriting the node's initial-data seed.

- SynchronizedFocasClient: per-device SemaphoreSlim gate + per-call timeout around every wire op (Connect/Probe gated, not double-bounded); wired in EnsureConnectedAsync. ReadAsync/WriteAsync map a per-call timeout to BadCommunicationError instead of rethrowing.
- FlexibleStringConverter on FOCAS config Series: the AdminUI persists the enum as a number ("series":6); accept number-or-string instead of throwing -> stub.
- FocasHostAddress.TryParse tolerates a scheme-less {ip}[:{port}] (AdminUI hostAddress form); canonical focas:// unchanged, malformed schemes still rejected.

247 FOCAS tests green; each fix has a regression test. Live-validated on wonder-app-vd03 (tags read Good).
2026-06-26 05:59:54 -04:00
Joseph Doherty 5f0a52864c feat(focas): real FANUC 30i/31i-B PDU-v3 support (live-validated on a 31i-B)
First real FOCAS hardware contact (Makino Pro 5 / 31i-B @ 10.201.31.5). A full
v3 data-PDU capture corrected the initial diagnosis: the v3 block envelope is
identical to v1, so only specific payload structs / request math / one client
robustness gap were wrong — not "framing rewrites".

Fixes (all re-validated live through the fixed driver):
- version gate: accept inbound PDU {1,3}, keep emitting v1 (FocasWireProtocol).
- cnc_rdtimer: 8-byte {minute,msec} payload is little-endian (ParseTimer) — the
  only decode with an in-range msec field.
- pmc_rdpmcrng: request range widened to the data-type byte width
  (end = start + width - 1) so a Word/Long isn't truncated to 0 values
  (was spurious BadOutOfRange); decode extracted to ParsePmcRange.
- cnc_rdsvmeter: per-axis LOADELM is 8 bytes (not 12) and names come from the
  0x0089 block — ParseServoMeters fixes the misaligned 655360 garbage. Also the
  "hang" was NetworkStream.ReadAsync not aborting a stalled socket: ReadExactlyAsync
  now disposes the stream on cancellation so a stalled peer can't wedge a poll loop.
- cnc_rddynamic2: contract guard rejecting axis < 1 (driver poll already 1-based).
- FocasDriverProbe: run a real wire session (initiate + cnc_statinfo) instead of
  degrading to Ok=true "TCP reachability only" when FWLIB is absent — a bare TCP
  listener no longer reports HEALTHY.

cnc_rdparam (0x000e) is unsupported on this control — EW_FUNC across 14
request-framing variants x 4 known-present params; needs a reference FWLIB trace
or is restricted. Deferred (deployed config uses macros, not parameters).

Tests: FOCAS suite 234 green (+16), full solution builds 0 errors. Raw v3
captures checked in under tests/.../Fixtures/v3/. Capture tools under scripts/focas/.

Docs: docs/plans/2026-06-25-focas-pdu-v3-{30i-b-support,implementation-plan}.md,
docs/drivers/FOCAS.md, docs/v2/focas-version-matrix.md,
docs/deployments/wonder-app-vd03-makino-z-34184.md.
2026-06-25 16:41:42 -04:00
Joseph Doherty 23b42b424d fix(code-review): resolve OpcUaServer-001 — UNS Area/Line rename refreshes folder DisplayName
A rename-only deploy produced an IsEmpty plan that short-circuited before MaterialiseHierarchy,
leaving the OPC UA folder DisplayName stale. AddressSpacePlanner now diffs UnsAreas/UnsLines by
stable id into a RenamedFolders set (counted in IsEmpty); the applier refreshes the folder in
place via a new UpdateFolderDisplayName on ISurgicalAddressSpaceSink (forwarded through
DeferredAddressSpaceSink so it is NOT inert on driver hosts; falls back to rebuild when the sink
is non-surgical). DeploymentArtifact byte-parity untouched (rename rides the existing Name
round-trip). No EF migration, no serialized wire/proto contract change. +13 OpcUaServer tests, Runtime rebuild test.
2026-06-20 23:10:24 -04:00
Joseph Doherty 94eec70fb0 fix(code-review): resolve Batch 3 wave A (OpcUaServer history/guard, ControlPlane topology gate)
- OpcUaServer-002: HistoryRead-Events NumValuesPerNode==0 now maps to unbounded (int.MaxValue) instead of the backend default-cap sentinel; no Core.Abstractions contract change (+EventMaxEvents helper tests)
- OpcUaServer-004: EnsureAddressSpaceCreated guard on public mutators -> clear InvalidOperationException instead of bare NRE if called pre-start (+tests)
- OpcUaServer-003: Deferred (endUtc inclusive/exclusive needs live Wonderware boundary confirmation)
- Configuration-013: wire DraftValidator.ValidateClusterTopology into AdminOperationsActor deploy gate (read-only, no migration) (+2 tests)
2026-06-20 22:53:29 -04:00
Joseph Doherty ab57e53b92 fix(code-review): resolve Batch 2 open findings (AbCip, AbLegacy, Galaxy, FOCAS)
- Driver.AbCip.Contracts-001: parse 'writable' from TagConfig JSON (default true) instead of hardcoding
- Driver.AbCip.Contracts-002/-003: Dt type comment; drop dead [Display]/[Range] annotations
- Driver.AbCip.Contracts-004: dedicated AbCipEquipmentTagParser test class (+15)
- Driver.AbCip-017: document Tick severity Low-fallback on Bad severity read
- Driver.AbLegacy.Contracts-002/-003/-004: isArray-scalar remarks (+tests), MaxTagBytes/ForFamily docs
- Driver.Galaxy.Browser-003 + Driver.Galaxy.Contracts-003: extract ResolveApiKey -> GalaxySecretRef (dedup)
- Driver.Galaxy-019: cache buffered-interval only on Ok + ILogger warnings + ClassifyIntervalReply (+tests)
- Driver.FOCAS.Contracts-002: thread WriteIdempotent through DiscoverAsync (+test)
2026-06-20 22:43:36 -04:00
Joseph Doherty 98b27fc1b6 fix(code-review): resolve Batch 1 open findings (AdminUI auth, AlarmHistorian dispose guards, docs)
- AdminUI-001: gate Script editor pages at Administrator,Designer + loosen ScriptAnalysis backend to match
- AdminUI-004: explicit [Authorize] on FleetStatus/Alert/ScriptLog hubs
- Core.AlarmHistorian-014: ObjectDisposedException guards on GetStatus/RetryDeadLettered (+ regression test)
- Core.Scripting.Abstractions-004/-007: Deadband tolerance doc + stale ScriptedAlarms.md path
- Host-003: correct config-overlay precedence in ServiceHosting.md
- Configuration-014: LdapGroupRoleMapping collation-dependency doc
- Driver.TwinCAT.Contracts-002: Structure enum doc (discovery-only sentinel)
2026-06-20 22:30:33 -04:00
Joseph Doherty 3e789dcafc review(Driver.AbCip): thread ElementCount/IsArray through factory tag DTOs
Cross-module fix from the review sweep. -018 (Medium): AbCipTagDto/AbCipMemberDto dropped
elementCount/isArray, so driver-config-authored array tags read as a single scalar. Added the
two optional JSON fields (additive; missing -> scalar as before) threaded into the tag def +
TDD.
2026-06-19 12:29:40 -04:00
Joseph Doherty 40749d3f67 review(OpcUaServer): fix silent auto-unshelve failure (empty User -> 'system')
Cross-module fix from the review sweep. -007 (Medium): OnTimedUnshelve built its AlarmCommand
with User=string.Empty, so Part9StateMachine.ApplyUnshelve rejected it (ArgumentException,
swallowed) and a TimedShelve never auto-expired. Pass the canonical 'system' user; the
AlarmAck-gate bypass is preserved. Repurposed the test that had encoded the bug.
2026-06-19 12:29:40 -04:00
Joseph Doherty 298bd4bfe5 review(Driver.OpcUaClient.Browser): add JsonStringEnumConverter (systemic enum bug)
Cross-module fix from the review sweep. -003 (Medium): the browser's JsonOpts lacked
JsonStringEnumConverter (the factory+probe both carry it), so AdminUI string-enum configs
(AuthType/SecurityPolicy/SecurityMode/TargetNamespaceKind) threw on deserialize. Added the
converter (accepts string AND numeric) + TDD.
2026-06-19 12:29:39 -04:00
Joseph Doherty 7580e37807 review(Driver.TwinCAT.Cli): clean parse errors + FlushLogging() in finally
Re-review at 7286d320. -008 (Low): ParseValue maps FormatException/OverflowException to a
clean CommandException (was raw stack trace) + tests. -009: FlushLogging() in all 5 commands'
finally blocks (parity with AbCip.Cli).
2026-06-19 12:08:45 -04:00
Joseph Doherty f8bf067243 review(Driver.S7.Cli): endpoint validation + cancellation/flush/write-lock consistency
Re-review at 7286d320. -008 (Medium): S7CommandBase.ValidateEndpoint (port range + timeout>0)
in all commands +tests. -009 clean OperationCanceledException handling; -010 FlushLogging()
in subscribe finally; -011 lock console writes in OnDataChange. -012 (Verdict headline) deferred.
2026-06-19 12:08:45 -04:00
Joseph Doherty b0f9b8016a review(Driver.Modbus.Cli): FlushLogging() + interval validation + banner-before-events
Re-review at 7286d320. -009 FlushLogging() in finally; -010 validate --interval-ms positive
(+8 tests); -011 print subscribe banner before wiring OnDataChange (no main/poll-thread
interleave). Parity with AbCip.Cli.
2026-06-19 12:08:45 -04:00