mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 07:07:24 +08:00
copilot/fix-github-actions-job-validation
86
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
397db603db |
Add OPC UA for Asset Administration Shell: the published OPC 30270 and v3 draft (#4244)
# Description Implements the **Asset Administration Shell** companion specifications on top of `marcschier/wot-type-binding`, in two halves that a Server chooses between: * **AAS V3** — the [draft](https://github.com/marcschier/opcua-drafts/tree/main/companion-specs/AAS) (`http://opcfoundation.org/UA/I4AAS/v3/`), all sixteen conformance units: the metamodel, the registry built on the abstract xRegistry base, generational materialization, federation, packages with digest-bound integrity, the Digital Product Passport vocabulary, and the Annex F bridge to WoT Thing Descriptions. * **AAS V2** — the published [OPC 30270](https://reference.opcfoundation.org/specs/OPC-30270/full) (`http://opcfoundation.org/UA/I4AAS/`), which maps the AAS V2.0.1 metamodel: object model, ingestion of XML, JSON and AASX, materialization, server and client. The two are different metamodel generations rather than two revisions of one model — V2 keeps `Asset` as a first-class Identifiable, `identification.idType`, Views and ordered collections, none of which survive into V3 — so they are **alternatives**. A Server hosts one or the other; this is documented rather than enforced at startup. Each is named for the generation it maps (`Opc.Ua.Aas.V2`, `Opc.Ua.Aas.V3`, `AddAasV2Server`, `AddAasV3Server`), and what genuinely serves both carries no marker and sits at the root of `Opc.Ua.Aas`: the deterministic NodeId encoding and idShortPath conventions, `AasOptional<T>`, the digest rule, the materialization diagnostics, the AASX container walk, and the value and operation providers, which are keyed on `NodeId` and `Variant` and so carry no version at all. ## What is in here **New libraries**: `Opc.Ua.Aas`, `Opc.Ua.Aas.Server`, `Opc.Ua.Aas.Client`, `Opc.Ua.Aas.WoT`, plus `samples/Aas` and `docs/Aas.md`. **Enabling changes to existing code**, each needed by the above: * The OPC UA `Decimal` DataType added to `Opc.Ua.Types`, with its Part 6 §5.1.10 wire encoding. `BinaryDecoder` gained one internal member, `TryReadRemainingBodyBytes`, because a trailing field with no self-describing length cannot otherwise be read; `Decimal.Decode` is its only caller. Part 6 Table 3 types the unscaled `Value` as an **OctetString whose byte count is `Length` − 2**, not a length-prefixed `ByteString`, so it carries no length of its own — `DecimalEncodingTests` pins the body byte-for-byte against that mistake, and against the `Length ≤ 2` values the same table calls invalid. * The xRegistry projection engine extracted into `Opc.Ua.XRegistry.Server` and `Opc.Ua.WotCon.Server` migrated onto it (−848/+387 lines), with the WoT Connectivity tests passing **unedited**. * Two source-generator fixes, both of which made a model impossible to build (see below). ## Defects found and fixed along the way Running the stack end to end, rather than trusting per-area test suites, surfaced defects that no unit test caught. The ones worth a maintainer's attention: * **The generator emitted non-terminating constructors.** A structure field with no declared default was initialized with `new T()`, so a self-referential DataType (`AASReferenceDataType.referredSemanticId`) recursed until the stack overflowed and `AddOpcUaAas` could not load the model at all. The test host *died* rather than failing. Only the field that closes a cycle is now left null. * **The generator omitted `new` on a redeclared placeholder.** `AASOrderedSubmodelElementCollectionType` redeclares the `SubmodelElement` slot of its base type, and the generated `Add` method hid the base one without saying so, so the V2 model would not compile. No existing model could have hit either defect — both are build failures, so anything that builds today is unaffected. * **The metamodel half had never worked outside its unit tests**: the emitted NodeSet declared no reference-type aliases and was rejected by the loader; the Mandatory `Invoke` Method was never emitted, because a NodeSet is imported as written rather than instantiated from its type; the runtime bound at the authored namespace index instead of the one the Server assigns; and `MethodDeclarationId` was addressed in the wrong namespace. Every materializer test inspected the in-memory object graph, which cannot see whether the graph is loadable — two tests now import it exactly as `RuntimeNodeSetNodeManager` does. * **The client could not see any Property member.** It browsed `HasComponent` with subtypes, but `HasProperty` is a sibling under `Aggregates`, so reading a value failed and invoking failed argument validation. * **Every repeated registry write forked a duplicate group and resource** instead of adding a version, because the identifier was re-derived against a sibling set that already contained the entity itself. This also made the persistence bounds unreachable. * `.gitignore`'s `**/packages/*` silently swallowed the whole package-integrity implementation and its tests, which would have vanished on a fresh clone. ## Specification findings Nine findings against the V3 draft are fixed in [marcschier/opcua-drafts#75](https://github.com/marcschier/opcua-drafts/pull/75). The substantive one: clause 6.1.5's absent-versus-empty rule was **unsatisfiable** for five container fields that materialize as parent components with no node of their own, which made `AAS-LosslessRoundTrip` unachievable. OPC 30270 contradicts itself twice. Both are recorded in `AasV2ConformanceUnits.cs` and the documentation rather than smoothed over: Table 83 spells the unit `I4AAS MultiLangaugeProperty` where Table 85 spells it correctly, and Table 85 lists an `I4AAS Security` unit that Table 83 never defines. Table 84, which would assign profile URIs, is empty, so `ServerProfileArray` gains no I4AAS entry. ## Known limitation Calling an `Operation` **with arguments** answers `BadTooManyArguments`. Method resolution works from both the instance NodeId and the `MethodDeclarationId`, and the argument definitions are published, but `NodeState.AddChild` does not route through `MethodState.FindChild`, so a Method imported from a NodeSet leaves its typed `InputArguments` null. Closing this means changing how the shared NodeSet import claims typed children, which affects every consumer of runtime NodeSets and is out of scope here. It is documented in `docs/Aas.md` §11, the conformance matrix marks `AAS-OperationInvoke` partial rather than claiming it, and `samples/Aas` exercises the path and reports the status rather than working around it. V2 is unaffected — its `Operation` is an OptionalPlaceholder with no declared arguments. ## Related Issues - No tracking issue yet. Opening as a **draft** for design discussion first, per the ADR guidance below; happy to open one and link it once the shape is agreed. ## 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 | Check | Result | |---|---| | AAS tests | **670 passed, 0 failed** on net472, net48, net8.0, net9.0, net10.0 | | Coverage | Client 80.8 %, Server 81.2 %, `Opc.Ua.Aas` 82.0 %, WoT 82.4 % — all above the 80 % gate | | Libraries | `Opc.Ua.Aas`, `.Server`, `.Client`, `.WoT` build clean on every target framework, zero warnings | | Regression | `Opc.Ua.Types` 8677, `Opc.Ua.WotCon` 1094, `Opc.Ua.XRegistry` 198, `Opc.Ua.Server` 4050 — all green | | Sample | `samples/Aas` builds warning-free and runs end to end | | Merge validation | Solution builds clean on net10.0 and net48; WoT Types 504, WotCon 1111, AAS 670, XRegistry 198 tests passed | | CI | All 99 checks green on `ff9298e44`; only the CLA signature status is outstanding | ### Review pass A full review against both specifications, and for API consistency and correctness, produced twelve fixes. Each one has a test that fails without it. | Defect | Where | Why it mattered | |---|---|---| | V2 projection aborted on **every** `File` read from a document | `AasV2EnvironmentRuntime` | The runtime bound `File/Value` unconditionally, the materializer emitted it only with content, and the builder throws `BadNotFound` for an unknown NodeId — aborting the whole registration. No reader ever populates `AasFile.File`, so no AAS V2 document could be hosted at all. | | `aas-suppl` anchored on the wrong part | `AasxPackageReaderCore`, `AasxPackageWriter` | IDTA and the Package Explorer anchor it on the environment part; we used the origin part. Every real `.aasx` returned zero supplementary files, silently. Affects both halves. | | Federation failed closed on pooled connections | `AasFederation` | `ConnectCallback` fires only for new connections, so the second fetch to an origin — and all of net472/net48/netstandard2.1 — failed with a security-flavoured error. The peer is now validated *inside* the callback, which makes reuse safe by construction. | | Malformed `Decimal` escaped as `ArgumentOutOfRangeException` | `BinaryDecoder` | A declared body shorter than `Scale` produced a negative read length. Two of the three test cases had been passing for the wrong reason. | | V2 and V3 cross-wired their value providers | `OpcUaAasServerBuilderExtensions` | Both registered `IAasValueProvider`; whichever was added first served both AddressSpaces. V2 now has its own contract, so co-hosting is harmless rather than silently wrong. `EnvironmentFolder` was also bound and ignored on the V2 path. | | Export filter skipped three nesting kinds | `AasEnvironmentExporter` | Content under `Entity`, `AnnotatedRelationshipElement` and `Operation` was exported unevaluated *and* stamped with a Digest presenting it as complete (clause 6.5.10). List authorization paths also shifted after a denial. | | Concealment held at the Method but not in the projection | `AasRegistryModel` | `GetSubmodel` answered `BadNotFound` while an anonymous Browse revealed the node, its identifier, semanticId and digest (clause 6.5.7). | | File handles were unsynchronized and unowned | `AasElementFileManager` | The async node manager holds no lock, so concurrent Calls raced a bare `Dictionary`; sequential handles let any Session read, seek or commit another's. Writes were also unbounded. | | Projection host was unimplementable outside the stack | `AasEnvironmentProjection` | The handle demanded a type with an internal constructor — our own tests used `FormatterServices.GetUninitializedObject` to get round it. Now mirrors WotCon's interface-typed registration. | | Embedded `FileType` omitted 7 of 10 Mandatory members | `AasEnvironmentMaterializer` (V2) | OPC 10000-3 6.4.4 requires them all; without `Size` and `OpenCount` a Client cannot size a read or detect an abandoned handle. | | Round-trip oracle ignored most of the model | `AasRoundTripComparer` | `Blob`, `File`, `ReferenceElement` and `BasicEventElement` had no case arm, so their fields compared equal by falling off the switch; `valueType`, `semanticId`, `entityType` and `Key.type` were never read. The clause 6.4 losslessness claim rested on it. | Known, unchanged: calling an `Operation` with arguments answers `BadTooManyArguments`, because `NodeState.AddChild` does not route through `MethodState.FindChild`. Documented in `docs/Aas.md` §11. Not addressed in this pass, and worth a maintainer decision: OPC 30270 §9.2 says all NodeIds of Nodes not defined in the companion specification **shall not** use the standard namespaces, and Table 87 marks a vendor-specific instance namespace Mandatory. Both halves currently mint instances in the companion namespace. Separating them touches NodeId minting, the runtime and the client together, so it is better done deliberately than folded into a review pass. This is a large change and touches shared code (`Opc.Ua.Types`, the source generator, the xRegistry projection). It is deliberately a draft: the enabling changes are separable if maintainers would prefer them split into their own PRs. The latest `master` is merged into this branch in `0dd0ff0e1`. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8 Copilot-Session: c3d88950-4a65-4ff6-87d2-1f545a875c9b Copilot-Session: b8ab3763-b9bd-46ae-b0ef-045db26e5782 |
||
|
|
2b44fcad22 |
Robot intent: a task-level command API with a Part 10 lifecycle (#4165)
# Description Implement the draft **OPC UA — Robot Intent** companion model end to end: task-level motion verbs, a Part 10 lifecycle, a server hosting surface, a client, two samples and a live integration suite. It replaces the opt-in convention API merged in #4127 with an implementation generated from a companion information model. ## Why OPC 40010-1 *Robotics* describes robot **topology** in detail and defines **no motion verbs at all** — its entire actuation surface is `Start`, `Stop` and loading a named program. A conformant client can discover everything about a robot's construction and cannot ask it to move anywhere. #4127 filled that gap with ten verbs in an application-owned namespace, resolved by BrowseName, and the file said what it was: *"opt-in, explicitly non-normative industrial operation conventions"*. That contribution established a **vocabulary**. It established no **lifecycle**, and the lifecycle is the harder half. A motion takes seconds; a pick takes a minute. OPC 10000-4 §5.12.2 discards a method result when the Session ends *"independent of the task actually performed at the Server"* — so a synchronous motion method does not merely time out, it **loses the outcome of work that has already physically happened**. OPC 10000-10 §4.1 gives the OPC Foundation's own resolution: a Method performs a calculation, a **Program** runs a batch process or a machine tool part program. So `SubmitIntent` returns as soon as the intent is *admitted*, and what it returns is a NodeId — an `IntentOperationState` (a Part 10 program instance) the client subscribes to for progress and reads for the result. ## What changed **Model and shared code** — `Opc.Ua.RobotIntent.NodeSet2.xml` is source-generated in `Opc.Ua.Robotics`, so the enums, the polymorphic intent structures, `IntentOperationState : ProgramStateMachineState`, the method states and the typed clients derive from the model rather than being hand-written. Its only `RequiredModel` is the base UA namespace, so a server can adopt it without OPC 40010 or DI. `PoseMath` implements the specification's Annex C conversion between its unit quaternion and the core `ThreeDFrame` — including the `asin` clamp that stops a pole orientation becoming a domain error, and the non-negative-`w` representative that makes two servers agree on four numbers. `FrameTree` composes transforms along the frame tree. **Verbs are a DataType hierarchy, not one Method each.** A single submission and a mission step are then the same shape, and a new intent is a subtype rather than a new method — which is what `AddOperation<TRequest,TResponse>` was working around. **Server** — `IntentControllerHost` owns admission in the specification's order, the queue with PLCopen buffer modes, blending, cancellation *with the server's right to refuse*, missions with a committed base and a revisable horizon plus an IEC 61131-3 step graph, safety-aware refusals, real-time channel brokerage, and the capability declaration. All thirteen Methods are wired; command authority and channel leases are released when the holding Session closes; every client-supplied NodeId is validated against the controller being commanded (clause 11.3). `RobotIntentNodeManager`, the fluent `IIntentControllerBuilder` and `AddRobotIntent`/`ConfigureRobotIntent` make it reachable, and `RobotIntentFacetCalculator` computes which of the specification's facets an instance actually satisfies. **Client** — `RobotIntentClient` discovers `Server/RobotIntent/Controllers` and reads what a robot accepts; `IntentOperationHandle` subscribes and completes on a terminal state, recovering the result after a reconnect because clause 6.7 promises it survives; `CommandAuthorityLease` and `RealTimeChannelLease` manage the two exclusive resources; fluent builders cover every intent and mission. **Samples** — all robotics samples move under `samples/Robotics/`. `IntentEnabledRobot` is one UR5e-style offset-wrist arm on a bench, declared completely enough to show what a conformant server publishes, with a simulated safety source so the clause 10.4 refusals are demonstrable. `IntentViewerClient` turns a **click on a target prim in the OpenUSD viewport** into an intent and watches the arm execute it; it also runs headless, which is how CI exercises it. **Intents execute serially**, which satisfies every `BlockingMode` constraint by construction: the specification forbids beginning a `Single` or `Hard` intent while another executes and merely *permits* `None` and `Soft` to overlap. The convention API (`RoboticsOperationConventions`, `RoboticsOperationsBuilders`, `RoboticsOperationsClient`) is removed. ### On safety — what this deliberately does *not* claim The interface is **non-safety-rated**, and that is a property of the technology rather than a scoping choice. OPC 10000-15 carries cyclic safety data from a SafetyProvider to a SafetyConsumer; the consumer's `RequestSPDU` holds an identifier, a monitoring number and one octet of explicitly **non-safety** flags, so a caller has no channel through which to supply safety-rated arguments. Every safety fieldbus (PROFIsafe, CIP Safety, FSoE, openSAFETY) expresses a safety command as a **continuously asserted cyclic signal**, because the integrity argument rests on the fail-safe state that follows when assertion *stops* — and a Method call has no defined behaviour when it stops being called. What the host does instead is **observe and refuse**: `UpdateSafetyState` is how the application reports what the safety system is enforcing, and admission then refuses on the same values a client can read, so a refusal is explainable from the address space rather than from Server-internal state. It may never *instruct* the safety system, and no Method here commands a safe motion function, changes an operational mode or clears a stop. ## Defects this found in the specification, fixed upstream Implementing a specification is the only way to find out whether it can be implemented. Seven defects went back to [marcschier/opcua-drafts#47](https://github.com/marcschier/opcua-drafts/pull/47) rather than being worked around here: - **Refusal was unobservable.** Clause 6.2 orders six distinct refusals and clause 5.8 calls the failure set "small and diagnosable on purpose" — but `SubmitIntent` returned only `IntentId` and `Operation`, so a client could see *that* something was refused and never *which*. It now returns `Accepted`, `Failure` and `Message`, and clause 6.2 states that a refusal returns `Good` rather than substituting a Bad `StatusCode`. - **The Part 10 promotion was illegal.** `IntentOperationType.ProgramDiagnostic` was declared as a Property of `PropertyType`; OPC 10000-10 declares it a Variable of `ProgramDiagnostic2Type` reached by `HasComponent`. The declaration added a *second* member beside the inherited one instead of promoting it — which is precisely why an earlier version of this branch had deleted it. Correcting the TypeDefinition made it generate cleanly with **no change to the curated Core**. - **Clause 9 said nothing about the Method surface.** A server could declare `MissionsSupported` true and omit `SubmitMission` entirely, because the mission and channel Methods are Optional. This implementation did exactly that until an integration test caught it. A fourth honesty rule and a table now fix which Methods each declaration implies. - **Conformance was not machine-readable, and two implementations disagreed.** Clause 12 defined conformance in terms of facets and gave a Server nowhere to publish which ones it satisfied, so every client had to re-derive Table 12.2 from the address space. This repository proved why that fails: the server''s `RobotIntentFacetCalculator` and the client''s `RobotIntentRules.DeriveFacets` each implemented clause 12.2 independently and **disagreed on essentially every row** — the client''s `Blending` was `BlendingSupported` alone where the server also requires all four blending buffer modes, its `QueuedIntents` was `MaxQueueDepth > 0` where the server also requires `Buffered`. A client and server from the same release answered "does this controller conform?" differently, and both were reading the specification correctly, because several rows are behavioural and no amount of browsing settles them. `IntentCapabilitiesType` now carries **`SupportedFacets`**, exactly as OPC 10000-5 does with `ServerCapabilitiesType.ServerProfileArray`, RI-Base requires it, and clause 12.2 separates structural requirements a Server shall meet before listing a facet from attested ones governed by clause 9. - `WaitIntentDataType.Signal` was unbounded where clause 11.3 has to check it; §11.3 gained a table fixing the expected type of every NodeId-valued member; §5.7.0 gave `Ready`/`ActiveIntent`/`ActiveMission` normative meaning; §6.9 bounded `RequestedLease`; §6.5 and §6.4 now say what a server that cannot differentiate `StopMode` must do, and which stop a superseded intent gets. ## Bugs the tests and reviews caught Each of these would have been silent in the field: - **The execution engine was never wired into the public API.** `IntentControllerBuilder.RegisterAsync` built the address space and never started an `IntentControllerHost`, so through the documented `AddRobotIntent` → `ConfigureRobotIntent` path **every Method returned `BadNotImplemented`** and the registered `IIntentExecutor` was never resolved. A browsable address space is not a working robot interface, and only a live integration test showed the difference. - **NodeId scoping failed open on an empty index.** A controller advertising `CallProgramIntentDataType` with no published `ProgramType` accepted an *arbitrary* NodeId as `CallProgram.Program` — and `CallProgram` runs code the server holds. - **Blending completed the predecessor at submission time** and abandoned its motion, telling the client it succeeded and publishing an all-zero pose at the origin as a genuine `AchievedPose`. - **Mission base immutability compared only identifiers**, so a client could keep the ids and swap the *motion* of an already-committed step. - **`FinalResultData/Result` was never registered** with the node manager: Browse showed it, Read returned `BadNodeIdUnknown` — so clause 6.7's promise to a Part 10 client did not hold. - `DisposeAsync` disposed primitives out from under in-flight executor code; re-using a terminated `IntentId` destroyed the retained result and duplicated a NodeId; neither side registered the Robot Intent encodeables, so `Pose3DDataType` did not decode; host startup was hooked to a task that ran in only one hosting shape. - The result was published **after** the state went terminal; capabilities were resolved against a stale namespace table; an empty `ContentFilter` arrives as an empty element array rather than a null filter, so testing only for null made every unconditional mission transition silently untaken — a mission would run its first step and stop. ### Found by a later test-quality audit, after the above was written An audit of the test suite itself found five tests whose **names** asserted a normative requirement while their **code** asserted something weaker or nothing at all. The worst **fabricated the answer it checked**: a helper caught a decode failure and invented "5 supported intents, 6 axes, all facets true", so six capability-honesty assertions across four tests could pass while reading nothing from the server. The fallback is gone and the suite passes with honest reads. Chasing the facet gaps it reported led to the clause 12 defect above. Two further defects followed from completing that work: - **`RI-Interop-40010` was never computed**, and the code documented the omission as impossible to fix "because the calculator receives only a Robot Intent controller, not the linked OPC 40010 model". That was wrong: Annex B's structural requirement is an **inverse** `HasIntentController` reference, reachable from `IntentControllerState` — and the integration suite was already browsing exactly that. A server implementing Annex B could not claim Annex B. - **The published claim could go stale.** `SupportedFacets` was a snapshot taken during `RegisterAsync`, so a reference attached afterwards left it wrong. It is now bound through `BindRead` and recomputed on read, so the claim tracks the address space by construction rather than by discipline — which matters for anyone bolting Robot Intent onto an existing OPC 40010 node manager, where the attach ordering is often not theirs to choose. Two tests that **could not fail** were also replaced: one asserted `AccessLevel` equals `CurrentRead` to pin a read-only marking, but `BaseVariableState` initialises every variable to exactly that, so deleting the marking left it green; it is now a write expecting `BadNotWritable`, including a real OPC UA `Write` service call over a live session. Finally, a build break that a plain build hides: `RoboticsAotTests` used `out string?` in a project declaring `<Nullable>disable</Nullable>`. Building the project **without** `-f` suppresses `CS8632` through `NoWarn=nullable` and succeeds; building it **with** `-f net10.0` resolves `NoWarn` differently, `CS8632` fires, and `TreatWarningsAsErrors` makes it an error. CI builds per-TFM, so this would have failed there while passing every local build to that point. ## Testing and coverage | | | |---|---| | `dotnet build UA.slnx -t:Rebuild` | **0 errors, 2 warnings** — both pre-existing `CA1861` in `tests/Opc.Ua.Tools.Tests`, a file this PR does not touch | | **Whole `UA.slnx` test suite, net10.0** | **50 of 52 assemblies fully green** (measured at `40d9cf213`, before the `master` merge; the other two are explained below) | | `Opc.Ua.Robotics.Tests` | **467** net10.0, **447** net48 | | `Opc.Ua.Robotics.Intent.Tests` (live client <-> server) | **20** on net10.0 and net48, run repeatedly | | `Opc.Ua.OpenUsd.Tests` | **904** after merging `master` (664 before; the rest arrive with #4160 / #4162 / #4180) | | `Opc.Ua.Aot.Tests` | **121/121** | | `Opc.Ua.Di.Tests` (regression from the sample move) | **364/365**, one pre-existing skip | | **Changed-line coverage** | **90.7 %**, no file below 80 % — comfortably above the enforced band, which after #4158 applies only to patches larger than 100 changed lines | The two non-green assemblies are **not** caused by this branch. Rather than assert that, I ran the same selections at the merge-base `ae241b662` on the same machine: - `ServiceCallReassemblerTests.PrivateRequestSummaryFormatsKnownRequestKinds` expects `12.5` and gets `12,5` — a culture-sensitive assertion failing on a European locale. Fails 4/4 on **both** commits. - `CertRotationLiveTests.L2Cert2RotateCertificateDuringServerRestartRecoversSharedChannelAsync` fails on a teardown count. Fails 4/4 on **both** commits. - A third failure seen once, AOT `FindServersAsync` with `BadSecureChannelClosed`, did not reproduce: 4/4 on retest and 121/121 in the final run. Environmental. Two things are worth calling out because they would otherwise be invisible: - `tools/**` is **not** in the `coverage-thresholds.json` ignore list, and no test project referenced the OpenUSD tools assemblies — so their changed lines counted as uncovered and the real patch figure was 75 %, exactly the blocking threshold. Rather than widening the ignore list, the pure decisions in the viewport host (pointer-to-physical-pixel conversion, `RenderPickRequest` construction, stale-retry, backend discovery, pick-mode selection) were separated from the Avalonia/native shell and tested headless against a fake picking backend. - `dotnet build UA.slnx` **hides warnings on unchanged projects**; only `-t:Rebuild` surfaces them. That is how a `CA2263` introduced by a net48 portability fix survived several "zero warning" builds. The multi-target requirement earned its keep repeatedly: it caught a default interface implementation, `ArgumentNullException.ThrowIfNull`, `string.Create` with an interpolated handler, `ValueTask.FromResult`/`CompletedTask`, `Task.WaitAsync`, generic `Enum.GetValues` and generic `Enum.IsDefined`, and a non-generic `TaskCompletionSource` — none of which the net10.0 build complained about. A net10.0-**only** hang in the simulated executor and two load-sensitive flaky tests were also found and fixed at the cause. ### Merged with `master` `master` (through `e73e71184`) is merged in as `08205d81d`. Four conflicts, all where the two branches touched the same robot and OpenUSD surfaces, and none resolved by dropping a side: - #4162 added six files to `samples/MinimalRobotServer/` while this branch moved that directory to `samples/Robotics/MinimalRobotServer/`. They are kept at the moved path, and `UA.slnx` takes the union of both sides' new projects. - #4160 replaced `object` with a `UsdValue` union and rewrote `Flatten`; this branch had reformatted the same method and fixed a path in its doc comment. Git produced a hybrid that did not compile. The resolution keeps `master`'s `UsdValue` arm and `Flatten(UsdValue)` overload in this branch's block-bodied form, with the path fix retained. - `master` inserted `FindStageCamera` directly before two methods this branch had relocated, so the merge duplicated `WriteStageUsda` and `GetPrivateStateRoot`. Only `FindStageCamera` is genuinely new — `master`'s diff shows the other two as unmodified context — so the duplicates are dropped and this branch's `internal` versions kept, including the `GetPrivateStateRoot(string?)` overload the connector tests use to stay out of the real user profile. - `master` moved the OpenUSD stack to `0.4.0-alpha`; this branch pins `OpenUsd.Rendering` in the test project so the renderer-pick tests can reach the picking backend. Left at `0.1.0-alpha` that pin is below what the viewer now resolves transitively, which is `CS1705`. The pin follows `master`. CI was green on `baaf35e9` with **190 checks passing and none failing**, across every build leg (net472 / net48 / netstandard2.0 / netstandard2.1 / net8.0 / net9.0 / net10.0, Debug and Release) and the full test matrix on Windows and Linux. Since then `master` has been merged twice more and two CI-stability fixes landed (see below), so the current head is re-running. Locally, `dotnet build UA.slnx -t:Rebuild` gives **0 errors** and the 2 pre-existing `CA1861` warnings; Robotics **467** net10.0 / **447** net48, Robot Intent **20** on both, OpenUSD **904**, AOT **121/121**, source generation **3771**. ### Review round Five reviewers over disjoint areas found defects worth calling out, all fixed in `3ffec2c1`: - **A refused mission step was reported `Succeeded`.** The advance path returned the same `false` for "steps exhausted" and "step refused", so a protective stop between steps told a supervisory system the mission had completed while the robot stood halted with work unexecuted. - **Three attacker-controlled NodeIds reached the executor unvalidated** — `Pick.Tool`, `Place.Tool`, `ToolChange.DockStation`. `DockStation` drives a physical tool-exchange motion to a location the server never authorised. The replacement test enumerates NodeId members by reflection so the next one added cannot be missed. - **A lock-order inversion that loses a stop.** `CanCancel` and the mission condition evaluator ran under the host lock while the executor's own thread re-entered it via `ReportBlendBegin`. - **A non-terminal executor outcome killed the pump** from outside the `try/catch` that exists because the executor is untrusted — `Ready` stayed true and submissions kept being accepted while nothing moved. - **`Pause` published `Suspended` while the robot kept moving.** Now queue-only and honest about it; the specification gained the matching rule ([opcua-drafts `357a558`](https://github.com/marcschier/opcua-drafts/pull/47)). - **`RI-RealTimeChannel` and `RI-Safety` were false claims** — granted on an empty folder with no methods, and on the mere presence of a Mandatory node. - **The Part 10 promotions never promoted.** Namespace-1 BrowseNames declared second members beside the inherited ones; the conformance test could not see it because its lookup stripped the prefix. Fixed upstream and regenerated. - **Renderer picking never worked.** The reflective probe bound a renderer that is not a picking backend and throws off its owner thread. It now binds only `IRenderPickingBackend`, falls back immediately and loudly, and the docs say plainly that renderer picking is unavailable with this package version. - **The client lost command authority the instant it gained it** — monitored items report the current value first, and the lease treated every notification as a loss. ### CI stability fixes for master-side failures The CI split in #4158 turned on legs that had never run, and two of the failures they exposed were real bugs on `master` rather than anything this branch introduced. Both are fixed here because they were blocking every branch: - **ECC key generation was broken on macOS.** `ExportExplicitParameters` throws `PlatformNotSupportedException` there — Apple's SecurityTransforms supports named curves only — and it was called for exactly one value, the curve order `n`. The four curves OPC 10000-12 admits have published orders, so those are carried directly, with `CurveOrderTableMatchesThePlatform` checking each against the platform's own exported order wherever export works, so the constants cannot silently drift. This accounts for the two ECC failures in `test-macOS-latest-Server`. - **The certificate-leak detector raced asynchronous disposal.** It polled a fixed budget and reported whatever it saw, which cannot distinguish a genuine leak (count steady) from a suite still draining fire-and-forget disposals (count falling) — on a loaded agent that produced `Certificate leak detected: 2 instance(s)` with every test passing. It now waits for the count to **settle**, so a real leak is reported sooner than before while a slow drain gets as long as it keeps progressing. Shared by every suite that asserts no certificate leaks. The remaining macOS failures on `master` (`Gds`, `Tools`, `Redundancy.Samples`, `aot-macos`) have different root causes, are not reproducible without a macOS host, and are **not** addressed here. ### Merged with `master` again `master` through `416d619c4` (#4158, the CI matrix split and graduated coverage bands) is merged in as `08ae4528`. One conflict, in `docs/DeveloperGuide.md`, where both sides had deliberately rewritten the coverage-gate section: - `master`'s rewrite is kept in full — graduated `patch.bands`, Codecov being informational-only, and where the numbers surface per CI system. - This branch's paragraph about the `ignore` globs is kept too, re-sited ahead of those subsections. I re-checked it against the merged `coverage-thresholds.json` rather than assuming it still held: the list is still `tests/**`, `samples/**`, `obj`, `bin`, `*.g.cs`, with **`tools/**` absent** — so the warning that anything under `tools/` is measured like product code, and that an assembly no test project references contributes changed lines counted as *uncovered*, is still accurate and worth keeping. - `master`'s "reproduce a coverage failure locally" line supersedes this branch's near-identical one, so only `master`'s survives. Post-merge: `dotnet build UA.slnx -t:Rebuild` **0 errors**, 2 pre-existing `CA1861`; Robotics **467** net10.0 / **447** net48; Robot Intent **20** on both. ### Getting CI green The pipeline had never actually run on this branch until now (it was waiting on the fork-PR approval gate), so the first run surfaced four real problems, each fixed at source: - **Seven build legs failed** because `IntentEnabledRobot` pins `net10.0` for the OpenUSD payload but never opted into `RestrictForLegacyTfm`. On the legacy legs the libraries build as net472/net48, where `Opc.Ua.Types` exposes the `System.Threading.Lock` polyfill publicly, and that collides with the sample's own net10.0 BCL (`CS0433`). `Opc.Ua.OpenUsd.Connector.Viewer` was already using the opt-out for the same reason. - **A Linux-only test failure** turned out to be a real defect, not a platform quirk: `SetPose` published the *commanded* interpolated pose beside joint frames derived from the *solved* joints, so the two disagreed by the inverse-kinematics residual — about 8 mm, constant after any real motion. That is the lying-digital-twin defect one level deeper than the per-step IK fix reached. `SetPose` now derives both from the joint angles. - **Three source-generator tests** asserted the old emitted shape. The clause 6.2 Null-Variant fix was implemented in `ObjectTypeProxyGenerator` so every generated proxy benefits, which changed that shape; the tests are updated and strengthened to pin the `IsNull` branch as well. - **A CI-only timeout** in the simulated arm drain: the executor runs on a manual clock so the tests do not depend on machine speed, but the drain guarded itself with a wall-clock deadline, which put that dependence back. CI runs with coverage instrumentation, which is enough to take a per-step IK probe from ~0.6 s to over 5 s. The guard is now a simulated step budget. One failure was **not** from this PR and did not recur: a Roslyn `AccessViolationException` compiling `Opc.Ua.ISA95` on one leg, while the Debug leg of the same target passed on identical code and the Release build succeeds locally. ## Related Issues - Relates to #3827 — the request that prompted the robotics intent work. - Builds on #4127. The companion specification is drafted in the open at [marcschier/opcua-drafts#47](https://github.com/marcschier/opcua-drafts/pull/47) (`metaverse-specs/robot-intent/`), with the prior art and the reasoning behind each decision in the research document beside it. **Nothing in it is normative or endorsed by the OPC Foundation, and its NodeIds and namespace URI are provisional** — which is the main reason this is a draft. ## 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. On the test checkbox, precisely: the whole `UA.slnx` suite now runs on net10.0 with **50 of 52 assemblies fully green**, and `Opc.Ua.Robotics.Tests` passes on **all five** target frameworks including net472, net8.0 and net9.0 which had never previously been exercised. The two remaining failures are reproduced identically at the merge-base and are detailed above, so I have ticked the box; if you would rather it stayed unticked until those two are green, say so and I will revert it. ## Points I would most like feedback on 1. **The namespace and NodeIds are provisional.** They come from a draft specification, not from the OPC Foundation. If this lands before the model is registered, the identifiers will move. 2. **The model lives in `Opc.Ua.Robotics`.** It is standalone on the base UA namespace and takes no dependency on OPC 40010, so a separate `Opc.Ua.RobotIntent` package would arguably be cleaner. Keeping it here was a deliberate call — this is where the robot-facing API already is — but it is the easiest thing to change now and the hardest later. 3. **Removing the convention API from #4127 is a breaking change** for anyone who adopted it. It was explicitly marked non-normative, so I have assumed that is acceptable — please say if a deprecation period is wanted instead. 4. **Serial execution** is a deliberate simplification. Concurrent `None`/`Soft` intents would be an optimisation, not a correction, but it is worth agreeing that reading. 5. **`samples/Robotics/` reorganisation** moves `MinimalRobotServer`. Happy to split that into its own commit if it makes review easier. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1dab1302-19c5-4a9b-a50c-d97d389713aa Copilot-Session: a248a589-9a20-4372-868e-5d347e57001b |
||
|
|
fb80bfdc82 |
[WotCon] Track WoT Connectivity/Binding 1.1 and add projections (Section 12) (#4182)
Follow-up to #4128, which merged the WoT Connectivity 1.1 work against **draft2** of the companion specifications. Both drafts have moved since, and this branch tracks them through to **draft3**, adds the projection feature they introduced, and closes the gaps a bidirectional re-audit found. The specifications are the private [`OPCF-Members/spec-drafts`](https://github.com/OPCF-Members/spec-drafts) drafts of *OPC UA — WoT Connectivity 1.1* and *OPC UA — WoT Binding 1.1*. They are still drafts, which is why this is opened as a draft PR — see [Status](#status) at the bottom. ## What changed in the specifications Two revisions landed upstream while #4128 was in flight. **draft2** replaced the `eventGroup` / `actionGroup` grouping constructs with a single **projection / view** concept, added fifteen Section 6 vocabulary terms, and made two identifier rules normative. **draft3** ([spec PR #8](https://github.com/OPCF-Members/spec-drafts/pull/8)) un-deprecated the incorporated OPC 10100-1 v1.02 surface, restructured the conformance clause into three profiles, and tightened two security obligations. The draft3 NodeSet is **structurally identical** to draft2 — I diffed all 286 nodes node-by-node. Every change is metadata: 96 `ReleaseStatus="Deprecated"` attributes removed, `PublicationDate` advanced, and `WOTC-ProjectionMaterialization` claimed by three Type Nodes that previously implemented it in name only. ## Projections (WoT Binding Section 12) The substantial addition. A projection document selects affordances from other Thing Descriptions or Thing Models and resolves to a document of its own; materialized, it becomes an OPC UA `View` that **creates no affordance Node** and only `Organizes` what its sources already materialized. | Piece | Where | |---|---| | Document model and validation | `Opc.Ua.Types/Wot/WotProjection.cs`, `WotProjectionModel.cs` | | Four-stage resolution engine | `Opc.Ua.Types/Wot/WotProjectionResolver.cs` | | Materialization plan | `Opc.Ua.WotCon.Server/Materialization/WotViewProjectionPlan.cs`, `WotProjectionViewBuilder.cs` | | Live address-space host | `WotProjectionViewNodeManager.cs`, `LifecycleWotViewProjectionHost.cs`, `IWotViewProjectionHost.cs` | | Node lookup seam | `IWotMaterializedNodeIndex.cs` | Groups reached by `ua:Organizes` become organizational Objects rather than nested Views, only the outermost materialization is a `View`, and a member whose source is served by another server is omitted and reported rather than recreated — the federation case the clause calls out. ### How this is validated The specification publishes twenty worked examples, two of which are a **golden pair**: a projection document and the resolved view it is defined to produce. `WotSpecExampleTests` embeds all twenty and runs the pair through the resolver, asserting against *the specification's own expected output* rather than against my reading of the prose. That single document exercises all three selection forms, the bulk naming rule, the security closure naming and the provenance term. This is the strongest evidence in the PR, and it is worth more than the unit tests around it: it is the same check the upstream `validate_local.py` performs. ## Other spec work - **Section 6 vocabulary** — fifteen missing model and platform terms with their validation rules (`WotNodeSetConverter.ModelVocabulary.cs`). - **Portable identifiers** — the two rules release 1.1 made normative, with `WotNodeSetConverterOptions.AllowNonPortableIdentifiers` as a documented downgrade-to-warning switch for documents authored against v1.00. Defaults to `false`. - **xRegistry §6.9 identifier construction** (`XRegistryIdentifier.cs`) — reverse-authority symbolic identifiers; all seven published worked examples are test cases. ## Gaps found by re-auditing against draft3 I audited in both directions — every normative requirement to code, and every `uav:` term in code back to a spec that defines it. Three findings, all fixed: 1. **`Wot-Con 1.02` accepted anything well-formed.** draft3 makes format validation part of the unit, because *WoT-Con Minimal* is one unit wide and materialization projects a *valid* document. The 1.02 upload path only deserialized into the `ThingDescription` POCO, which happily accepts JSON that is not a WoT document at all — every member of that type is optional, so `{}` deserialized and was materialized. It now parses via `WotDocument.Parse` and requires the document to identify itself by a non-empty `name` or `title`, returning `Bad_DecodingError` and materializing nothing otherwise. The rule lives in `ThingDescriptionFormatValidator` and is shared with `CreateAssetForEndpoint`, whose auto-generated Thing Descriptions §11 also treats as untrusted input. 2. **Two `WoTFile` operations were ungated.** draft3 §11 names `Write` and `CloseAndUpdate` alongside the five management methods; both reached the materializer without an access check. `EnforceManagementAccess` is now wired into `WotAssetFileManager`. 3. **Two invented `uav:` terms.** `uav:NodeSet2Preservation` was declared and never used — removed. `uav:eventFields` is read by `OpcUaBindingPlanner` and defined in *neither* spec, while a comment claimed it came from OPC 10101. It still works for compatibility but is now documented as a non-standard extension at both sites rather than misattributed. The SSRF prohibition is satisfied by construction: there is no HTTP client anywhere in these assemblies, and all resolution goes through the injected `IWotThingResolver`. ## Two ambiguities fixed in the specifications themselves The audit turned up two places where a conforming implementer had to invent an answer. Rather than pick silently, I took both upstream — they are pushed to [spec-drafts PR #8](https://github.com/OPCF-Members/spec-drafts/pull/8) (all checks green) and are now normative in *Binding* §12.6 and *Connectivity* §7.13: - **`ViewVersion`** had one sentence saying only *that* it changes. It now changes iff the resolved membership changes, is updated at commit time alongside the `NodeVersion` stamping, and is a deterministic function of the membership alone in a canonical order — so two servers that resolved the same membership agree, which a per-server counter would not across a redundant pair. Explicitly **not monotonic**: compare for inequality only. - **"Type-level view"** was used once in §12.2 and defined nowhere, while every other statement of materialization was universal. §12.6 now *defines* it rather than contradicting §12.2: the NodeClass is `View` in both cases. Both were already what this code did, so neither changed behaviour — but the order-insensitivity the clause added was **not** covered by any test, and `ViewVersionIsUnchangedWhenOnlyTheOrderOfTheMembershipChanges` now holds it. It is mutation-verified: removing the ordinal sort turns it red. `docs/WotBindings.md` no longer lists these as points "the specification leaves open". ## Validation | Suite | Result | |---|---| | `Opc.Ua.Types.Tests` | 8525 ✅ (8518 on net48) | | `Opc.Ua.WotCon.Tests` | 1057 ✅ (net10.0 and net48) | | `Opc.Ua.WotCon.Bindings.Tests` | 559 ✅ | | `Opc.Ua.XRegistry.Tests` | 196 ✅ | | `Opc.Ua.Server.Tests` | 4027 ✅ | | `Opc.Ua.SourceGeneration.Core.Tests` | 3771 ✅ | | `Opc.Ua.WotCon.Samples.Tests` | 9 ✅ | The full `UA.slnx` builds with **0 errors and 0 warnings attributable to this PR** on all 6 TFMs (`TreatWarningsAsErrors` is on). `Opc.Ua.Server.Tests` and the source-generation suites are listed because this PR now also touches `MasterNodeManager` and the WoT-Con NodeSet. ## Compatibility No public API is removed or broken. Everything added is new surface using `ArrayOf<T>` / `ByteString` per the repo conventions, wired for DI with a direct-construction fallback, and the one behavioural tightening (`Wot-Con 1.02` format validation) rejects input that was never valid to begin with. One pre-existing issue is **not** addressed here because it came in with #4128 and is out of scope: `WotAssetFileManager.OnCloseAndUpdate` uses `.GetAwaiter().GetResult()`, which the repo rules ban. Worth a follow-up. ## Landed since this PR was opened - **DI upgraded to 1.05.0.** `ConnectsTo` was declared a subtype of `HierarchicalReferences`, contradicting OPC 10000-100 §5.5 Table 48. That is an upstream error in every official DI NodeSet up to 1.04, corrected by the OPC Foundation in 1.05.0, so the fixture is replaced with the verbatim 1.05.0 file rather than hand-patched. A guard test pins the corrected form. This also closed a second gap: six DI types the WoT model was missing are 1.05 additions. - **The sample Pump has the type definition OPC 40223 declares for it.** `Pump1.Identification` carried DI's `FunctionalGroupType` instead of `PumpIdentificationType`. Every test, the sample documents and the README agreed with each other about it, which is why it survived. - **`WotPumpAddressSpaceComparisonTests`** compares this server against `PumpDeviceIntegrationServer`, which builds the same OPC 40223 Pump from generated NodeSets and hand-written wiring. An independent oracle rather than a restatement of the same assumptions - it is what found the defect above. - **Error containment in `MaterializeProjectionViewsAsync`.** An exception mid-loop discarded the applied View handles before they were published, leaving View Nodes in the address space that nothing could remove. - **`HasWoTProjection`** is now created; it was documented and plumbed but never wired. - **The default in-memory View host** no longer deletes the View it has just applied. - **An undeclared NodeSet alias now says so.** It previously surfaced as `IdentifierMissing`, naming neither the value nor the fact that an alias was expected. - **`ViewVersion` follows the algorithm the specification now states.** My earlier spec text was rejected on review, correctly: it named neither the function nor the canonical order it demanded, ignored that OPC 10000-3 §5.4 requires a value greater than zero, and its "when, and only when" is unsatisfiable for a 32-bit value. §12.6 now specifies the algorithm outright and this implementation follows it exactly, pinned by an independently computed test vector. Implementing it surfaced a further defect in the clause - joining members on U+000A is not injective, because a NodeId string identifier may itself contain U+000A - which is fixed in the specification by length-prefixing each member. - **An auto-generated Thing Description is treated as untrusted input.** §11 requires it; `CreateAssetForEndpoint` validated the endpoint but handed whatever the discovery provider returned straight to the materializer. The `Wot-Con 1.02` format rule now gates both paths from one place. - **Document bytes moved out of the registry snapshot** into a content-addressed store, so a commit no longer rewrites the whole corpus to edit one document. Writes stage and are promoted during the commit, which preserves both durability-first ordering and the fail-closed rule that a blob directory without a manifest means operator recovery. Restoring streaming digest verification along the way closed a real gap: content integrity had degenerated to a length comparison. - **External references now reach a NodeManager registered later.** A reference to a Node that does not exist yet is dropped rather than queued, and the dynamic-registration path had no second phase to apply them in, so registering a NodeSet before the one it points into lost the forward edge permanently. Review of that change then found a state leak in its rollback path, fixed with a red-to-green regression test. - **`WOTC-Legacy` is renamed `Wot-Con 1.02`** in the specification and here. Editorial: the NodeSet diff is `<Category>` values only. ## Status Open for review. Two things may still move: 1. **The specifications are unpublished drafts.** Anything here can change if the working group decides differently. The `ViewVersion` definition has since been settled - §12.6 specifies the algorithm and this follows it - but the same could happen to any other clause tracked here. 2. **`uav:eventFields`** is kept as a documented non-standard extension. That is a judgement call the WG may want to reverse, either by adopting the term or removing it. Two known items are recorded rather than fixed: - `Pump1` has no hierarchical parent, so it is reachable by NodeId but not by browsing down from `Objects`. The cross-NodeSet ordering defect this was originally attributed to is fixed above, and cross-manager references are now symmetric in both registration orders. The Pump itself is still blocked on a different cause found while verifying that fix: with the reference added, `DeviceSet` exists but the Pump has **zero** inverse references, so the edge is dropped at *import* - before any external-reference machinery runs. That is a separate defect in the materialization path, not the one that was fixed, and it is left recorded rather than guessed at. - `WotAssetFileManager.OnCloseAndUpdate` uses `.GetAwaiter().GetResult()`, which the repo rules ban. Pre-existing from #4128 and out of scope here. Review of the projection resolver and the materialization host is the most valuable thing to look at; the vocabulary and identifier commits are mechanical by comparison. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8 |
||
|
|
ae241b6625 |
Robotics: the OPC 40010 companion SDK, its client and the robot sample (#4127)
**Stack 5/5** — base #4126. Part of the #4096 split; see #4096 for the overall picture. Closes the API work for #3827. > Review after #4123, #4124, #4125 and #4126. This branch is content-identical to the original #4096 branch, so merging the stack lands exactly what #4096 contained. ## The problem this solves OPC 40010 1.02 defines a rich robot **topology** but **no motion verbs at all** — its actuation surface is the SystemOperation and TaskControl state machines plus a Controller `Programs` directory. Meanwhile [URML](https://github.com/URML-MARS/URML) names OPC UA Robotics as its canonical non-ROS substrate and asks for `MoveTo` / `Grasp` / `Release` methods the companion spec does not define, so today it needs a hand-maintained per-deployment NodeId mapping file. This closes that gap in two clearly separated tiers, and never presents the second as standard. ## Contents **Server** — stock `RoboticsNodeManager`, ordered `IRoboticsModelProvider` composition, `IRoboticsConfigurator` code-behind, and validated fluent topology builders for systems, controllers, motion devices, axes, power trains, motors, gears, drives, safety states, software, task controls and task modules, with the correct OPC 40010 semantic references. **Standard operations** — `AddSystemOperation` and `AddTaskControlOperation` drive the Part 16 state machines. An illegal cause is rejected with `BadInvalidState` before the handler runs; a handler returning a bad `ServiceResult` does not move the machine; and a transition already in flight rejects a concurrent one rather than letting both commit. **Programs** — binds the optional `FileDirectoryType` through the reusable `IFileDirectoryBinder` added in 1/5. **Operation conventions** — opt-in and explicitly non-normative `MoveTo` / `MoveJ` / `MoveL` / `Grasp` / `Release` / `PickFrom` / `PlaceAt` / `SwapTool` / `SetOutput` / `CallProgram`, created **only** in an application-owned namespace — the OPC UA, DI, IA and Robotics namespaces are rejected — plus a generic `AddOperation<TRequest, TResponse>` extension point. **Client** — `RoboticsClient` extends the DI client, is subtype aware throughout, and provides typed topology snapshots, the standard operation and task-control clients, `ProgramsAsync`, streaming observation and a DI factory. Verbs resolve by BrowseName, so no NodeId mapping file is needed. **Sample** — `MinimalRobotServer` composes five models (Robotics, IA, DI, OpenUSD, RSL/GPOS) purely through `IRoboticsModelProvider` contributions, which is the real proof that the stock hosting is sufficient. It follows the published KUKA KR 16-2 kinematics and axis limits with an eased pick-and-place cycle. Also restores the vendor-subtype scanner test held back from 2/5, which uses the Robotics NodeSet as its fixture. ## Defects found and fixed along the way - **`ControllerType.CurrentUser`** is Mandatory in OPC 40010 but was neither built nor validated. Now has a builder and a validation error. - **`MotionDeviceType.TaskControlReference`** was absent because it must target a `TaskControlOperationType` instance that did not exist. Now wired. ## Validation `dotnet build UA.slnx` — 0 errors, 0 warnings. | Suite | Result | | --- | --- | | Opc.Ua.Robotics.Tests | 103 / 0 | | Opc.Ua.OpenUsd.Tests | 635 / 0 | | Opc.Ua.Di.Tests | 342 / 0 | | Opc.Ua.SourceGeneration.Tests | 94 / 0 | | Opc.Ua.Aot.Tests (NativeAOT) | 120 / 0 | --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8cbb8cd0-f0cb-4ab0-bea2-6202fbf69485 |
||
|
|
ea0f266b66 |
Move build and test to the Azure DevOps managed pool and replace Codecov with an in-pipeline coverage gate (#4122)
# Description Moves the all-target-framework build, the cross-platform test matrix and the Native AoT run from GitHub Actions onto the Azure DevOps `netstandard` Managed DevOps Pool, and replaces Codecov with a coverage gate that runs inside the pipeline. ## 1. CI backend switch One conceptual switch decides who owns that work. It is checked into source in **two places that must be flipped together**: | File | Setting | Default | | --- | --- | --- | | `azure-pipelines.yml` | `parameters.ciBuildBackend` | `ado` | | `.github/workflows/buildandtest.yml` | `env.CI_BUILD_BACKEND` | `ado` | Setting both to `actions` restores the previous behaviour exactly. Azure Pipelines gains what it was missing to take the work over: - A **Linux all-TFM `sln.yml` leg**, replacing `build-all-tfm-linux`. `sln.yml` gets a `configurations` parameter so this leg builds `Release` only instead of doubling the managed-pool job count. - A **Linux agent in the `net10.0` `testfastpr` matrix**, replacing the ubuntu `build-and-test` job. - **Native AoT and Code Coverage now gate pull requests** instead of running only on Schedule/Manual. The coverage stage drops its `testdebug` dependency, which is Schedule/Manual-only and would otherwise have skipped coverage on every PR. - On a PR, Windows builds the .NET Framework TFMs and Linux the modern ones — mirroring the split the GitHub Actions jobs used — so all seven TFMs stay covered without fanning every TFM across both pools. Two things are deliberately **not** covered by the switch: - **macOS** stays on GitHub-hosted runners, because Managed DevOps Pools provide no macOS image. - **`master378` and `develop/*`** keep running the GitHub Actions jobs, since Azure Pipelines only builds `master`/`main` from this file. GitHub Actions does not expose the `env` context to job-level `if:` conditions, so the switch is resolved once in the existing `discover` job and re-exported as job outputs. A new **`build-and-test summary`** job gives branch protection one stable check to require, instead of the generated matrix job names. It treats an intentionally skipped job as success but still fails on a real failure or cancellation. The `paths:` allow-list was moved off the `pull_request` trigger and into `discover`. A workflow filtered out by `paths` never reports its checks, and a required check that never reports leaves a PR permanently *"Expected — waiting for status to be reported"*. That would have made the routine "ratchet the coverage floor" PR (which touches only `coverage-thresholds.json` and docs) unmergeable. ## 2. Coverage gate replacing Codecov Codecov is removed entirely — `codecov.yml`, both uploads, the badge, and the token usage. `.azurepipelines/check-coverage.ps1` runs at the end of the `Code Coverage` stage against the Cobertura report ReportGenerator already merges, driven by `coverage-thresholds.json`: | Check | Behaviour | | --- | --- | | Project floor | **Blocking** — total line and branch rates against an absolute floor. | | Patch coverage | **Blocking on PRs** — changed lines must reach `patch.target`, tolerating `patch.threshold` percentage points (the same semantics `codecov/patch` had). | | Baseline delta | **Advisory** — reports the change against the recorded baseline, never fails. | The project rate is recomputed from the per-line data rather than read off the Cobertura root element, so the `ignore` globs carried over from `codecov.yml` apply to the project floor as well as to the changed-lines gate — otherwise samples and generated code would count towards one gate but not the other. `testcc.yml` gains an explicit `checkout: self` with `fetchDepth: 0`, because the default shallow clone cannot diff against the base branch. ## Follow-up actions for maintainers Two items cannot be automated from source: 1. **Enable the `/azp run` gate.** In the Azure DevOps portal: pipeline → **More actions** → **Triggers** → **Pull request validation** → **Require a team member's comment before building a pull request**, scoped to *Only on pull requests from non-team members*. This makes Copilot-authored and outside-contributor PRs wait for a maintainer's `/azp run`, mirroring the approval GitHub Actions already requires. The documentation added here describes the workflow and the private-org-membership gotcha; **the setting itself still has to be switched on.** 2. **Update branch protection and secrets.** Drop the `codecov/project` and `codecov/patch` required checks, require `build-and-test summary` and the ADO coverage check instead, and remove `CODECOV_TOKEN` from the GitHub repository secrets and the ADO variable group. Also note the thresholds in `coverage-thresholds.json` are seeded from the last Codecov reading (73.61 %) with headroom, because the in-pipeline number had not been observed yet. The first full `Code Coverage` run on master should confirm it and the floors should be tightened then — the gate prints the exact figure it computes. ## Related Issues _No tracking issue; this is CI infrastructure work requested directly._ ## 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. - No product code changed, so there are no unit tests to add. The new logic was verified with purpose-built harnesses instead: - All 8 branch/event/backend combinations of the `discover` switch, confirming `master378` and `develop/*` are untouched and that `actions` restores the old behaviour. - 12 scenarios for `check-coverage.ps1`: the project floor, the patch gate, ignore globs applying to *both* gates, three path-normalization shapes, and the advisory delta. - 14 path-relevance cases confirming the in-job filter matches the retired `paths:` allow-list exactly. - End-to-end against a **real** ReportGenerator report: correctly resolved `src/Opc.Ua.PubSub/DependencyInjection/PubSubBuilder.cs`, found exactly the four modified tracked lines and reported 50 % patch coverage (2 covered, 2 uncovered). - [x] I have added all necessary documentation. - New **Continuous integration** section in `docs/DeveloperGuide.md`, a CI note in `CONTRIBUTING.md`, and links from `docs/README.md`. Also corrected the CI matrix description, which claimed the PR gate ran net48 + **net8.0** — it runs net48 + **net10.0**. - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. - `dotnet build src/Opc.Ua.Server` → 0 warnings, 0 errors. No product code changed. - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. - Not applicable — no product code changed. `Opc.Ua.Server.Tests` was run on net10.0 during development (3480 passed; the single `ServerFluentApiHostingTests` failure is pre-existing and reproduces on master). - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. - [x] I have addressed **all** PR feedback received. - A review pass over this branch flagged the `paths:`-filter deadlock described above; it is fixed in `1926e240e`. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 181ba7ea-c72f-42d7-a737-00a3faadaa17 Copilot-Session: 26aa55a2-5731-4a4c-b61c-df2f7a785eca |
||
|
|
5b9bcedc62 |
Add OPC UA ISA-95 Common Model and Job Control support (#4089)
# Description Adds first-class support for the OPC UA ISA-95 companion specifications: - OPC-10030 Common Model. - OPC-10031-4 Job Control V1. - OPC-10031-4 Job Control V2. The change introduces `Opc.Ua.ISA95`, `Opc.Ua.ISA95.Client`, and `Opc.Ua.ISA95.Server`, together with a NativeAOT-compatible minimal server sample and comprehensive tests. ## Highlights - Compiles the three canonical NodeSets and identifier mapping CSV files through the source generator. - Adds NodeSet identifier mapping validation, typed method arguments/results, companion event records, and generated method argument nodes. - Provides typed Common Model builders with normative relationships and an injectable `GeoSpatialLocationType` provider. - Implements lossless V1/V2 Job Control providers over a shared version-neutral store, including state paths, status events, comments, response queries, catalog changes, and cohesive DI registration. - Adds subtype-aware typed clients, standalone encodeable registration, `IIsa95ClientFactory`, and V2 status-event streaming. - Adds the `MinimalIsa95Server` sample, package documentation, and an ISA-95 guide with conformance and HA guidance. - Tracks typed RSL/GPOS integration as follow-up issue #4095. ## Validation - `Opc.Ua.ISA95.Tests`: 137/137 passed on `net10.0` and `net48`. - Source-generator suites: 81/81 model, 90/90 stack, 3718 passed / 8 skipped core. - `EventRecordFieldReaders` feedback tests: 6/6 on `net10.0` and `net48`. - `EventRecordFieldReaders` changed-method patch coverage: 100% line coverage (36/36) and 100% branch coverage (22/22). - `Opc.Ua.ISA95`, `.Client`, and `.Server` build across all configured target frameworks without new diagnostics. - Cobertura line coverage: 88.08% model, 90.04% client, 91.19% server. - NativeAOT sample publishes and passes a live typed V2 Store/status-event validation. - The ISA-95 NativeAOT smoke test passes in the source-generated native test executable. - OPC UA KB NodeSet validation reports zero errors for Common, V1, and V2. The broader NativeAOT executable still reports unrelated existing GDS and complex-type test failures; the ISA-95-specific native test passes. ## Node state generator fixes Generating method arguments inline for imported nodesets regressed every model that already emits `InputArguments` / `OutputArguments` child node factories: - The inline block ran before the child factories. `MethodState.CreateOrReplaceInputArguments` keeps the property that was assigned first, so the child carrying the well-known NodeId was dropped and a NodeId-less placeholder remained in the address space. This broke browsing (reference descriptions without a target) and made method arguments unreachable at their well-known NodeIds. - The `MethodDeclarationId` fallback keyed on an autogenerated display name redirected every declared method of the standard address space to its own NodeId, which made method calls fail. The inline block is now emitted after the child factories and reduced to a value assignment when a child factory exists. The value is only re-emitted when an argument references a namespace other than OPC UA, because the value of an imported property is the verbatim XML of the source nodeset whose namespace indices do not survive the import. The `MethodDeclarationId` fallback is restricted to declarations without a resolvable NodeId. The generated OPC UA address space is byte-identical to `master` again. ## Related Issues - Fixes #4088 - Follow-up: #4095 |
||
|
|
8538f531e7 | Update paths for the consolidated repository layout | ||
|
|
7d0e5870cb |
Migrate to source-generated logging across Stack, Libraries and Applications (#3908) (#4003)
# Description Migrates the entire code base (Stack, Libraries and Applications) from direct `ILogger.LogInformation/LogError/LogWarning/LogDebug/LogTrace/LogCritical(...)` calls to **source-generated `[LoggerMessage]` logging**, as tracked in #3908. Source-generated logging avoids boxing of value-type arguments, caches the message formatter, and emits an efficient `IsEnabled` check so a disabled log level costs nothing. This is a behavior-preserving refactor: message text, levels and structured placeholders are unchanged; only the call shape changes. ## What changed - **36 projects migrated.** Every static log call now goes through a per-file `internal static partial class <Class>Log` holding `[LoggerMessage]` extension methods on `ILogger`, so call sites keep the natural `logger.SomeEvent(...)` shape. Closely-related files that emit the same messages share one log class (e.g. the encoders/decoders in `Opc.Ua.Types` share `EncodingLog`) to avoid duplication and CS0121 ambiguity. - **Per-assembly event-id classes.** Each project has one `internal static class <AssemblyToken>EventIds` (e.g. `CoreEventIds`, `ClientEventIds`, `PubSubEventIds`) with a reserved `public const int` offset block per log class. The assembly-token prefix avoids CS0436 collisions across `InternalsVisibleTo` boundaries. - **`CA1873` enabled solution-wide** in `.editorconfig` now that the whole code base (including test, sample and tooling projects) is migrated, so any regression back to a direct `ILogger.Log*` call with an expensive argument is flagged. Every occurrence has been guarded with an `IsEnabled` check (or the check merged into an existing condition where the log was the sole body of an `if`/`else`, preserving side effects); the only suppressions are two Moq `Verify` expression-tree sites, which are not executed log calls. - **Dynamic log levels stay hand-written.** A handful of calls whose level is only known at runtime keep the structured `logger.Log(logLevel, ...)` form under an `if (logger.IsEnabled(logLevel))` guard — `[LoggerMessage]` requires a compile-time level. - **netstandard duplicate-generator fix.** Projects that also pull in an R9 package (`Microsoft.Extensions.Http.Resilience`, `.Compliance`, `.Telemetry`) get the `Microsoft.Gen.Logging` generator in addition to the in-box one; on `netstandard` both implement every partial method (CS0757). A single `Directory.Build.targets` target removes the R9 analyzer on `netstandard` only (no-op elsewhere and where it is not referenced). - **Shared/linked source files** that are compiled into more than one project (e.g. sample files linked into test projects) use self-contained literal event ids instead of a per-assembly class the consumer does not compile. - **Docs.** The authoring recipe (event-id convention, log-class convention, parameter rules, the `IsEnabled` guard, shared/linked files, and the netstandard generator interaction) lives in the new [`Docs/DeveloperGuide.md`](Docs/DeveloperGuide.md#add-a-log-message-source-generated); `Docs/Diagnostics.md` keeps the runtime/observability side and links to it. - **Redaction preserved.** Log calls that previously wrapped sensitive values with `Redact.Create(...)` keep passing the `RedactionWrapper<T>` (instead of an eager `.ToString()`), so format-time redaction still applies — this also clears the CodeQL clear-text alerts introduced during the migration. ## Validation - Full `UA.slnx` build passes with **0 errors / 0 warnings**; the multi-target test projects force every referenced library to build on net472/net48/netstandard2.1/net8.0/net9.0/net10.0. - No unguarded static `ILogger.Log*` calls remain anywhere (only the guarded dynamic-level calls); `CA1873` is clean across the full solution on net10.0. ## Related Issues - Fixes #3908 ## Checklist _Put an `x` in the boxes that apply. You can complete these step by step after opening the PR._ - [ ] 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. - [ ] 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. - [ ] 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. |
||
|
|
f9a8343dea |
Full 1.05.06 compliant OPC UA Part 14 (PubSub) (#3892)
# Full OPC UA Part 14 (PubSub) — v1.05.06 This PR replaces the legacy PubSub stack with a modern, **OPC UA Part 14 v1.05.06**-compliant reimplementation built on current .NET: fully `async`, dependency-injection-first with a fluent builder (`services.AddOpcUa().AddPubSub(...)`), NativeAOT/trim-safe, and designed for high-throughput, highly-available, distributed deployments. The libraries — `Opc.Ua.PubSub`, `Opc.Ua.PubSub.Udp`, `Opc.Ua.PubSub.Mqtt`, `Opc.Ua.PubSub.Server`, and `Opc.Ua.PubSub.Adapter` — keep source compatibility with 1.5.378 where practical (obsolete shims) and ship a complete migration path. See the **[PubSub guide](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md)** and the **[2.0.x migration guide](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/migrate/2.0.x/pubsub.md)**. **Encodings & transports.** Both wire encodings are implemented in full — **UADP** (key/delta/event/keepalive, RawData, chunked, signed+encrypted) and **JSON** (verbose/compact, single-message, metadata; migrated from `Newtonsoft.Json` to `System.Text.Json`) — across **UDP** (uni/multi/broadcast + discovery), **MQTT** (3.1.1/5.0, retained metadata, TLS), and a from-scratch **DTLS 1.3** secured datagram transport (`opc.dtls://`) implemented on the .NET BCL crypto primitives (ECDHE, HelloRetryRequest cookies, certificate auth, AEAD record protection, HKDF key schedule), wired through `AddUdpTransport().WithDtls(...)`. Details in [Transports](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#transports) and [Encodings](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#encodings). A raw **Ethernet (Layer 2)** transport (`opc.eth://`, EtherType `0xB62C`, optional 802.1Q VLAN) carries UADP NetworkMessages directly in Ethernet II frames with no IP/UDP, via `AddEthTransport()` (`.WithPcap()` for cross-platform capture). See [Ethernet transport](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#ethernet--uadp-opceth). **Runtime schema generation.** A new schema subsystem (`Opc.Ua.Core.Schema`, `Opc.Ua.PubSub.Schema`) generates **XSD**, **OPC Binary (BSD)**, and **JSON Schema** for OPC UA data types at runtime — including PubSub NetworkMessage/DataSet message schemas — driven from the type system and source-generated metadata (no reflection; NativeAOT/trim-safe). It is consumable from the client, server, and PubSub stacks. See the **[Schema generation guide](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/SchemaGeneration.md)**. **Security, fail-closed.** Message-level security is wired end-to-end through `IPubSubSecurityWrapperResolver`: publishers are fail-closed (a secured group with no key source throws rather than emitting plaintext) and subscribers enforce per-reader inbound security. It includes AES-CTR (128/256) policies with HMAC-SHA-256, a deterministic 96-bit nonce with a messages-per-key cap, a monotonic sliding replay window, and a Security Key Service (SKS) pull client + in-memory server with SecurityGroups and key rotation. Two independent security assessments (IEC 62443-4-2 × OPC UA Part 2/4/14, plus a Microsoft SDL/SFI delta pass) drove remediation of all actionable findings — including orphaned message security, chunked-frame security bypass, unauthenticated Action invocation, pre-auth reassembly DoS, and DTLS hardening. See [Security](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#security) and [SKS](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#security-key-service-sks). **Server-side & high availability.** The runtime can optionally surface through a standard in-process OPC UA address space (`Opc.Ua.PubSub.Server`) with configuration methods, per-component diagnostics, the full PubSubState model, KeepAlive and MessageReceiveTimeout. Pluggable configuration/runtime-state stores, an id allocator, and HA runtime-state providers with failover support running publishers/subscribers in a distributed system. See [Server-side address space](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#server-side-address-space) and [High availability](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#high-availability-state-providers). **External-server adapter (bridge) with hot-reload.** A new `Opc.Ua.PubSub.Adapter` binds PubSub publishers, subscribers, and Action responders to an **external** OPC UA server over `ManagedSession` — cyclic Read or subscription-backed sources, browse-path node mapping, and metadata that resolves/retries and re-emits on model-change events. Adapter and PubSub configuration **hot-reload incrementally** (only changed writers/readers/responders are rewired; unchanged sessions are reused), driven by `IPubSubConfigurationStore.Changed` and `IOptionsMonitor`, with a pluggable change-feed source (e.g. etcd). See [Binding PubSub to an external server](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#binding-pubsub-to-an-external-opc-ua-server-client-session-adapters). **Samples, packaging & quality.** One combined sample, `ConsoleReferencePubSubClient`, demonstrates publisher, subscriber, and external-server modes on `Host.CreateApplicationBuilder` with full fluent/DI wiring; all libraries publish **NativeAOT with zero IL2026/IL3050 warnings** ([Native AOT](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/PubSub.md#native-aot), [DI](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/DependencyInjection.md)). The change ships comprehensive NUnit/integration suites and a libFuzzer harness, maintains ≥80% coverage on the new libraries, and is documented end-to-end ([What's new in 2.0](https://github.com/OPCFoundation/UA-.NETStandard/blob/part14pubsub/Docs/WhatsNewIn2.0.md)). |
||
|
|
0f710adc8e |
Split MigrationGuide.md, add What's New in 2.0, rewrite Profiles.md and Landing page (#3745) (#3874)
# Description This PR is a documentation overhaul for OPC UA .NET Standard 2.0. It splits the monolithic `Docs/MigrationGuide.md` into per-version sub-docs (so a coding agent can load only the relevant section), adds a new "What's New in 2.0" landing doc, rewrites `Docs/Profiles.md` and the repo-root `README.md` to reflect what master actually implements, and renames the misnamed `Docs/ALiasNames.md` to `Docs/AliasNames.md`. The branch is `docupdates` and contains **6 commits** stacked on top of `master`. All changes are doc-only except for `HelpLinkUri` string updates in `Opc.Ua.MigrationAnalyzer` (all 119 analyzer tests still pass × 5 TFMs). ## What changed ### `Docs/MigrationGuide.md` is now a thin landing page Before: a single ~144 KB / 1,549-line document covering 22 distinct migration topics in one file. After: a small 3.8 KB / 64-line landing page that introduces general principles, lists a per-version table, and keeps the tiny `1.05.377 → 1.05.378` and `1.04 → 1.05` legacy sections inline. The 1.5.378 → 2.0 content moves to a new **`Docs/migrate/2.0.x/`** folder with a README index plus 12 thematic sub-docs, mapped 1-to-1 to the existing H3 sections: | Sub-doc | Folded H3 sections | | --- | --- | | `telemetry.md` | Telemetry and Logging | | `packages.md` | Package, Target Framework and Dependency Changes | | `source-generation.md` | Source Generation | | `types.md` | Improved Type safety | | `encoders.md` | Encodeable Factory + Encoders/Decoders + Complex Types | | `node-states.md` | Node States + INodeCache changes | | `identity.md` | User Identity Token Handlers + User Identity Providers + Secrets | | `certificates.md` | Certificate Management + ICertificateProvider | | `configuration.md` | Configuration + Session/Browser State Persistence | | `sessions-subscriptions.md` | GDS Client API modernisation + ManagedSession + Subscriptions/Transports | | `alarms-model-change.md` | Alarms and Conditions + Address-space model change tracking | | `timeprovider.md` | Time and Timer abstraction (TimeProvider) | Each sub-doc starts with a **"When to read this"** lead paragraph (analyzer rules / symptoms / API areas it covers) and ends with a sibling-links footer. `Docs/migrate/2.0.x/README.md` has a symptom → sub-doc index table reused as-is in the agent skill. ### `opcua-v20-migration` agent skill is now context-efficient `.agents/skills/opcua-v20-migration/SKILL.md` gains a "Migration sub-doc index — load only what you need" section near the top with an explicit *"Do not load `Docs/MigrationGuide.md` into context"* instruction and a table keyed by symptom / API area / analyzer rule ID, so an agent loads at most one sub-doc per migration scenario. Path references in `README.md`, `references/analyzer-rules.md`, `references/migration-patterns.md`, and `references/source-generator.md` are updated to point at the new sub-doc locations. ### New `Docs/WhatsNewIn2.0.md` Developer-facing narrative tour of the 1.5.378 → 2.0 delta (~320 lines, hybrid grouping: cross-cutting themes first — type system & immutability, async/cancellation/TimeProvider, DI & hosting, Native AOT, source generators, companion specs Part 9/11/13/16/17/18/20/100 + WoT + LDS, performance, security — then by layer: Server, Client/Sessions/Subscriptions, PubSub, GDS, Tooling, CI). Paragraph per feature, no PR/commit references, links to the deeper per-feature docs. Linked from `Docs/README.md` between `Profiles.md` and `MigrationGuide.md`. ### `Docs/Profiles.md` rewritten to reflect master Adds Functional Facets for newly-implemented features (Part 9 Alarms, Part 11 Historical Access, Part 13 Aggregates, Part 16 State Machines, Part 17 Alias Names, Part 18 Role Management, Part 20 FileSystem server, Part 100 Device Integration / Software Update, OPC 10100-1 WoT, Node Management, Local Discovery Server, Auditing, Model Change Tracking). Expands Client Profiles (classic `Session` vs `ManagedSession`, V2 subscription engine, `FileSystemClient`, `HistoryClient`, `AlarmClient`, NodeSet export, source-generated typed `ObjectType` proxies). Adds a Reference Applications section listing all of `Applications/*` (Quickstarts/Ctt/Mono configs, Reference Client, LDS, MCP, Minimal* / Pump* samples). Security policies and transport profiles confirmed against `Stack/Opc.Ua.Core/Security/Constants/*` — no changes needed there. For new facets whose canonical URI could not be confidently verified against `profiles.opcfoundation.org` (the registry is fully SPA-rendered), the doc describes the facet by name and points readers at the OPC Foundation registry rather than fabricating URIs. The `ServerProfileArray` in the reference-server configs is intentionally left unchanged to avoid CTT-compliance regressions from advertising an unverified URI. ### Repo-root `README.md` cleaned up Front page now makes the 2.0 / `master378` context explicit. New 5-section structure with one emoji per heading: 🆕 version callout → 📦 What it is → 🚀 Getting started (grouped sample list linking to each sample's own README) → 🔧 Migrating from 1.5.378 → 🤝 Contributing & license → 📚 Further reading. Removes the "New in 1.04.368 / 1.05.373 / .374 / .375 / .376 / .378 / 2.0" laundry list, the dated "Key Features and Updates in OPC UA 1.05" section, inline VS / `dotnet run` build instructions, the "Start the server" walkthrough, the "Remarks" (self-signed certs + LDS) notes, and the inaccurate `.NET Standard 2.1` overview claim. Trims the 11 status badges down to 5 essentials (release, NuGet downloads, build, tests, codecov). ### File rename — `Docs/ALiasNames.md` → `Docs/AliasNames.md` The on-disk filename had a stray capital `L` while every link in `Docs/README.md`, `Docs/Profiles.md`, etc. used the lowercase spelling. Renamed via case-only `git mv` (Windows two-step). Existing links across the docs now resolve on GitHub's case-sensitive filesystem. ### `Opc.Ua.MigrationAnalyzer` `HelpLinkUri` updates - `Tools/Opc.Ua.MigrationAnalyzer/Diagnostics/DiagnosticIds.cs` — `HelpLinkUriBase` repointed from `Docs/MigrationGuide.md#` to `Docs/migrate/2.0.x/README.md#` so the rule-ID help links land on the symptom → sub-doc index instead of a generic landing page. - `Tools/Opc.Ua.MigrationAnalyzer/Diagnostics/DiagnosticDescriptors.cs` — UA0021 / UA0022 message strings reference the new `Docs/migrate/2.0.x/certificates.md`. - `Tools/Opc.Ua.MigrationAnalyzer/Analyzers/UA0021CertificateValidatorRenameAnalyzer.cs` — XML-doc path updated. - `Tools/Opc.Ua.MigrationAnalyzer/NugetREADME.md` and `AnalyzerReleases.Unshipped.md` — references repointed. ## Verification - `Opc.Ua.MigrationAnalyzer` builds with **0 Warning(s), 0 Error(s)** in Release. - `Opc.Ua.MigrationAnalyzer.Tests`: **119/119 tests pass × 5 TFMs** (net48, net472, net8.0, net9.0, net10.0). - `ConsoleReferenceServer` builds with **0 Warning(s), 0 Error(s)** in Release on net10.0 (verifies the reference-server config XML still parses cleanly after our doc-touching). - **Every relative markdown link** in the new / changed docs (the new `Docs/migrate/2.0.x/*`, `Docs/MigrationGuide.md`, `Docs/WhatsNewIn2.0.md`, `Docs/Profiles.md`, `Docs/Sessions.md`, `Docs/IdentityProviders.md`, the agent skill files, and the new `README.md`) was walked programmatically — **0 broken links**. - Deep heading anchors verified to match GitHub-generated slugs (`MigrationGuide.md#encoders-and-decoders`, `Sessions.md#1-session--the-opc-ua-session-primitive`, `Sessions.md#3-managedsession--the-connection-state-machine-facade`, `migrate/2.0.x/identity.md#user-identity-providers`, `migrate/2.0.x/identity.md#user-identity-token-handlers`, `migrate/2.0.x/encoders.md#encoders-and-decoders`, `migrate/2.0.x/node-states.md#inodecache-changes`, `migrate/2.0.x/certificates.md#certificatemanager-and-segregated-interfaces`, `migrate/2.0.x/sessions-subscriptions.md#managedsession-and-automatic-reconnection`). - No `ALiasNames` (capital L) references remain anywhere in the repo (case-sensitive search). ## Commit history (6 commits) 1. `548d0b5ed` — Docs: rename `ALiasNames.md` to `AliasNames.md` 2. `8a8db8c30` — Docs: add `WhatsNewIn2.0.md` (1.5.378 → 2.0 highlights) 3. `e69b2169e` — Docs: rewrite `Profiles.md` to reflect master implementation 4. `5fb8a282b` — Merge remote-tracking branch `origin/master` into `docupdates` (resolves overlap with #3854's `Docs/Subscriptions.md` rename of `StreamingSubscription.md`) 5. `d853fbc74` — Docs: split `MigrationGuide.md` into per-version sub-docs (#3745) 6. `65d6e6851` — Docs: rewrite repo-root `README.md` for 2.0 ## Related Issues - Fixes #3745 (MigrationGuide.md document structure) ## 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. *(N/A — documentation change; existing `Opc.Ua.MigrationAnalyzer.Tests` (119 tests × 5 TFMs) still pass after the `DiagnosticDescriptors` / `DiagnosticIds` text updates.)* - [x] I have added all necessary documentation. *(this PR is the documentation; all internal links verified)* - [x] I have verified that my changes do not introduce (new) build or analyzer warnings. *(both `Opc.Ua.MigrationAnalyzer` and `ConsoleReferenceServer` build with 0 warnings / 0 errors)* - [ ] I ran **all** tests locally using the **UA.slnx** solution against at least .net **framework** and .net **10**, and all passed. *(only the migration-analyzer test suite was rerun, since this PR is doc + a few `HelpLinkUri` strings; full UA.slnx run pending CI)* - [ ] I fixed **all** failing and flaky tests in the CI pipelines and **all** CodeQL warnings. *(pending CI)* - [ ] I have addressed **all** PR feedback received. |
||
|
|
01d7162313 |
Part 9 Alarms & Conditions: complete server + client implementation (#3805)
Complete implementation of **OPC UA Part 9 ΓÇö Alarms & Conditions** for
both server and client, **OPC UA Part 16 ΓÇö State Machines** (generic
extensible client + server API), and the supporting infrastructure
(source-generated event records, source-generated ObjectType proxies,
`IAsyncEnumerable` streaming subscriptions, address-space model change
tracking, per-node `INodeCache` invalidation, unified DI integration).
### Server-side (Part 9 complete)
- **`AlarmConditionState`** ΓÇö every Part 9 method wired automatically
in `OnAfterCreate`: `Silence`, `Suppress`/`Suppress2`,
`Unsuppress`/`Unsuppress2`, `RemoveFromService`/`RemoveFromService2`,
`PlaceInService`/`PlaceInService2`, `Reset`/`Reset2`,
`GetGroupMemberships`. Each method generates the spec-mandated audit
event and supports an optional veto-delegate hook.
- **Latched alarms** ΓÇö `ActiveState` reflects real process state;
`LatchedState` persists until explicit `Reset` (with full precondition
validation).
- **Re-alarming** ΓÇö
`ProcessReAlarm`/`ResetReAlarmRepeatCount`/`IsReAlarmEnabled` helpers
(opt-in, no hidden timer).
- **Audible alarms** ΓÇö `UpdateAudibleState` for
`AudibleEnabled`/`AudibleSound`/`SilenceState` lifecycle.
- **Suppression + groups** ΓÇö `AlarmGroup`, `AlarmSuppressionEngine`
(with first-in-group), `AlarmRateTracker` (for `AlarmMetricsType`).
- **Spec-compliance fixes** ΓÇö `SuppressedOrShelved` now includes
`OutOfServiceState`; `GetRetainState` includes `LatchedState`;
`EffectiveDisplayName` composition includes `OutOfServiceState` +
`LatchedState`.
### Client-side Part 9 API
- **`AlarmClient`** ΓÇö typed Part 9 operations. Each method delegates
to the matching source-generated `*TypeClient` proxy
(`ConditionTypeClient`, `AcknowledgeableConditionTypeClient`,
`AlarmConditionTypeClient`, `DialogConditionTypeClient`,
`ShelvedStateMachineTypeClient`) ΓÇö one source of truth for every
method NodeId.
- **Interface hierarchy** — `IConditionOperations` →
`IAcknowledgeableConditionOperations` → `IAlarmOperations` +
`IDialogConditionOperations`.
- **Source-generated event records** ΓÇö new `EventRecordGenerator`
(alongside `ObjectTypeProxyGenerator`) emits `{Type}TypeRecord` per OPC
UA event type: `ConditionTypeRecord` →
`AcknowledgeableConditionTypeRecord` → `AlarmConditionTypeRecord` →
`LimitAlarmTypeRecord`/`ExclusiveLimitAlarmTypeRecord`/… all the way
to vendor subtypes. **Vendor alarm types get records automatically.**
- **`AlarmEventDecoder`** ΓÇö decodes `EventNotification.Fields` into
the most-specific generated record.
- **`AlarmEventFilterBuilder`** ΓÇö fluent builder for Part 9
`EventFilter`s.
- **`ISession.GetAlarmClient(telemetry)`** convenience extension.
### Part 16 state machines (generic, extensible)
- **Client**: extension methods on the source-generated
`StateMachineTypeClient` / `FiniteStateMachineTypeClient` ΓÇö
`GetCurrentStateAsync`, `ObserveFiniteTransitionsAsync`
(`IAsyncEnumerable<FiniteStateSnapshot>`), `WaitForStateAsync`,
`GetAvailableStatesAsync` / `GetAvailableTransitionsAsync`. **Vendor
proxies inherit the API automatically** through the generated
inheritance chain.
- **Server**: `StateMachineBuilder` (fluent: `AddState` /
`AddTransition` / `OnCause` / validation) +
`FluentFiniteStateMachineState` ΓÇö declare a Part 16 state machine
without subclassing the abstract base.
- **Alarms alignment**: `AlarmClient.GetShelvingStateAsync` /
`ObserveShelvingTransitionsAsync` surface the API for every alarm's
`ShelvingState` child.
### IAsyncEnumerable streaming subscription
- **`IStreamingSubscription`** with
`SubscribeDataChangesAsync`/`SubscribeEventsAsync` returning
`IAsyncEnumerable<T>` (Channel-backed).
- **`ManagedSession.DefaultStreaming`** ΓÇö lazy shared subscription;
disposed with the session.
- **Helpers** ΓÇö `TakeUntilAsync`, `WithTimeoutAsync`, `TakeAsync`,
`BufferedAsync` for state-machine waits and bounded observation windows.
- **`AlarmStreamExtensions`** ΓÇö
`SubscribeAlarmsAsync`/`SubscribeConditionsAsync`/`SubscribeDialogsAsync`
returning the source-generated record types.
### Address-space model change tracking
- **Server**: `RaiseGeneralModelChangeEvent` on both `CustomNodeManager`
and `AsyncCustomNodeManager`; `ModelChangeAggregator` (in
`Opc.Ua.Server.NodeManager`, used by every node manager) batches changes
per service call; **auto-emit** from `CreateNode`/`DeleteNode` (opt-out
via `ModelChangeEmissionEnabled = false`).
- **Client**: `IModelChangeTracker` subscribes to
`BaseModelChangeEventType` via the streaming subscription, decodes
`ModelChangeStructureDataType` payloads, invalidates the `INodeCache`
per-node.
- **`INodeCache.InvalidateNode(NodeId)`** ΓÇö new interface member (true
per-node eviction in `NodeCache`).
- **`ManagedSession.EnableModelChangeTrackingAsync()`** +
`ManagedSessionOptions.ModelChangeTracking` +
`ManagedSessionBuilder.WithModelChangeTracking()` fluent method.
### Dependency Injection integration
- **`services.AddOpcUa().AddClient(...).AddAlarms()`** ΓÇö registers
`AlarmClientFactory` so DI-hosted apps can resolve a Part 9 client per
session without `new`-ing one manually.
- Cross-link added to the unified DI surface (`AddServer` / `AddClient`
/ `AddComplexTypes` / `AddGdsServer` / `AddLdsServer` /
`AddWotConServer`).
### Quickstart server modernization
- `Quickstarts.Servers/Alarms/AlarmNodeManager.cs` ported to
`AsyncCustomNodeManager` and demonstrates the new `AlarmGroup` +
`AlarmSuppressionEngine` end-to-end via a writable `MaintenanceMode`
boolean that suppresses every member alarm.
### Documentation
- New: `Docs/AlarmsAndConditions.md`, `Docs/StreamingSubscription.md`,
`Docs/ModelChangeTracking.md`, `Docs/StateMachines.md` ΓÇö full
developer guides.
- Updated: `Docs/DependencyInjection.md` (`AddAlarms()` row + section),
`Docs/README.md` index, `Docs/MigrationGuide.md` (Part 9 / Part 16 /
model change tracking sections covering behavior changes + breaking API
surface).
- Sample: `Applications/ConsoleReferenceClient/AlarmClientSample.cs`.
|
||
|
|
8d2402c995 |
Bump version to 2.0 and land 1.5.378 to 2.0 migration guide (#3819)
Prepares the repository for a 2.0 release: bumps every version reference from 1.6 to 2.0, and lands the migration guide describing the 1.5.378 → 2.0 upgrade path. The 1.6 line never shipped, so this is a straight rename with no historical 1.6 migration content to preserve. The PR is split into two commits: 1. **`Bump version to 2.0 (next release is a major version change)`** — version metadata, NuGet metadata, the side documentation, and the migration agent file. 2. **`[Docs] Close 19 audit gaps in 1.5.378 to 2.0 migration guide`** — adds and reorganizes `Docs/MigrationGuide.md` to close every gap from a rigorous code-review audit of the 1.5.378 → 2.0 breaking-change surface. ### Version bump (commit 1) - `version.json`: `1.6-preview` → `2.0-preview` - `common.props`: `<VersionPrefix>1.6</VersionPrefix>` → `<VersionPrefix>2.0</VersionPrefix>` - `nuget/Opc.Ua.nuspec` and `nuget/Opc.Ua.Symbols.nuspec`: `1.6.0` → `2.0.0` - `README.md`: "New in 1.6" → "New in 2.0" - `Docs/ComplexTypes.md`, `Docs/Observability.md`, `Docs/Sessions.md`: 1.6 → 2.0 mentions - Renames `.github/agents/opcua-v16-migration.agent.md` → `.github/agents/opcua-v20-migration.agent.md` and updates the agent metadata (name, description, trigger phrases, version table) - `UA.slnx`: solution-items reference updated to the new agent filename - `Tools/Opc.Ua.SourceGeneration.Core/Generators/ObjectTypeProxyGenerator.cs`: two v16 → v20 comment fixes ### Migration guide (commit 2) Closes 19 gaps from an audit comparing `master378` (1.5.378) with `master` (2.0). Adds a new top-level **Package, Target Framework and Dependency Changes** section covering the new `Opc.Ua.Core.Types` and `Opc.Ua.Security.Certificates` packages, the `LibTypesTargetFrameworks` netstandard2.0 drop, the NuGet dependency additions/removals, and the residual Newtonsoft.Json dependency in `Opc.Ua.PubSub`. In-place additions: - new H4 for `INodeManager3` under Node States - `Session.Call` `Variant.From` worked example - new H3 `Subscriptions and Transports` with H4 verdicts for Durable subscriptions (source-breaking), PubSub (no source break) and Reverse connect (no source break) - new H3 `Telemetry and Logging` covering `ITelemetryContext` constructor-injection patterns - `in DataValue` async copy-to-local pattern - UNSAFE / SAFE pair for `WithPoolNotifications` pooling - using-alias example for classic vs V2 `SubscriptionOptions` - "Silent regression" callout for `OnAfterCreate` - numbered lifecycle ordering for `ApplicationConfiguration.CertificateManager` Corrections: - clarifies that the `SessionReconnectHandler` `[Obsolete]` is still present in 2.0 (not withdrawn); - clarifies that the source-generated `ServerCapability` constants vs the `ServerCapabilityInfo` registry type are distinct; - disambiguates `ReconnectPolicyOptions` (DTO) from `ReconnectPolicy` (runtime). Adds an "Automate the migration" pointer to the forthcoming `Opc.Ua.CodeFixers` analyzer package, which mechanizes 17 of the patterns covered in this guide. The analyzer is published in a follow-up PR. |
||
|
|
f3928db05b |
Add unified Microsoft.Extensions DI surface across OPC UA libraries (#3803)
Introduces a consistent `Microsoft.Extensions.DependencyInjection`
surface across the OPC UA libraries. Every consumer starts from a single
root entry point and chains feature-specific extension methods on a
unified builder. PubSub is intentionally excluded per the original
scope.
```csharp
services
.AddOpcUa() // Stack/Opc.Ua.Core
.AddServer(o => { /* … */ }) // Libraries/Opc.Ua.Server
.AddNodeManager<MyNodeManagerFactory>()
.Services
.AddOpcUa()
.AddClient(o => { /* … */ }); // Libraries/Opc.Ua.Client
```
Manual non-DI constructors (`new ApplicationInstance(...)`, `new
StandardServer(...)`, `new LdsServer(...)`, `new ManagedSession(...)`,
etc.) remain unchanged — DI is purely additive for users who want the
.NET Generic Host to own application lifetime, logging, and
configuration.
### What ships
| Feature library | Method on `IOpcUaBuilder` | Returns |
|----------------------------------------------|-------------------------------------------|--------------------------|
| `Opc.Ua.Core` (root) | `services.AddOpcUa()` | `IOpcUaBuilder` |
| `Opc.Ua.Configuration` | `builder.AddApplicationInstance()` |
`IOpcUaBuilder` |
| `Opc.Ua.Client` | `builder.AddClient(opt => …)` |
`IOpcUaClientBuilder` |
| `Opc.Ua.Client.ComplexTypes` | `builder.AddComplexTypes()` |
`IOpcUaBuilder` |
| `Opc.Ua.Server` | `builder.AddServer(opt => …)` |
`IOpcUaServerBuilder` |
| `Opc.Ua.Gds.Client.Common` | `builder.AddGdsClient(opt => …)` |
`IGdsClientBuilder` |
| `Opc.Ua.Gds.Server.Common` | `builder.AddGdsServer(opt => …)` |
`IGdsServerBuilder` |
| `Opc.Ua.Lds.Server` | `builder.AddLdsServer(opt => …)` |
`ILdsServerBuilder` |
| `Opc.Ua.WotCon.Server` | `builder.AddWotConServer(opt => …)` |
`IWotConServerBuilder` |
| `Opc.Ua.WotCon.Client` | `builder.AddWotConClient(opt => …)` |
`IOpcUaBuilder` |
### Design highlights
- **Binary-safe inheritance** — `IOpcUaBuilder :
IDependencyInjectionBuilder` so any existing `AddLogging(this
IDependencyInjectionBuilder)` callers keep compiling unchanged.
- **Three options overloads** per feature: `Action<TOptions>`
(AOT-safe), `IConfiguration`, `IConfigurationSection`. The two binding
overloads are annotated `[RequiresUnreferencedCode]` /
`[RequiresDynamicCode]`. Section names follow the `OpcUa:<Feature>`
convention (`OpcUa:Server`, `OpcUa:Client`, `OpcUa:Gds:Server`,
`OpcUa:Lds`, …).
- **Generic Host integration** — each server feature (regular, GDS, LDS)
registers its own `BackgroundService`. Marker singletons throw
`InvalidOperationException` on duplicate `.AddServer()` /
`.AddGdsServer()` / `.AddLdsServer()`. A regular server + GDS + LDS can
coexist in one host; each owns its own `IApplicationInstance` produced
by a shared `IApplicationInstanceFactory` so PKI roots, endpoints, and
certificates don't collide.
- **Registration isolation** — node managers registered under the
regular server via `.AddNodeManager<T>()` are wrapped in
`OpcUaServerNodeManagerRegistration` so they don't bleed into the GDS /
LDS hosted services.
- **WoT Connectivity server** composes on top of the regular server
feature: `.AddServer(...).AddWotConServer(...)` registers
`WotConnectivityNodeManagerFactory` as a server node-manager
registration.
- **Unified telemetry** — `services.AddOpcUa()` registers
`ITelemetryContext` via `TryAddSingleton` using a new
`ServiceProviderTelemetryContext` that resolves the host's
`ILoggerFactory` on first use. User-supplied registrations win.
### Removed pre-release DI surface
The pre-release `services.AddOpcUaServer(...)`,
`services.AddOpcUaClient(...)`, `services.AddOpcUaGdsClient(...)`
extensions are deleted. `IClientBuilder` was renamed to
`IOpcUaClientBuilder`. In-repo callers migrated:
- `Applications/MinimalCalcServer/Program.cs`
- `Applications/MinimalBoilerServer/Program.cs`
- `Tests/Opc.Ua.Client.Tests/ClientBuilder/OpcUaClientBuilderTests.cs`
(renamed from `OpcUaClientServiceCollectionExtensionsTests.cs`)
- Docs (`MigrationGuide.md`, `Sessions.md`,
`SourceGeneratedNodeManagers.md`, README, new
`Docs/DependencyInjection.md`)
### csproj audit
Pruned every redundant `Microsoft.Extensions.*` package reference.
`Opc.Ua.Core` already pulls in `Microsoft.Extensions.Diagnostics` and
`Microsoft.Extensions.Logging`, which transitively expose DI / Options /
Configuration.Abstractions / Options.ConfigurationExtensions to every
downstream library. Only `Microsoft.Extensions.Hosting.Abstractions`
stays explicit on the server libraries (it isn't transitively
available). Also pruned redundant `System.Formats.Asn1` (Bindings.Https)
and `System.Collections.Immutable` (Security.Certificates).
### Tests
50 new tests in eight projects:
| Project | Tests |
|--------------------------------------|-------|
| `Opc.Ua.Core.Tests` | 12 |
| `Opc.Ua.Client.Tests` | 4 |
| `Opc.Ua.Client.ComplexTypes.Tests` | 3 |
| `Opc.Ua.WotCon.Tests` | 6 |
| `Opc.Ua.Lds.Tests` | 5 |
| `Opc.Ua.Gds.Tests` | 6 |
| `Opc.Ua.Server.Tests` (integration) | 10 |
| `Opc.Ua.Aot.Tests` (smoke) | 4 |
| **Total** | **50** |
Combined-host integration tests cover the rubber-duck-driven scenarios:
telemetry-context override across legacy and new entry paths, server +
GDS + LDS coexistence with distinct ports and PKI roots,
duplicate-registration rejection, and node-manager isolation across
features.
### AOT
`dotnet publish Tests/Opc.Ua.Aot.Tests -c Release -r win-x64
--self-contained` reports zero `IL2026` / `IL3050` / `IL2104` warnings
from the new DI surface. All generic registrations carry
`[DynamicallyAccessedMembers(PublicConstructors)]`. AOT consumers stick
to the `Action<TOptions>` overload exclusively; the new
`Tests/Opc.Ua.Aot.Tests/HostingAotTests.cs` smoke fixture exercises this
end-to-end.
|
||
|
|
66f44396ff |
[Server] Improve Request Queue, fix Event handling, add Tests (#3611)
- implement RequestQueue using Channels - add event load test - add event integration test verifying event from server object is received - fix Client Reconnect Test - fix DiagnosticsNodeManager not creating RootNotifier for Server object - add Benchmarks for MonitoredItem.QueueEvent - add serveral UnitTest for DiagnosticsNodeManager Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: romanett <7413710+romanett@users.noreply.github.com> Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
0a2eb23e3a |
[Server] Implement AsyncCustomNodeManager (#3563)
* Use Interfaces Introduced with 1.5.378 Refactor to use interfaces for node managers and server Replaced concrete types with interfaces (e.g., IMasterNodeManager, ICoreNodeManager, IDiagnosticsNodeManager, IConfigurationNodeManager, IServerBase) across method signatures, properties, and internal structures to improve abstraction and flexibility. * Refactor CoreNodeManager to be Based on CustomNodeManager2 * Add Test * Add INodeManager3 with role permission validation Introduce INodeManager3 interface extending INodeManager2, adding synchronous role permission validation methods. Update all node manager interfaces and classes to use INodeManager3. Extend IAsyncNodeManager with async permission validation methods and update adapters for sync/async bridging. Deprecate FindNodeInAddressSpace in favor of new async FindNodeInAddressSpaceAsync. Refactor MonitoredNode2 to use thread-safe collections and locking for monitored items. Enhance event reporting with security and permission checks. Update constructors and documentation to reflect new interfaces and best practices. * Implement AsyncCustomNodeManager * fix comment * fix * Add tests * fix * Implement better locking strategy * fix * Add more tests * Add tests * Add tests * Add Tests * cleanup * cleanup * Add tests that verifies for AnalogItemState the instrument range is checked brefore writing * only assign new NodeID in default factory if NodeId isNull * Run all tests also with sampling groups * Add Docs * cleanup * Add chaos test * fix test |
||
|
|
601cabf86f | Replace all use of object in code and API with Variant (#3550) | ||
|
|
703069ed97 |
Add long-running connection stability test with dynamic token renewal (#3424)
* Initial plan * Add connection stability test with 5-minute token renewal Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Fix code style issues in ConnectionStabilityTest Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Remove unused dictionaries to reduce memory overhead Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Extract magic numbers to named constants for better maintainability Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Add explicit permissions to workflow for security Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Change non-CI test duration to 30 minutes for 6 token renewals Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Set local test to 1 min with 10-second token renewal for 6 renewals Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> * Fix SecurityTokenLifeTime to be applied for Server & Client before Startup --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> Co-authored-by: Roman Ettlinger <romanett98@gmail.com> |
||
|
|
c9b211e61e |
Add comprehensive OPC UA Profiles and Facets documentation (#3397)
* Initial plan * Add comprehensive OPC UA Profiles and Facets documentation Co-authored-by: romanett <7413710+romanett@users.noreply.github.com> * Fix URL in Profiles documentation to use current OPC Foundation site Co-authored-by: romanett <7413710+romanett@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: romanett <7413710+romanett@users.noreply.github.com> |
||
|
|
2960388560 |
Update headers and license files (#3388)
Update license headers to copy right year 2025, replace RCL/GPL headers with MIT, replace the nuget license with MIT (via license expression). |
||
|
|
35db52a7a6 | Move from .net 9 to .net 10 SDK and build new target framework .net 10 (#3333) | ||
|
|
8e7a73c7df | Introduce telemetry context and proper use of ILogger (#3213) | ||
|
|
805398942a |
[Server] Complete IAsyncNodeManager Interface with support for sync Calls using SyncNodeManagerAdapter (#3218)
* Allow to register IAsyncNodeMangers, Add SyncNodeManager Adapter. * Add RemoveNamespaceMananager(IASyncNodeManager) * Allow AsyncNodeManagers in ReferenceServer * Add documentation * Create real sync GetManagerHandle implementation --------- Co-authored-by: Marc Schier <marcschier@users.noreply.github.com> |
||
|
|
2f0770c569 |
[Server] Restore durable subscriptions on Server Restart (#3025)
* Enable Durable Subscriptions (#9) * [Server] Durable Subscriptions (#2683) * Implement a system test for DurableSubscriptions (#2839) * Client Side Co-authored-by: ALTERNATE-DEV\Archie <archie@dreamsandadventures.ca> * make a static Method CalculateRevisedQueueSize * Adress review feedback * fix FilterRetainTests * Add MonitoredItem Id to Queues Use NodeIdDictionary where possible * first proto * fix reference server * implement store * initialize early * fix issues * Move to Reference Server & start implementing queue restore * Persist queues * Register queues in factory after restore * Add Docs * minor fixes * Test Subscription Restore & Transfer * fix Durable Subscription Test Browse * Fix conversion of extension objects * use CustomConverter for NumericRange * Adress review Feedback * Documentation improvements * Update Readme * update sampling group manager to work without session * remove launch settings |
||
|
|
ef9387b525 |
Enable Durable Subscriptions (#2978)
* Enable Durable Subscriptions (#9) * [Server] Durable Subscriptions (#2683) * Implement a system test for DurableSubscriptions (#2839) * Client Side Co-authored-by: ALTERNATE-DEV\Archie <archie@dreamsandadventures.ca> * make a static Method CalculateRevisedQueueSize * Adress review feedback * fix FilterRetainTests * Add MonitoredItem Id to Queues Use NodeIdDictionary where possible |
||
|
|
493da5d5be |
Extend ServerConfiguration & GDS Server for ECC (#2817)
* update ServerConfiguration Node to support multiple Application Certificates. Trust List Support for User Certificates & Https Certificates * GDS ready for ECC Step1 * fix test * fix build * ensure hash algorithm is set * update configurations to new format, fix minor ecc bugs * implicitly set certificate type of certificate identifier extend CertificateFactory to allow Revocation with ECC Issuer Certificate * Revert "update configurations to new format, fix minor ecc bugs" This reverts commit |
||
|
|
ee8205d248 |
Add a 1.05 update notice (#2709)
* Add a 1.05 update notice Co-authored-by: Martin Regen <mregen@microsoft.com> |
||
|
|
65f245d360 |
Remove winforms sample projects from main repo (#2626)
* remove winforms sample projects from main repo, can be found now in the samples repo! see --> https://github.com/OPCFoundation/UA-.NETStandard-Samples/tree/master/Samples |
||
|
|
dddc1cb4ab |
Make X509CertificateStore support CRLs on Windows (#2571)
This PR extends the existing X509 CertificateStore building on top of the .NET X509Store Class to support Certificate Revocation Lists (CRLs) This is important to make the X509Store viable for use as Trust and Issuer Store. Considerations: - only Works on Windows, on all other OS the "SupportsCRLs"-Property of the Store still returns false - builds on top of the Win32 API - uses source generated P-Invoke calls to call into Win32 API (https://github.com/microsoft/CsWin32) - makes use of Extension Methods of .NET X509Store Class to make the CRL Enumerate / Add / Delete accessible for the Store Implementation - guards ever call with OS Support calls and throws PlatformNotSupportedException - Behaviour of the X509CertificateStore for Linux/MacOS is unchanged - Added unit tests for the Certificate Store testing all relvant implemented crl functions - Extended GDS Tests for Integration Testing by utilizing Client & Server making second run on windows with only X509 Stores |
||
|
|
6d85d8007b |
document role based user mangement (#2528)
* document role based user mangement |
||
|
|
343f7b0fbc |
Add custom test targets to the build process, support for .NET 8. (#2410)
The goal is to reduce the overall number of builds, stop using end of life platforms in the build pipeline and to support build of future/deprecated build targets in a custom build step, which is also triggered weekly in the ci builds. In addition: - added a doc which describes the release and versioning strategy of this project - produce the Nuget packges with .NET 8 assemblies. - produce a Docker build based on a .NET 8 based container. - add custom test targets to test outdated platforms. Need a manual step to enable for VS or command line. - bug fixes: the latest move to an alternate crypto library for Bouncy Castle, which is only used for the GDS support and .NET Framework 4.6.2./netstandard2.0, revealed already a few issues which were previously not found. - only .NET8/.NET6/.NET Framework 4.8 are tested in Pull requests. Remaining builds in weekly scheduled builds. |
||
|
|
1b58ca7cc0 |
Update docs, test and dependabot/codecoverage scripts (#2041)
- Update docs to match latest. - Fix the benchmark/codecov batch files for VS2022 - Split flaky code coverage script to run per project |
||
|
|
8d0eb40206 |
Update README.md and templates (#1749)
- fix a few badges - add new template for issues and PR |
||
|
|
7d9be41cf5 |
Doc updates and fixes for .368 preview, ref server improvements (#1695)
- Doc, docs, docs updates for new 368 support - fix a missing null pointer check when ConditionRefresh is run - Ref server: move NodeManager factory API to StandardServer, decoupling the NodeManager implementation from Ref server - Ref server: add shadow mode flag, to allow to map a docker configuration in a mapped folder. - Ref server: sample batch files how to run Github CR and local docker images - Improve some misleading 'delete application certificate' log messages. |
||
|
|
a147eb55c5 |
Tracing and Logging improvements for the UA Core and libraries (#1631)
- support for Microsoft.Extensions.Logger.ILogger in Opc.Ua.Core as the new logging interface - backward compatible with config and TraceEvents - new API Utils.SetLogger allows to set ILogger - updated Console samples to demonstrate ILogger support with Serilog - support for EventSource logging - see Docs/Logging.md for more info |
||
|
|
2374155dd0 |
Code quality display reference points to old devops pipeline (#1572)
* fix code quality display on main page * Update README.md fix build status |
||
|
|
7a4a22201b |
MQTT support in PubSub library (#1376)
Implementation of MQTT support in PubSub library |
||
|
|
b3bc1615ec |
fix commands and .NET-version in README (#1331)
* fix commands and .NET-version in README * enable mandatory build for doc changes Co-authored-by: Martin Regen <mregen@microsoft.com> |
||
|
|
24e57ca983 |
Add build pipeline to upload nuget packages to a preview feed (#1107)
preview feed for Nuget.Config:
```
<configuration>
<packageSources>
<add key="opcua-preview" value="https://opcfoundation.pkgs.visualstudio.com/opcua-netstandard/_packaging/opcua-preview/nuget/v3/index.json" />
</packageSources>
</configuration>
```
|
||
|
|
783c20f487 | Doc updates for reverse connect (#1092) | ||
|
|
423bcaed26 | Update README.md (#1068) | ||
|
|
01bc720c01 |
Update badges, appveyor and create dependabot (#1029)
* Update badges * Update appveyor.yml * Create dependabot.yml |
||
|
|
cf1260ea86 |
Update readme files after repo split (#1026)
* Update README.md after repo split * Rename UA-NetStandard solution |
||
|
|
689f4f3068 |
Unit tests for IEncodeables, badges and fixes for build pipeline (#952)
- add encode/decode tests for all IEncodeables in Opc.Ua.Core. - *Breaking change*: ExtensionObject now returns the proper TypeId on decode for known encodeables. - remove Nuget.Config, it may have caused some intermittent build problems in Azure pipelines - Code coverage now 39.5% - Add a few badges for project info - fix travis build, it only supports .Net Core 2 projects. - *Breaking change*: SessionlessMessage uses the same TypeId as SessionlessInvokeRequestType which can cause problems in type factory, demoted the structure as not being IEncodeable as it is only used as a helper class in encoders/decoders. - restructure AzPipeline builds to avoid imtermittent build issues, |
||
|
|
35f0cacc41 | Fixes #391 - Quickstart Samples link in readme.md | ||
|
|
52ac5a66d8 | Update README.md | ||
|
|
44a2a93472 | Update README.md | ||
|
|
975759ca86 | Update README.md | ||
|
|
8d96971df2 | Update README with Mono support | ||
|
|
02d73c5e76 |
Update README and references to compliance testing (#354)
* Update README and references to compliance test * Update Reference Server docs * Update latest info how to run the samples |
||
|
|
9bbca71841 | Update README.md |