Commit Graph

3822 Commits

Author SHA1 Message Date
Joseph Doherty baf8cc48c3 Merge b640-small: CI readme drift check, SlowUnit tags, LDAP bind pw var, historian/MTConnect docs (#606 #619 #600 #613)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:03:59 -04:00
Joseph Doherty 216dc63a28 Merge b640-focas: FOCAS Status/ subtree, PMC range coalescing, parts-count docs (#257 #266 #258, tracker #633)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:03:53 -04:00
Joseph Doherty 9244ae2c31 docs(mtconnect): feature status table; EVENT enums + DATA_SET/TABLE listed as unsupported (#613)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:03:26 -04:00
Joseph Doherty c86204b7d6 fix(focas): PMC coalescing treats any rc refusal as per-tag fallback, skips transient busy memo, one connect per batch (#266)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:03:25 -04:00
Joseph Doherty 598cb133a8 docs(historian): document deferred non-numeric types and UInt16->UInt4 fallback (#600)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:03:14 -04:00
Joseph Doherty bdf13d29ba chore(docker-dev): LDAP bind password via OTOPCUA_LDAP_BIND_PASSWORD with dev default (#619)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:02:51 -04:00
Joseph Doherty ad108f6a02 test(mqtt): tag 16 wall-clock-bound tests Category=SlowUnit; document local opt-out (#606)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:02:44 -04:00
Joseph Doherty 555e8d8047 Merge b640-622: OpcUaClient history + reconnect wire tests (#622)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 17:02:21 -04:00
Joseph Doherty 4a93d4cead test(opcuaclient): reconnect + subscription-resume wire test via restartable rig (#622)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:59:58 -04:00
Joseph Doherty 487d394cde docs(focas): Status/ flags, PMC coalescing, parts count via PARAM:6711, read-only policy (#257 #266 #258)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:59:08 -04:00
Joseph Doherty 8d916f2af7 feat(focas): coalesce adjacent PMC reads into one pmc_rdpmcrng (#266)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:59:07 -04:00
Joseph Doherty 73e54656f8 Merge b640-s7 (S7 live codec coverage + STRING/DT BadOutOfRange #632)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:57:59 -04:00
Joseph Doherty 489dc3d50a Merge b640-646 (AbLegacy indirect addressing #646)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:57:59 -04:00
Joseph Doherty dac0747bb4 docs(s7): fixture DB1 size 2048 references
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:57:24 -04:00
Joseph Doherty 73ec6cec0f Merge b640-642: typed native-alarm editor in RawTagModal (#642)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:57:05 -04:00
Joseph Doherty 73fdb80157 fix(ablegacy): indirect element index up to 999 on PLC-5; clear IndirectLastName on dispose
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:56:58 -04:00
Joseph Doherty 62f19dd013 test(adminui): guard NativeAlarmModel driver set against IAlarmSource implementers (#642)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:56:04 -04:00
Joseph Doherty bbea38483f test(s7): codec live tests use the documented byte-anchored DBB form (#632)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:55:10 -04:00
Joseph Doherty b0b0ce9c77 feat(ablegacy): resolve indirect addresses at read/write time (#646)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:53:56 -04:00
Joseph Doherty 5b36c717de feat(focas): cnc_statinfo status flags as Status/ fixed-tree nodes (#257)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:53:44 -04:00
Joseph Doherty 2aae3c094f test(s7): live python-snap7 coverage for the #632/#638 codecs, TSAP override and PDU size (#632)
Extends the s7_1500 profile (DB1 2048 bytes, 'hex' seed type) with distinct
non-zero seeds for STRING/WSTRING/CHAR/WCHAR/DTL/DATE/TOD/TIME/S5TIME plus
undecodable buffers; S7_1500CodecTests covers reads, BadOutOfRange paths,
write round-trips (S7_SIM_ALLOW_WRITES=1), TSAP keys and negotiated PDU size.
Skips when S7_SIM_ENDPOINT is unreachable.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:53:02 -04:00
Joseph Doherty dcc8adcf48 test(opcuaclient): HistoryRead Raw/AtTime/Processed wire tests against in-process server (#622)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:52:59 -04:00
Joseph Doherty 6951faa08f feat(adminui): typed native-alarm section in the raw tag modal (#642)
NativeAlarmModel: IsAlarm=false now removes the alarm key; advisory Warnings, driver gating, order-safe Edit.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:52:55 -04:00
Joseph Doherty 41e600e069 fix(s7): classic STRING/DT unrepresentable writes map to BadOutOfRange, not BadCommunicationError (#632)
S7.Net's own STRING/DT encoders throw ArgumentException; that fell into the
generic write catch (BadCommunicationError + Degraded health). Now surfaced as
S7ValueRangeException like the WSTRING/DATE codecs.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:51:20 -04:00
Joseph Doherty 839f32910d ci: gate code-reviews README drift via regen-readme.py --check (#606)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:49:40 -04:00
Joseph Doherty a8b07ae12d docs(claude): exact condition Severity + two-element select-clause trap
v2-ci / build (push) Successful in 6m26s
v2-ci / unit-tests (push) Failing after 19m7s
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:32:24 -04:00
Joseph Doherty cd03b1b51d Merge b626-livealarm: client alarm filter browse paths + exact Part 9 condition severity
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:20:51 -04:00
Joseph Doherty aba3aa7a8d fix(opcua): condition Severity is carried verbatim, not re-bucketed to 5 points
OtOpcUaNodeManager.MapSeverity re-bucketed every projected severity onto the
SDK's five EventSeverity values (<800 -> 700, else 900), defeating #626's exact
OpcUaSeverity: an MTConnect Fault (800) read and evented as 900; a scripted
alarm graded 1000 published 900 while /alerts said 1000; a native Low (200)
published 300. EventSeverity merely names points on the 1..1000 scale, so the
node now carries the value clamped to 1..1000 (ToPart9Severity), at
materialise, projection and in the delta gate.

MTConnect wire test now asserts Fault ActiveState=true/Severity=800 and the
Normal clear ActiveState=false/Retain=false/Severity=800.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:19:27 -04:00
Joseph Doherty 49a470a080 fix(client): alarm filter selected ActiveState/Id as ONE QualifiedName — every event read inactive
EventFilter.AddSelectClause(type, "ActiveState/Id") does not split the string;
it selects a single QualifiedName named "ActiveState/Id", which no event carries.
AckedState/Id, ActiveState/Id and EnabledState/Id therefore always arrived null
and Client.CLI 'alarms' (and Client.UI) reported every alarm inactive and
unacknowledged while a direct Read said Active. Pre-existing since a2883b82;
server-side event delivery was correct (proven with two-element select clauses,
incl. after #629). Select them with explicit two-element browse paths.

Over-the-wire test drives OpcUaClientService.SubscribeAlarmsAsync against the
in-process server (native + scripted); ServerEventRig now also selects
ActiveState/Id, Severity and Retain.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 16:19:27 -04:00
Joseph Doherty cc7cd2db11 docs(claude): MTConnect CONDITION alarms (#626)
v2-ci / build (push) Successful in 4m44s
v2-ci / unit-tests (push) Failing after 20m37s
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:48:45 -04:00
Joseph Doherty b82c09d2fe Merge b626-626 (batch #626+): MTConnect CONDITION data items as native Part 9 alarms (opt-in TagConfig.alarm)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:48:04 -04:00
Joseph Doherty 95b389497a test+docs(mtconnect): wire test for the Unavailable blip + ack survival; docs for the review rulings (#626)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:44:50 -04:00
Joseph Doherty 9c8e3da5da fix(mtconnect): review rulings for CONDITION alarms (#626)
- Unavailable keeps an ACTIVE condition active: quality-only BadNoCommunication; on data
  return same statement => quality-only Good, Normal => Clear, different => Retrigger.
- A changed alarm reference set (alarm-bearing delta = full rebuild) clears the whole
  last-told record so every active condition is re-asserted; the same set keeps it.
- Quality-only events go through the host's AlarmQualityUpdate path, never a projected
  snapshot (which could ack a retained unacked condition or rewrite message/severity).
- Deterministic tie-break between equal-level conditions (ordinal nativeCode, then type).
- Dedup key is level+nativeCode+nativeSeverity+qualifier; a text-only change updates nothing.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:38:52 -04:00
Joseph Doherty 6a5f5c5a28 Merge branch 'master' into b626-626 2026-10-08 15:32:51 -04:00
Joseph Doherty bbf5f85c95 docs(claude): batch #626+ facts — re-init release, S7/TwinCAT/AbLegacy/OpcUaClient/MTConnect ports, condition child NodeIds (#629)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:25:19 -04:00
Joseph Doherty f4d513e111 test+docs(mtconnect): CONDITION alarm wire test, reinit barrier, docs (#626)
MTConnectConditionAlarmWireTests drives a scripted Agent's Fault/Normal through the real
driver, NativeAlarmProjector and sink to a client event subscription on the raw device
folder. docs/drivers/MTConnect.md replaces 'CONDITION v1: plain String' with the alarm
mapping, opt-in authoring, multi-condition, ack, replay and comms-loss behaviour, and a
live-check recipe; EVENT vocabularies as enumerations marked won't-fix. AlarmTracking.md
lists the MTConnect source and the two new AlarmEventArgs fields.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:13:30 -04:00
Joseph Doherty 5051275b78 Merge b626-629 (batch #626+): Part 9 condition children get instance-scoped NodeIds ({ConditionId}/{browse path}); shelve methods keep resolving; subtree forgotten on teardown
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:02:07 -04:00
Joseph Doherty 66c47c3ddd fix(opcua): shelve declaration ids from a fixed set, not an address-space lookup; pin instance-id + state-machine-id shelve calls (#629)
New() ran FindNodeInAddressSpace under Lock, taking other node managers' locks (lock-order
risk); the three AlarmConditionType_ShelvingState_* ids are now a static set. Adds wire tests:
TimedShelve/Unshelve by the condition's own instance method ids (object = condition or its
ShelvingState) succeed and report, and a ShelvedStateMachineType_* id call is pinned as
BadMethodInvalid with a positive control.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:01:54 -04:00
Joseph Doherty 653d138637 feat(mtconnect): CONDITION data items as native Part 9 alarms via IAlarmSource (#626)
The parser keeps a condition's text, nativeCode, nativeSeverity, qualifier and type. A new
MTConnectConditionTracker applies MTConnect's multi-condition semantics (per-nativeCode
activation/clearing across /sample deltas, /current baseline reset, Unavailable), and
MTConnectConditionAlarmMapper turns tracker state into Part 9 transitions: Warning=500,
Fault=800, Normal=clear, Unavailable=inactive + BadNoCommunication; escalation or a new
code retriggers. The driver implements IAlarmSource keyed by the alarm tag's RawPath,
diffs against what the server was actually told (only when a handler is attached), so a
reconnect/re-baseline never re-raises an unchanged condition and a missed transition is
delivered on the next subscribe. Ack is a no-op success. Existing CONDITION String tags
are unchanged; a tag becomes an alarm only via the standard TagConfig 'alarm' object.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 15:01:39 -04:00
Joseph Doherty a60279eb92 Merge b626-opcuaclient (batch #626+): #635 subscription tuning, OperationLimits via SDK batching, OTel event counters, CRL options, discovery URL, full aggregate set
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:58:41 -04:00
Joseph Doherty 1219b7b13e fix(opcuaclient): review fixes - drop driver-side OperationLimits chunking (SDK batches), diagnostics as OTel counter, safe discovery selection, options validation at load (#273 #275 #276 #277 #278)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:55:53 -04:00
Joseph Doherty cb85a455c9 feat(alarms): exact OPC UA severity + per-condition quality on native alarm events (#626)
AlarmEventArgs gains optional OpcUaSeverity (supersedes the four-bucket mapping, clamped
1..1000) and QualityStatusCode (projected onto the condition's Quality). An Unspecified-kind
event carrying a quality is a quality-only annotation: the host updates the condition but
publishes no alerts row. Both default null, so every existing native source is unchanged.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:55:46 -04:00
Joseph Doherty 1de8357d13 Merge commit 'e6028678' into b626-629 2026-10-08 14:55:30 -04:00
Joseph Doherty 935ba8d1d4 Merge branch 'master' into b626-opcuaclient 2026-10-08 14:47:49 -04:00
Joseph Doherty d0052d9fed fix(opcua): Part 9 condition children get instance-scoped NodeIds (#629)
Create(assignNodeIds: true) asked CustomNodeManager2.New, whose default returns the
node's current id, so every condition child kept the type's ns=0 instance-declaration id
(or an SDK-generated ns=0 number): translate of a child path returned BadNoMatch and a
Read of a browsed child id was BadNodeIdUnknown or served from the TYPE (UnshelveTime 0
on a condition timed-shelved for a minute), bypassing the data-plane ACL.

OtOpcUaNodeManager.New now derives {conditionId}/{browsePath} in the condition's
namespace, so the ACL scope of a child is its condition's scope plus the path. The
ShelvingState methods adopt their original AlarmConditionType_ShelvingState_* id as
MethodDeclarationId so clients calling them by that id still resolve. Condition removal
and rebuild forget the whole PredefinedNodes subtree so a removed condition's children
stop answering.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:45:46 -04:00
Joseph Doherty e6028678b3 docs(mtconnect): upgrade note for existing multi-Device MTConnect drivers (#628)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:44:44 -04:00
Joseph Doherty 5c6e2d1b59 Merge b626-631 (batch #626+)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:43:45 -04:00
Joseph Doherty 723392e3da Merge b626-mtconnect (batch #626+)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:43:45 -04:00
Joseph Doherty 7a9b51aeca Merge b626-ablegacy-focas (batch #626+)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:43:44 -04:00
Joseph Doherty 2377da3cb4 Merge b626-630 (batch #626+): release prior device/session state on re-Initialize in six drivers
Conflict in S7Driver.ReleaseConnectionAsync resolved: kept the gated Plc swap from #630 and
carried the negotiated-PDU reset from #632 #294 into it.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:43:44 -04:00