mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-18 22:55:08 +08:00
fix/encoder-stack-depth-json-flake
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e8bec27d5d |
Complete WoT binding preservation and two-pump aggregation (#4431)
# Description
Complete WoT Binding preservation and the two-pump aggregation workflow
without implicit overlays or multi-source action fan-out.
- Add verified linked-document export and bounded partition merging.
Preserve complete source facts, reject contradictory archival content,
and retain usable readable content when a future native grammar is
unsupported.
- Wire local Methods and Condition events through the existing
injectable binding/runtime infrastructure. Keep local and upstream
identities distinct, translate namespace-bearing values, and bound
occurrence routes by source and generation.
- Wait for asynchronous event-producer readiness before completing
subscription creation, so the first alarm is not lost. Preserve
cancellation, teardown, and startup-failure reporting.
- Import runtime types in inheritance order and attach typed Method
argument Properties. Keep generated Double spelling deterministic across
supported frameworks and remove an AOT-unsafe registry validation
conversion.
- Generate both pumps and their asset/group projections. Expose fifteen
typed readings per pump, separate source-owned controls, and
acknowledge/confirm/reset workflows. Extend the demo script for managed
and NativeAOT execution, bounded failures, and exact-process cleanup.
- Synchronize current-state documentation and specification fixtures.
Compose fixture paths from platform-independent components and emit/pin
LF pump XML so byte-exact regeneration holds on Windows, Linux, and
macOS.
- Keep sample JSON readable with sorted members, two-space indentation,
and LF line endings. Normalize generated filenames to single hyphens
without changing registry resource IDs, document values, logical links,
or dependency ordering; the manifest maps resource IDs to their physical
filenames.
Most changed files are deterministic regenerations of checked-in sample
documents. The demo manifest contains 533 linked resources; the
aggregation server remains generic and has no Pump-specific compiled
model dependency.
No new alarm vocabulary, shelving/suppression/dialog/ConditionRefresh
transport mapping, Azure SDK changes, or complete Pump/OpenUSD parity is
introduced.
## Related Issues
Fixes #4430, which records the approved implementation scope, design
decisions, and acceptance criteria.
Follow-up to #4411. Specification fixtures are pinned to
[OPCF-Members/spec-drafts commit
82620d9](https://github.com/OPCF-Members/spec-drafts/commit/82620d9e313f35386dfc7477f7875cd47d2fd268),
associated with OPCF-Members/spec-drafts#35.
## Validation
- .NET 10 and .NET 8: complete affected converter/schema, WoT
Connectivity, and binding suites passed.
- .NET Framework 4.8: affected libraries, converter/generator, projected
runtime, readiness, and NodeSet-import compatibility runs passed. The
existing filesystem test exceeding the host's legacy path limit passed
through a temporary short-path alias, without changing its assertions.
- All 29 aggregation sample tests passed, including the independent
companion-model oracle.
- Managed/default-port and NativeAOT/alternate-port demo runs completed
with 533 documents, two pumps, thirty Good readings, and all four
pump/source control and alarm workflows.
- Timeout and duplicate-port failure paths were exercised; owned
processes and ports were released.
- Specification artifact/rule checks, byte-identical example mirroring,
statement-digest verification, and changed-file whitespace checks
passed.
- The [Build and Test .NET 10.0 workflow for
|
||
|
|
90fc3cca2f |
Complete WoT Binding 1.1 alignment and conformance coverage (#4411)
# Description Completes the UA-.NETStandard implementation of the final OPC UA WoT Binding 1.1 semantics and aligns the WoT Connectivity 1.1 model with xRegistry 0.4.0. The change: - retires `uav:isEvent` and `uav:severity` from current Binding output while preserving unknown members through the generic converter's ordinary residue mechanism; - removes `uav:severity` from the asset Thing Description parser and materializer, so document metadata cannot set occurrence severity; - implements logical EventType definitions, ordered resolution providers, explicit/default selection precedence, materialized member paths, and the built-in `BaseEventType` fallback; - adds deterministic portable identity and browse-path anchoring; - populates bound type declarations instead of creating duplicate siblings, including inherited and closed-content validation; - resolves and validates `uav:externalSchema` without overriding authoritative DataType definitions; - hardens DataType dimensions, native projections, preservation envelopes, URI transmission, projection limits, and delete-policy handling; - synchronizes the xRegistry and WoT Connectivity source-generation inputs; - pins all 27 specification examples, all 97 implementation-owned normative requirements, and all 12 cross-language vector groups to the final specification revision; - adds `samples/WotCon/run-aggregation-demo.ps1` for a verified one-command aggregation client/server round trip and replaces the README topology sketch with Mermaid; - normalizes redirected PowerShell diagnostic presentation in the statement-digest test harness so its semantic assertions are stable on Windows, Linux, and macOS. WoT Connectivity remains model version `1.1`; draft iterations are identified by release labels such as `1.1-draft5`. **Specification dependency:** the evidence assets pin `OPCF-Members/spec-drafts` `main` commit `25db507121b503fd31c8816fbfae67a6822e813b`. The corresponding specification follow-up is `OPCF-Members/spec-drafts#35`. ## Validation - Latest GitHub/Azure CI: 266/266 checks passed. - .NET 10 feedback-focused tests: - WoT Connectivity event/model/traceability: 72 passed - binding capability/traceability: 8 passed - coverage-gate tests: 25 passed - Complete .NET 10 Tools project: 752 passed, 1 intentionally skipped. - .NET Framework 4.8: - WoT Connectivity event/model/traceability: 72 passed - WotCon bindings build: 0 warnings, 0 errors - NativeAOT build: 0 warnings, 0 errors. - WoT aggregation demo: 16 documents uploaded and all 10 mapped values read with Good status; exact processes and isolated PKI stores cleaned up. - Specification traceability: 402 normative statements mapped; all 97 implementation-owned statements have passing test evidence with zero gaps. - Shared conformance vectors: 12/12 groups execute. - Final CI coverage: - project line coverage: 86.83% (floor 70%) - project branch coverage: 76.71% (floor 60%) - patch coverage: 100.00% (2,591/2,591 changed lines) - coverage gate passed ## Related Issues Follow-up to #4375 and alignment with `OPCF-Members/spec-drafts#31` and `OPCF-Members/spec-drafts#35`. No separate tracking issue was opened for this follow-up. ## Checklist - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce new build or analyzer warnings in the changed paths. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [x] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed all PR feedback currently received. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 6a9f41b1-72f0-4c74-bd7f-26fa531e5738 |
||
|
|
a7c776e8d4 |
Align the WoT implementation with spec: type binding, removed vocabulary, Alarms and Conditions (#4225)
# Description Aligns the WoT implementation with the normative changes in the OPC UA WoT specification drafts that the stack had not picked up, and fixes the defects that alignment surfaced. The driving artifact is [OPCF-Members/spec-drafts#19](https://github.com/OPCF-Members/spec-drafts/pull/19), *"bind a projected node to an already-loaded type, by name or by NodeId"*, which landed on the spec `main` after this repository last tracked it. ## 1. Bind a projected node to an existing type (Binding §5.2.1) §5.2.1 lets a document state that the node it projects is an instance of a type that **already exists** — one a companion model defines and the Server has loaded, or one a sibling document of the same conversion defines — so a converter binds to that type instead of defining a second type of the same shape. The converter hardcoded `HasTypeDefinition` to `BaseObjectType`, so a document that named its type projected an **untyped** node: a client browsing for the companion type would not find it, and nothing said why. Both forms are now implemented, resolved against the §5.1.5 **local context** — the other documents being converted together first, a loaded AddressSpace as the fallback. `IWotNodeResolver` supplies that context (is a namespace held; what does a qualified BrowseName match; what does an ExpandedNodeId identify), `WotCompositeNodeResolver` composes implementations in the specified order, and `NullWotNodeResolver` is the default that holds nothing. The converter needed no new async surface: the binding is resolved in a pre-resolve pass, exactly as Thing references already are, and the synchronous core consumes the result. The §5.2.1 table is honoured: a unique name binds; the link settles an ambiguous name; a name that resolves to nothing while the link resolves is **invalid** (that is a mistake in the name, not a shorthand for the identifier); two forms resolving to different Nodes are invalid; a resolved type of the wrong NodeClass is invalid. A binding naming a type the local context does not hold **fails** rather than falling back to `BaseObjectType` — a silently mistyped node is worse than a reported failure. A compact name is told from ordinary `@type` annotation **by namespace**, not by whether the lookup succeeds, so `saref:TemperatureSensor` on a Server that has never heard of SAREF stays the annotation it always was. The definitive form — a `ua:HasTypeDefinition` link whose `href` is the ExpandedNodeId of the type. An ExpandedNodeId matches exactly one Node or none, so it needs no lookup. A Node has exactly one `HasTypeDefinition`, so: - two such links → `AmbiguousTypeBinding` (6019), and the root keeps the default rather than being bound to a guess; - a link with no usable identifier → `InvalidTypeBinding` (6020); - no binding at all → the `BaseObjectType` default is unchanged. ## 2. Drop the six vocabulary terms `#19` removed `#19` deleted `uav:capability`, `uav:componentModel`, `uav:reference`, `uav:congruentType`, `uav:congruentTypeName` and `uav:nameNamespace` from the context, the JSON Schema and the ontology. A link now names its ReferenceType directly in `rel`, so the three link relations become `ua:HasInterface`, `ua:HasComponent` and `ua:NonHierarchicalReferences`, and the congruent-type pair is superseded by the §5.2.1 type binding. Their validation and mapping are removed; they are now ordinary residue like any other unrecognised term. ### A real gap this uncovered The converter knew **eight** ReferenceTypes by name and had neither `NonHierarchicalReferences` (`i=32`) nor `HasInterface` (`i=17603`) — so the two relations that *replace* `uav:reference` and `uav:capability` could not resolve at all. That includes example 02, which this repository vendors as a golden asset and which `#19` rewrote to use them. Both are now in the map. `uav:congruentType` was also the only term that redirected one reference to another while resolving a link target, so `ResolveTargetNodeIdAsync` no longer needs a loop. It resolves once and reports an unresolved target; the resolution context is still entered and the bytes still counted, so the per-conversion document, depth and byte bounds are unchanged. ## 3. Reject an out-of-range event severity instead of clamping it `uav:severity` had grown in this implementation without being a specification term. Rather than drop it, it is proposed upstream in [OPCF-Members/spec-drafts#21](https://github.com/OPCF-Members/spec-drafts/pull/21), which defines it with an explicit rule: the value **shall** be in the OPC 10000-5 range 1..1000, and a consumer **shall not** silently clamp it. The implementation clamped, so a document asking for severity 5000 was accepted and published as 1000 — the mistake was hidden while what the author asked for was changed. An affordance carrying an out-of-range severity is now skipped, with the reason logged, and its half-built event type and `GeneratesEvent` reference are removed. ### Defect found while making that change The occurrence-time path read `ValidateSeverity(severity) ?? tag.Severity`. Because `ValidateSeverity(null)` returns the 500 default, it **never** fell back to the affordance's authored severity: an occurrence reported without a severity published 500 rather than the authored 900. Build-time validation and the occurrence-time choice are now separate methods. ## 4. Vendored examples and honest coverage claims Examples 21 and 22 were missing from the vendored golden assets. Converting example 22 is now the end-to-end check for §5.2.1 — the specification's own example rather than a fixture written to match the implementation. `docs/WotBindings.md` claimed ten of the eleven conformance units and silently omitted **WoT-ConditionMapping**, and said "twenty worked examples". The count is corrected, and WoT-ConditionMapping is now **covered** — see §5 below. ## 5. Alarms and Conditions (Binding §13) The last unclaimed conformance unit. A projected Condition event now derives from the ConditionType it names rather than from `BaseEventType`; falling back would lose the Condition state model entirely, leaving a Client unable to tell an alarm from an ordinary event. The two forms follow the hint-plus-pin pattern of §5.3: `uav:conditionTypeId` is definitive and wins, `uav:conditionType` is a readable hint resolved for the four ConditionTypes §13.1 scopes. An unpinned name outside that set is reported rather than guessed. Four conformance rules are enforced, each because breaking it yields a document a consumer can read but cannot act on: | Rule | Section | Diagnostic | | --- | --- | --- | | A Condition event declares `EventId` in its `data` | 13.3 | `ConditionEventIdMissing` | | `uav:conditionAction` is inside its closed set | 13.2 | `InvalidConditionAction` | | `uav:actsOn` names a Condition event in the same document | 13.4 | `InvalidConditionTarget` | | `Acknowledge`/`Confirm`/`AddComment` declare an `EventId` input | 13.4 | `ConditionActionInputMissing` | `Enable` and `Disable` act on the Condition instance rather than one occurrence and are deliberately exempt from the last rule, pinned by a negative test. Shelving, suppression, dialog conditions and `ConditionRefresh` stay out of scope, as §13.1 scopes the mapping. ## 6. The sibling half of the §5.1.5 local context `SnapshotWotNodeResolver` indexes the registry snapshot a conversion runs over and is wired into `WotNodeSetDocumentConverter`, so a document naming a type another document in the same registry projects resolves with no AddressSpace at all. Only Thing Models are indexed: a Thing Model projects a `UAObjectType` and is a valid target, a Thing Description projects an instance and never is. Identity comes from the new public `WotNodeSetConverter.TryDescribeProjectedType`, which applies exactly the rules the conversion uses, so an index entry and the projected node cannot drift apart. Ambiguity is preserved rather than resolved: two siblings sharing a qualified name yield both matches so the caller can report it. ## Deliberately not in this PR - **The AddressSpace-backed `IWotNodeResolver`.** The abstraction, the composite, the null default and the sibling-document implementation ship here; the AddressSpace-backed half needs a cached type-hierarchy walk behind a server `OperationContext` and follows separately. Until it exists a host gets the sibling context plus the null fallback, which reports a binding as unresolved rather than mistyping it. - **Two §5.2.1 obligations that need the address space**: not duplicating a mandatory instance declaration of the resolved type, and rejecting disagreement with an instantiated Thing Model. - **Connectivity §7.3 parent resolution.** Each is tracked; none is faked here. ## Related Issues There is no tracking issue: this came out of a spec-versus-implementation audit rather than a reported defect, and the driving artifacts are the two upstream spec PRs linked above. Happy to open one if maintainers prefer — the §5.2.1 follow-up in particular is worth an ADR, since the local-context resolver is a new injectable provider spanning two assemblies. ## Checklist - [x] I have signed the [CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf) and read the [CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md) doc. - [x] I have added tests that prove my fix is effective or that my feature works and increased code coverage. - [x] I have added all necessary documentation. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - [x] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [ ] I have addressed **all** PR feedback received. ### Verification detail | | net10.0 | net48 | |---|---|---| | `Opc.Ua.Types.Tests` | 8573 / 8573 | 8555 / 8555 | | `Opc.Ua.WotCon.Tests` | 1075 / 1075 | 1075 / 1075 | Full `UA.slnx` build: **0 errors, 0 warnings** across all target frameworks, re-verified after `master` was merged into the branch. Every new test was mutation-verified: the type-binding tests against three mutations (revert to the hardcoded default, drop the ambiguity check, accept an empty `href`), the severity tests against reinstating the clamp, and the ReferenceType additions against removing them from the map. Each mutation failed the suite before the change was kept. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8 |
||
|
|
e73e71184d |
[WotCon] Add OPC UA WoT Connectivity 1.1 with the Types, source generation, Server and xRegistry work it builds on (#4128)
## Summary Adds OPC UA WoT Connectivity 1.1 to the stack, together with the Types, source generation, Server and xRegistry work it is built on. This branch is the merge point for the fourteen-PR stack that replaced integration PR #4093; every constituent PR was reviewed and approved separately and is listed below. 414 files changed, +108,936 / -3,199. Roughly 46k added lines of product code, 57k of tests, plus samples, tools and documentation. ## Types - Complete `ExtensionObject` raw-body decoding for binary, XML and JSON bodies by resolving the concrete type through the message context's encodeable factory, and simplify the `Variant` structure helpers by delegating to it (#4128). - Add `IAtomicFileReplace` as an optional `IFileSystem` capability so existing external implementations keep working, with atomic publish for `LocalFileSystem` via `File.Replace`/`File.Move` and for `VirtualFileSystem` by re-keying the in-memory entry (#4128). - Add lossless conversion between WoT documents and NodeSet2 (`Opc.Ua.Wot.WotNodeSetConverter`). A byte-exact `uav:nodeSet` envelope is preserved when requested, the structured `uav:nodes` projection is used when the readable vocabulary is incomplete, and NodeSet2 is otherwise synthesized from readable WoT terms. Unmapped JSON members survive a round trip as pointer-addressed residue (#4131). ## Source generation - Improve the NodeSet to ModelDesign conversion and the generated node state (#4132). - Generate OPC UA models directly from WoT Thing Description files (#4134). ## Server - Add live NodeManager shadow and immediate reload. A reload materializes a new generation beside the active one and switches atomically; the superseded generation is retired gracefully so existing MonitoredItems keep being served until they drain, or immediately when the caller asks for it. Includes the request admission and drain machinery that lets an orderly shutdown wait for admitted requests instead of tearing down underneath them (#4147). - Make subscription transfer between sessions transactional, so a failed transfer leaves neither session holding a partially moved subscription (#4150). ## xRegistry - Allow clients to use explicit registry roots rather than assuming a well-known location (#4130). ## WoT Connectivity - Add the WoT Connectivity 1.1 information model: a registry-first revision layered on the abstract xRegistry base model, incorporating the complete published OPC 10100-1 v1.02 surface as deprecated nodes in the same namespace so existing 1.02 clients keep working (#4139). - Add the protocol binding abstractions and planners that compile WoT forms into executable plans (#4140). - Add the HTTP, Modbus, OPC UA and MQTT binding executors (#4144). - Add the registry and its stores, including document versioning, validation, dependency resolution and refresh (#4146). - Add the registry client (#4142). - Materialize WoT Thing Descriptions into the server address space, deriving types from Thing Models and instances from Thing Descriptions (#4154). - Restore the WoT asset registry bridge so the deprecated 1.02 asset surface is backed by the registry (#4171). - Add the WoT Connectivity samples and complete the documentation (#4156), and restore the remaining coverage and docs (#4172). ## New projects `src/Opc.Ua.WotCon.Bindings`, `src/Opc.Ua.WotCon.Bindings.Mqtt`, `tests/Opc.Ua.WotCon.Bindings.Tests`, `tests/Opc.Ua.WotCon.Samples.Tests`, and the `samples/WotCon` AggregationServer, AggregationClient and FlatTagServer samples. ## Constituent pull requests Merged into this branch top-down, each independently approved: | PR | Title | | --- | --- | | #4128 | [Types] Complete ExtensionObject decoding and add an atomic file replace capability | | #4131 | [Types] Add lossless conversion between WoT documents and NodeSet2 | | #4132 | [SourceGeneration] Improve NodeSet to ModelDesign conversion and generated node state | | #4134 | [SourceGeneration] Generate OPC UA models from WoT Thing Description files | | #4147 | [Server] Add live NodeManager shadow and immediate reload | | #4150 | [Server] Make subscription transfer between sessions transactional | | #4130 | [XRegistry] Allow clients to use explicit registry roots | | #4139 | [WotCon] Add the WoT Connectivity 1.1 information model | | #4140 | [WotCon] Add the WoT protocol binding abstractions and planners | | #4144 | [WotCon] Add the HTTP, Modbus, OPC UA and MQTT binding executors | | #4146 | [WotCon] Add the WoT Connectivity registry and its stores | | #4142 | [WotCon] Add the WoT Connectivity registry client | | #4154 | [WotCon] Materialize WoT Thing Descriptions into the server address space | | #4156 | [WotCon] Add the WoT Connectivity samples and complete the documentation | | #4171 | Restore WoT asset registry bridge | | #4172 | Restore WoT coverage and docs | Supersedes #4093. ## Follow-up The WoT Connectivity and WoT Binding drafts have moved to 1.1-draft2 since this work was authored. Aligning with that revision - the removed group vocabulary, the new projection/View construct, the remaining model vocabulary terms and the two new portable-identity validation rules - is tracked separately and will follow in its own pull request. ## Validation Built for every target framework with zero warnings and zero errors, and validated at the tip of the stack with `Opc.Ua.WotCon.Tests` at 999 passed / 0 failed and `Opc.Ua.Server.Tests` at 4024 passed / 0 failed. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8 |