8 Commits
Author SHA1 Message Date
5899354441 Clarify documentation, migration guidance, and SDK selection (#4823)
# Description

Remove stock phrasing and correct misleading or inconsistent technical
documentation. The initial two passes covered 367 Markdown files,
followed by an interactive review of 169 editorial flags. The U5
interpretation originally deferred in that review has since received the
upstream Mantis answer recorded by #4843; remaining server/spec
follow-ups stay explicit in the CTT log.

- Correct package responsibilities, migration steps, API names,
ownership, protocol descriptions and links. Keep bundled migration
guides synchronized. Preserve historical conclusions as historical
records with separate re-check notes.
- Provide at least two rendered TOC levels throughout `docs/`. Convert
appropriate architecture figures to Mermaid without changing code
examples, transcripts or historical quotations.
- Address the October 19 feedback with concrete setup/migration
instructions, corrected certificate paths and PubSub names, shorter
repeated caveats, consistent XML comments, and a full review of the
Concepts guide. Retain #4839's API corrections.
- Integrate #4841's runtime/performance guidance and review
incoming/recent prose. Preserve dated measurements rather than claiming
new benchmark runs; clarify allocation ratios, startup-only processor
settings, JIT warm-up and the benchmark commit's scope. Missing raw
measurement artifacts and repeat ranges remain tracked by #4842.
- Merge master through `e8c5d800a` in `32c6394b7`. Resolve the CTT log
conflict using the updated Mantis states and OPC answers, retaining
historical framing and re-checks for C28/C32/C43.
- Address the latest naming/index feedback in `adb5bf693`: rename the
guides to `docs/WotCon.md` and `docs/WotNodeSetConversion.md`; move the
provider and DataType-declaration contracts into the conversion guide
and remove `docs/WoT.md`. Rename `samples/DI` to `samples/Di` and
`samples/Isa95` to `samples/ISA95`. Add content-index READMEs to Di,
FluentApi, ISA95, Lds and PubSub; update solution entries, project
references, Docker/CI paths, test fixture paths and documentation links
with exact casing.

**WoT guide scope.** This branch retains the detailed conversion guide
that #4758 had replaced with an anchor stub. Conversion limits, verified
document sets, preservation rules and provider ownership now live
together in `WotNodeSetConversion.md`; binding/runtime guidance remains
in `WotBindings.md` and `WotCon.md`. The design-rationale/test-ledger
prose was shortened, the oversized argument-table cell moved into its
section, and existing anchors preserved.

**SDK policy is a separate, explicit user-directed change.**
`global.json` sets a stable minimum of `10.0.400`, `rollForward:
latestMajor`, and `allowPrerelease: false`. Newer major SDKs are
permitted; CI's explicit SDK pins are unchanged. README, GettingStarted,
DeveloperGuide and Recycling instructions reflect the possible analyzer
differences. The image-contract regression fix accepts compatible stable
SDK images instead of requiring exact equality with the floor.

The latest four rename/index threads are resolved after publication.
Three older threads remain open: universal obsolete-removal timing,
unconditional nonce-strength wording, and a blanket Vision NativeAOT
claim. Existing implementation/evidence constraints are explained in
their inline replies. The two-level TOCs remain as requested.

## Validation

- `git diff --check` and staged diff checks: passed. The untouched
upstream AutoID NodeSet retains its provenance bytes, including two
existing license-header trailing spaces.
- `.\.azurepipelines\validate-migration-plugin.ps1`: passed; 15 bundled
migration documents and manifests synchronized.
- Documentation/path validation: **101** documents retain two rendered
TOC levels (**2,228** entries), with idempotent formatting. **52** new
relative links and their anchors passed exact-case checks. No tracked
references to the old guide/sample paths remain. Both relocated
provider-contract sections are preserved in the destination.
- `dotnet sln UA.slnx list`: passed; shows the renamed Di/ISA95 projects
and the existing FluentApi/Lds/PubSub projects. Updated solution and
pump ProjectReference paths also passed exact-case existence checks.
- Latest focused commands, run from the repository root:

```powershell
# Passed: 61 channel send-order and reconnect tests.
dotnet test tests\Opc.Ua.Core.Tests\Opc.Ua.Core.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~UaSCBinaryChannelSendOrderTests|FullyQualifiedName~TcpReconnectKeyAgreementTests' --verbosity minimal

# Passed: 5 pump datasheet tests; builds the renamed pump sample.
dotnet test tests\Opc.Ua.Di.Tests\Opc.Ua.Di.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~PumpDatasheetConformanceTests' --verbosity minimal

# Passed: 4 repository NodeSet path/round-trip tests, including the moved pump models.
dotnet test tests\Opc.Ua.Types.Tests\Opc.Ua.Types.Tests.csproj -p:CustomTestTarget=net10.0 --filter 'FullyQualifiedName~RepositorySourceSurvivesSerializedLinkedPartitionsAsync' --verbosity minimal

# Passed: build, zero warnings/errors.
dotnet build samples\ISA95\MinimalIsa95Server\MinimalIsa95Server.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal

# Passed: dependent sample-test project build, zero warnings/errors.
dotnet build tests\Opc.Ua.WotCon.Samples.Tests\Opc.Ua.WotCon.Samples.Tests.csproj -p:CustomTestTarget=net10.0 -f net10.0 --verbosity minimal
```

Earlier focused integration checks passed 2,620 tests across AutoID,
event queues/scheduling, source generation, MCP/package profiles and
WoT. Both reviewed Mermaid diagrams rendered locally without network
access. These checks are not a full solution run.

The full solution has **not** been rerun for this review. Earlier
`dotnet test UA.slnx -p:CustomTestTarget=net10.0 --verbosity quiet` did
not pass: three AOT projects rejected VSTest and 20 Vision tests failed
during graphics initialization with `OutOfMemoryException`. Remaining
long-running tests were stopped at the user's request. A focused Vision
rerun passed 558 tests and reproduced those 20 failures. The earlier
`dotnet test UA.slnx -p:CustomTestTarget=net48 --verbosity quiet` was
blocked by the former SDK pin and was not retried after the policy
change. The user approved proceeding with those limitations documented;
current remote CI is not claimed green.

## Related Issues

- Follow-up #4842 tracks the requested 2.0.2+ benchmark/tuning and
diagnostic-tool documentation refresh. #4841 supplies documentation and
earlier results, not all newly requested measurements.
- #4758 explains the conversion guide's earlier consolidation; the
current provider contracts are now consolidated into
`WotNodeSetConversion.md`.
- #4839 supplies the reviewed API corrections; #4843 supplies the
updated CTT issue record.
- The original prose cleanup and SDK policy were user-requested without
a source issue. No new runtime architecture is introduced by these
documentation edits.

## 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.
- [ ] 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.

---------

Co-authored-by: Roman Ettlinger <romanett98@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 10:10:55 +02:00
0f85a9f719 AutoID: the OPC UA for AutoID Devices companion SDK (OPC 30010) (#4825)
# Description

This completes the AutoID contribution proposed in #4824: the model
package for [OPC 30010 OPC UA for AutoID
Devices](https://reference.opcfoundation.org/AutoID/v101/docs/) (1.01)
plus the server, the client, an MCP profile and a sample pair, in one
PR. It follows the newest companion pattern (GMS #4765, MachineTool,
CranesHoists, MachineVision).

## Commits

1. `fix(sourcegen)`: a structure property whose DataType a subtype
refines is redeclared with its own field (the NodeSet needs it;
unchanged from the first slice).
2. `feat(autoid)` model package `Opc.Ua.AutoID` (unchanged from the
first slice). Adds only `AutoIdReaderKind`.
3. `feat(autoid)` server package `Opc.Ua.AutoID.Server`.
4. `feat(autoid)` client package `Opc.Ua.AutoID.Client`.
5. `feat(autoid)` MCP profile `Opc.Ua.Mcp.AutoID`.
6. `feat(autoid)` samples `samples/AutoID`.
7. `build(autoid)` solution, package list and package validation tests.
8. `fix(autoid)` x3, `docs(autoid)` and `test(autoid)`: review fixes,
net48 compatibility, documentation, the native AOT test.
9. `fix(autoid)` x3 and `feat(autoid-client)`: `GetLocation` location
types, the `Client` of access events, the default scan cycle of the
simulated readers, and NodeIds and methods in the device snapshot
(details below).
10. `feat(autoid)`, `test(autoid)`, `docs(autoid)` (`afdd7858b`,
`22bc3b26e`, `ed25fe4ae`, on top of the master merge `1c7948f65`):
AutoID composes into an existing Device Integration owner like Pumps and
Scales (review feedback; see "Composition with other DI-based
companions").
11. `1458af9e9` (marcschier): the `AutoIdReadOptions` remarks state that
`StatusOnly` limits the values read and that each call still browses
(Copilot review).
12. `fix(autoid)` `3ef2f1507`: `GetDeviceLocation` answers with the same
status codes as `GetLocation`; `feat(autoid-mcp)` `f6405152f`:
`autoid_read_device` shows the new snapshot NodeIds and methods (details
below).

## Server: `Opc.Ua.AutoID.Server`

- Reader devices (RFID, OCR, optical, optical verifier, RTLS) on the DI
device model, created by a fluent builder (`AutoIdRuntime.Reader(name,
provider)`) and hosted with `AddAutoId` / `ConfigureAutoId` on
`IServiceCollection`.
- Hardware is behind provider interfaces: `IAutoIdReaderProvider`,
`IAutoIdScanProvider` (Scan), `IAutoIdAsyncScanProvider`
(ScanStart/ScanStop), `IAutoIdRfidProvider` (ReadTag, WriteTag,
WriteTagID, KillTag, LockTag, SetTagPassword, each opt-in by flag),
`IAutoIdRtlsProvider`. Providers push scans, presence, log entries and
access events through `IAutoIdReaderSink`. In-memory providers (an EPC
Gen2 tag model with banks, passwords, locks and kill) are included for
tests and samples.
- Optional children (methods, `LastScanData`, `DeviceLocation`,
`AntennaNames`, location methods) exist only when the provider supports
them. Facet profiles and conformance units are advertised only for what
is implemented (`IConformanceContributor`); the Full facet is withheld.
- `GetLocation` (OPC 30010 6.6.3.2) returns the location in the
requested `LocationType`. An unsupported type is `Bad_InvalidArgument`,
an unknown object `Bad_NotFound`, and a supported type the object's
location is not available in `Bad_InvalidState` (6.6.3.2: "not available
or not allowed e.g. due to special configuration"); a provider result of
another type never reaches the client. `InMemoryRtlsProvider` takes an
optional location converter, for example from the configured local
coordinate system (9.3.4) into WGS84.
- `GetDeviceLocation` (6.1.3.7) uses the same mapping: an undefined
location type is `Bad_InvalidArgument`, a device location that is not
available in the requested type `Bad_InvalidState` (it answered
`Bad_NotSupported` before). 6.1.3.7 lists only "Standard OPC UA status
codes defined for the Call Service in OPC 10000-4"; there
`Bad_NotSupported` means "The Method is not supported for the Object
instance", which is wrong for a published method, and OPC 10000-4
5.12.2.4 allows reusing an existing status code with the same canonical
meaning, so the method follows 6.6.3.2.
- Access events (7.10, 7.11): `Client` is "the name of the client
application which was the originator of the command", so it is the
application name of the session's client description (else its
application URI), not the user identity. The results of a `ScanStart`
scan name the client that started it; scans the device reports on its
own name the device.
- The simulated readers default to one scan cycle per second
(`CycleInterval`, configurable per provider; the length of a cycle is
device specific in OPC 30010), so an unbounded `ScanStart` raises at
most one scan event per second.
- Behaviour: one scan at a time with a generation counter;
ScanStart/ScanStop serialised; a scan started by a session stops when
that session closes; the maximum scan duration, tag read length and
result count are limited by `AutoIdServerOptions`; device status changes
and events are raised outside locks; registration is rolled back on
failure; readers, providers and the runtime are disposed in order and
in-flight operations are drained.

## Composition with other DI-based companions

A server has one Device Integration owner, and AutoID now uses the
existing harmonized composition instead of requiring its own node
manager:

- `AutoIdMachineryModelProvider` adds the AutoID model to the Machinery
owner's model-provider pipeline (dependency-ordered, `Order` 200) and
keeps the DI nodes the owner loaded, so DI and `DeviceSet` exist once.
- `AddAutoIdToMachinery` (after `AddMachinery`) registers it;
`ConfigureAutoIdFor<TNodeManager>` creates readers on any
`DiNodeManager` owner, successive delegates share one `AutoIdRuntime`;
`owner.CreateAutoIdRuntime()` is the path without DI hosting. The owner
disposes the runtime on shutdown.
- Readers are registered in the owner below its `DeviceSet`, NodeIds in
the owner's instance namespace (`AutoIdRuntime.InstanceNamespaceIndex`),
not in the `Machines` folder; the AutoID conformance units and facets
are published by the owner next to the DI and Machinery ones.
- `AddAutoId` remains for the "AutoID is the only DI-based companion"
case and is built from the same provider. Next to another DI owner it is
still rejected; the message names the composition API. (Bypassing that
check gives two `DeviceSet` copies; the server routes `DeviceSet` to the
first owner and readers in the second are not browsable, checked
locally.)
- `Opc.Ua.AutoID.Server` now references `Opc.Ua.Machinery.Server` (a
stable package), as Vision does; a stand-alone server still loads only
DI and AutoID.

## Client: `Opc.Ua.AutoID.Client`

Reader discovery (a failed browse propagates), typed calls for every
published method, a device snapshot with documented `AutoIdReadOptions`,
and an async event stream (`ObserveScan/Access/Presence/LogEntries`) on
`IStreamingSubscription`. Cancellation is honoured throughout.
`AddAutoIdClient` registers it for DI. Because most children of a device
are optional (6.1, 6.5.3, 6.6.3), the snapshot also carries the NodeIds
of `DeviceStatus`, `LastScanData`, `LastScanTimestamp` and
`DeviceLocation` and the browse names of the published methods
(`Methods`, `HasMethod`), taken from the same browse whatever the read
options, so a client can monitor the device and knows what it may call
without browsing again.

## MCP profile: `Opc.Ua.Mcp.AutoID`

`McpToolProfile.AutoId` with nine tools: list readers, read device,
scan, scan start/stop, observe scan events, read tag, get supported
location types, get location. `autoid_read_device` also returns
`deviceStatusId`, `lastScanDataId`, `lastScanTimestampId` and
`deviceLocationId` (NodeId strings, null when the device does not
publish the variable) and `methods` (the browse names of the published
methods), built explicitly without reflection. Registered in the host
builder, the profile switches of the other profiles,
`Test-McpGrade.ps1`, `McpREADME.md` and `.mcp/server.json`.

## Samples

`samples/AutoID/AutoIdServer` (a dock-door reader with a simulated
pallet) and `AutoIdClient` with a README and
`Test-AutoIdSamplePair.ps1`. The default port is not a common one; tests
use port 0.

## Other changes

- `Opc.Ua.Di.Server` gets `InternalsVisibleTo` for
`Opc.Ua.AutoID.Server` (`RegisterRootNotifierAsync`).
- `UA.slnx`, `.azurepipelines/expected-packages.txt`,
`PackageVersionPolicyTests`, `NuGetPackageSetValidationTests`.
- Docs: `docs/AutoID.md`, `docs/McpServer.md`, `docs/Profiles.md`,
`docs/Samples.md`, `docs/DeveloperGuide.md`, `docs/README.md`,
`docs/WhatsNewIn2.0.md`, `README.md`, NugetREADMEs. The "model package
only" statements are removed.
- All packages follow the root version (no preview override).

## Specification notes and questions for the maintainers

- The model is 1.01, published 2020-06-18 (the NodeSet header). OPC
30010 6.1.3.16 prescribes the `AutoIdModelVersion` string `"1.00"`,
which is what the NodeSet carries and what the server publishes.
- **Question:** the NodeSet's optical verifier `Scan` method returns
`WGS84Coordinate`, which looks like a model error. It is not published,
and providers that implement `IAutoIdScanProvider` for a verifier are
rejected. Is that the behaviour you want?
- The stack answers a call to an unpublished optional method with
`BadNotImplemented`.

## Design choices reviewers may want changed

- `Opc.Ua.Di.Server` gets `InternalsVisibleTo` for
`Opc.Ua.AutoID.Server`, so the server can call
`RegisterRootNotifierAsync`.
- `AutoIdReaderKind` (and `AutoIdReaderKinds.TypeIdOf`) lives in the
model package `Opc.Ua.AutoID`, not in the server package, so the client
and the MCP tools share it.
- The MCP profile is read-only/safe: no tools that write, kill or lock
tags.

## Tests

`tests/Opc.Ua.AutoID.Tests` has 306 tests (116 of the model slice, 190
new): reader lifecycle, scan semantics, tag access handlers, limits,
conformance, in-memory providers, hosting, and over-the-wire
client/server tests with result codes, events, concurrency and dispose.
`tests/Opc.Ua.Tools.Tests` has 13 AutoID MCP tests plus the updated
profile-count tests. New behaviour tests fail without the code (for
example a provider's own cancellation, a synchronous scan while a scan
stop is in progress; for the last round: `GetLocation` of a supported
type the object is not located in answered `Bad_NotFound`, access events
named the user "Anonymous" and attributed `ScanStart` results to the
device, and an unbounded `ScanStart` on a default simulated reader
reported 20 times in 400 ms). Composition: `AutoIdCompositionTests`
(stock Machinery owner, providers out of order, DI loaded once, instance
ownership, rollback, cancellation, runtime disposal, an
application-owned DI owner, a second DI owner rejected) and
`AutoIdMachineryCompositionEndToEndTests` (Machinery + Pumps + AutoID in
one hosted server, read back by the AutoID, Machinery and Pumps clients,
including a scan and the published profiles and conformance units).
Mutating the change (no lifetime registration, the DI instance namespace
for browse names, the old ownership message) fails three of them. The
sample pair script passes. `tests/Opc.Ua.Aot.Tests.Mcp` has an AutoID
profile test (`AutoIdMcpAotTests`); the project was published with
Native AOT on osx-arm64 and all 64 tests passed. Publishing it also
found two `JsonArray.Add<T>` uses in the MCP tools (IL2026/IL3050), now
fixed.

## Verification (local, macOS arm64, SDK pinned in `global.json`)

The branch contains the master merges `fc556be7e`, `1c7948f65` and
`af7feb886`; the three composition commits sit between the last two (the
latest merge touches no AutoID, DI or Machinery file). `1458af9e9`
(marcschier) and the two commits of the last round follow `af7feb886`.

- **Last round (`3ef2f1507`, `f6405152f`).** Builds with `-m:2
-nodeReuse:false -p:UseSharedCompilation=false`: `Opc.Ua.AutoID.Tests`
on all target frameworks and with `-p:CustomTestTarget=net48`,
`Opc.Ua.Mcp.AutoID` on net8.0/net9.0/net10.0, `Opc.Ua.Tools.Tests` on
net10.0: 0 warnings, 0 errors. `Opc.Ua.AutoID.Tests` 306/306 on net10.0;
`Opc.Ua.Tools.Tests` AutoID tests 60/60, and the profile, catalogue,
composition and package tests that reference AutoID (2241) pass. The
changed tests fail without the code: `GetDeviceLocation` answered
`Bad_NotSupported` for both cases, and `autoid_read_device` had no
`deviceStatusId`. `tests/Opc.Ua.Aot.Tests.Mcp` published with Native AOT
on osx-arm64 (no IL2026/IL3050 warning): 64/64 passed. `dotnet format`
whitespace, style and analyzers `--verify-no-changes` on the changed
files: no finding on a changed line (line endings aside, which the LF
checkout on macOS reports for every file).

- **Build (composition round).** `Opc.Ua.AutoID.Server` on all target
frameworks, `Opc.Ua.AutoID.Tests` on net10.0 and with
`-p:CustomTestTarget=net48`, and the AutoID server sample: 0 warnings, 0
errors. MCP tools and JSON unchanged, so the native AOT test was not
republished.
- **Build (earlier rounds).** The AutoID packages and
`Opc.Ua.AutoID.Tests` on net10.0 and, with `-p:CustomTestTarget=net48`,
on net48/net8.0/net9.0/net10.0; `Opc.Ua.Tools.Tests` and
`Opc.Ua.SourceGeneration.Core.Tests` on net10.0 (`-m:2 -nodeReuse:false
-p:UseSharedCompilation=false`): 0 warnings, 0 errors.
- **Tests.** `Opc.Ua.AutoID.Tests` 306/306 on net10.0 (composition
round; earlier round 288/288); `Opc.Ua.Tools.Tests` (AutoID, host
builder, profile and package tests) 2438/2438 on net10.0;
`Opc.Ua.SourceGeneration.Core.Tests` 4358 passed, 8 skipped, after the
master merge touched `NodeStateGenerator.cs`. net48 was built, not run
(no Mono on macOS); the fork CI runs it. The MCP tools and their JSON
are unchanged in the last round, so the native AOT test was not
republished.
- **Coverage.** Composition round: the local coverlet collector recorded
zero hits for every module in this environment (also for unchanged
code), so no new local number; the new paths are exercised by the 18
composition tests listed above and the fork CI uploads coverage. Earlier
round: cobertura from the AutoID tests: `Opc.Ua.AutoID.Server` 92.0 %
lines / 86.8 % branches, `Opc.Ua.AutoID.Client` 89.9 % / 80 %; 110 of
the 112 coverable changed lines of the last round are covered (the two
missed lines skip a browse result without NodeId or name). The generated
code of the model package is excluded as in the other model packages.
- **Format.** `dotnet format` whitespace, style and analyzers
(`--severity info`) `--verify-no-changes` on the changed files: clean.

Fork CI (full matrix including macOS): [run
39162077465](https://github.com/biancode/UA-.NETStandard/actions/runs/39162077465)
on `f6405152f`: green. (Previous round: run 39130154771 on `ed25fe4ae`,
90/90 jobs green; the upstream checks on the merge `af7feb886` green,
108 passed, 5 skipped.)
CodeQL: [run
39162079826](https://github.com/biancode/UA-.NETStandard/actions/runs/39162079826)
on `f6405152f`: green, no alert in a file this PR touches. (Previous
round: run 39130156598 on `ed25fe4ae`, green.)

## Related Issues

- Part of #4824 (tracking issue: AutoID companion SDK).

## 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. (The
solution builds on all TFMs; the AutoID test projects ran on net10.0
locally, net48 runs in the fork CI.)
- [x] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.

---------

Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-10-19 12:30:33 +02:00
Marc Schier 87012a147f Machinery: add bounded inspection and result transfers (#4804)
# Description

- Add opt-in bounded browsing and strict reads with original source,
status, timestamp, and structured-value evidence. Preserve the existing
tolerant constructors and convenience projections, and wire options
through session extensions, factories, and hosting registration.
- Add selected-result streaming downloads with byte/request limits and
independent cleanup, using `TemporaryFileTransferClient.CopyToAsync`
from the client/server predecessor. Retain the detailed transfer
regressions in the Machinery test layer.
- Expose genuinely null unpublished result slots, carry the two
Machinery sample updates, and include the complete Machinery guide and
package README changes.

## Related Issues

- Related tracking issue: #4461; this extraction does not close it.
- Frozen source PR: #4462; this extraction does not close or modify it.
- Immediate predecessor: #4803 (`marcschier-extract-amb-changes`). The
earlier dependencies are client/server #4802 and core/types #4801.

## 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.
2026-10-16 12:24:35 +02:00
Marc Schier 0374e19121 Add MachineTool, WMTP, GMS and CranesHoists companions (#4765)
# Description

_Describe the changes here to communicate to the maintainers why they
should accept this pull request. By default - this will become the
Commit message after merging and thus define history._

Add provider-backed MachineTool, bundled WMTP, GMS and CranesHoists
companions using the existing Machinery/MachineVision fluent, DI and
typed-client approach. **The PR is open/non-draft by user choice.
Current head `ecc4a7d6ca3e8004cf28ab54da605da302d026bf` is mergeable and
has 108 successful checks, six intentionally skipped checks, zero
failures and zero pending checks. All five primary workflows completed
successfully at that exact head.**

| Family | Released specification/model | Packaging |
| --- | --- | --- |
| MachineTool | OPC 40501-1, 1.02.0 | Model, Server, Client and MCP;
normal root versions |
| WMTP | OPC 40505, 1.0.0 | Bundled in MachineTool; distinct CLR/wire
namespace and independent WMTP-only hosting |
| GMS | OPC 40210 reference 1.0.1, normative model 1.0.0 | Model,
Server, Client and MCP; normal root versions |
| CranesHoists | OPC 40020-1, 1.00 | Model, Server, Client and MCP; all
four plus Debug variants forced preview |

There are **12 new packable projects**. No separate WMTP
package/profile/guide, root version bump, tag, release or package
publication is included. Mandatory instance structure is the default;
selected optional features require explicit providers. The
implementation reuses Machinery results/jobs/files/ProcessValues and
Robotics topology, including retained MachineTool Production, defined
GMS jobs/parts, modern ISA95/Machinery jobs, shared-owner hosting and
isolated instances alongside MachineVision.

Runtime and integration fixes preserve raw quality/timestamps, coherent
acquisition, namespace ownership, accepted-configuration state,
borrowed-resource lifetime, session/node/file cleanup, terminal-value
publication, event propagation and endpoint-aware capability
publication. Firmware/licence transfer means provider acceptance, not
installation/activation. Cancellation does not silently abort another
caller's operation. The later upstream MCP expansion is integrated
without losing either family set or its default-off privileged/live
execution policy.

### Conformance and scope

Canonical model inputs are pinned to UA-Nodeset revision
`96a08945425d0295edb3607d2a38f6841ef44046`.

**GMS upstream exception:** [Annex
A.1](https://reference.opcfoundation.org/specs/OPC-40210/annex-a) makes
the released NodeSet normative, but §12.3.2.7 requires undefined
`GMS_ProductionStatisticsType`. Inherited statistics and defined
jobs/parts are implemented without inventing a standard type/NodeId; the
complete GMS Production facet is withheld.

The requirement ledger records **102 positively evidenced CU rows, 96
directly negative-tested rows and six explicit negative-evidence
limitations**. All 648 test-class/name/TRX links were independently
resolved to passing cases. The six limitations concern Stacklight,
retained legacy PowerOnDuration, Machinery PowerOnDuration, WorkingUnit,
Obligation and MaintenanceMode. They are not claims that every
invalid-input or hardware-control behavior was tested. The undefined GMS
requirement is outside the 102 defined CUs. No external certification
claim is made.

Retained baseline complete-assembly line coverage, with generated code
retained and no source exclusions:

| Family | Model | Server | Client |
| --- | ---: | ---: | ---: |
| MachineTool/WMTP | 83.80% | 91.98% | 84.74% |
| GMS | 95.51% | 90.63% | 88.38% |
| CranesHoists | 92.76% | 94.45% | 92.24% |

These nine measurements predate the later focused review fixes and
merges; they are not relabelled as new `ecc4a7d6c` percentages. The
current head's separate CI code-coverage check succeeded. No new
exclusions or threshold relaxations were introduced.

## Related Issues

- Fixes
[#4764](https://github.com/OPCFoundation/UA-.NETStandard/issues/4764),
the implementation/design and acceptance tracker.

## 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.
- [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.
- [ ] 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.
- [x] I have addressed **all** PR feedback received.
2026-10-15 15:15:46 +02:00
Marc Schier be50f02700 Add additional companion spec MCP tool profiles (#4731)
# Description

Expose existing stack APIs through **337 additional domain MCP tools**:
114 industrial-companion tools, 35 WoT/xRegistry/document tools, and 188
higher-level stack-workflow tools. The fully enabled catalogue contains
**492 tools**; profile selection is not an execution grant.

The packages now follow the reviewed structure:
**`Opc.Ua.Mcp.Workflows`** contains roles/users, files, alarms and
history; **`Opc.Ua.Mcp.WotCon`** contains management/documents and
independently registered live WoT execution. GDS and schema tools remain
separate packages. The executable composes **18 libraries**.

| Workflow | Added domain tools | Profiles |
| --- | ---: | --- |
| GDS directory, certificates, reference-only credentials/tokens and
Part 21 onboarding | 46 | `gds`, `certificates`, `credentials`,
`onboarding` |
| Roles/users, regular and temporary files, alarms/conditions and
historical access | 74 | `roles`, `users`, `files`, `alarms`, `history`
|
| Bounded runtime types, XSD/BSD/UA JSON, PubSub and REST OpenAPI
artifacts | 6 | `schemas` |
| Named PubSub transports, remote configuration, declarative
adapters/bridges and SKS | 52 | `pubsub-runtime`,
`pubsub-configuration`, `pubsub-bridge`, `pubsub-security` |
| Live HTTP(S), MQTT, Modbus TCP and OPC UA WoT forms | 10 |
`wot-runtime` |

- Reuse typed clients, generated proxies, SDK transports, schema
providers and injected services rather than introducing another session
manager or protocol engine.
- Require host enablement plus exact caller/session/secret/network
grants. Protected generic Call/Write/node-management/history/file paths
enforce the same policy.
- Bind classifications, dispatches, file handles and historical cursors
to actual UA generations. Recovery cannot retarget an old handle or
authorized mutation. Preserve acknowledged results after late
cancellation; do not imply atomicity or retry business commands.
- Use opaque secret-store references, explicit writable-store
registration, issuer/resource/caller binding and reserved capacity
before issuance. Report remote success/local persistence failure as
partial completion; redact provider-controlled diagnostic text.
- Bound new named runtimes to eight per host, two per caller,
five-minute idle expiry and a thirty-minute absolute lease by default.
Pending/closing resources consume quota; failed cleanup remains visible.
Credential invalidation and session recovery close dependent runtimes.
- Keep document planning separate from execution, artifacts
root-confined and real JSON inputs typed. Preserve old profile ordinals
while widening the set to 64 bits. CI grades fully enabled privileged
catalogues rather than connection-only views.

**Limitations:** live Kafka remains explicitly unavailable because the
backend cannot enforce policy on broker-discovered connections;
configuration/schema/capability inspection stays available. The latest
public Dekaf v1.23.5 was also checked and its DNS injection remains
internal. No private-API workaround or bootstrap-only security claim was
added. Full legacy Core registration retains its documented
JSON-reflection limitation: native tests isolate its four connection
descriptors and validate the expanded production registrations, not
whole-host NativeAOT support.

## Related Issues

- Fixes #4730.

## 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.
- [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.
- [ ] 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.
- [x] I have addressed **all** PR feedback received.
2026-10-14 00:14:05 +02:00
Marc Schier 1bb4bdca1d Add Machine Vision Parts 1 and 2 (#4733)
# Description

Implement the released [OPC
40100-1](https://reference.opcfoundation.org/specs/OPC-40100-1) and [OPC
40100-2](https://reference.opcfoundation.org/specs/OPC-40100-2)
specifications with generated models, provider-backed runtimes, typed
clients, MCP workflows, and DI/Machinery integration.

- **Part 1 stays in `Opc.Ua.Vision`** and its client/server packages
alongside the distinct Perception model. Provide control/acquisition
state machines, jobs and synchronized steps,
recipe/product/configuration/result management, file transfers,
diagnostics, events, and conditions.
- **Part 2 is in `Opc.Ua.MachineVision`, `.Client`, and `.Server`.**
Provide asset categories, identification, image-handling aspects,
health/maintenance data, topology, and transactional updates. The base
Vision assemblies do not reference these optional packages; the Part 2
wire model does not depend on Part 1.
- Preserve the release-pinned model bytes, wire namespaces and NodeIds.
Perception types are in `Opc.Ua.Vision.Perception`; the AMCM generated
namespace remains `Opc.Ua.Vision.MachineVision.AMCM`, and asset
client/server extension namespaces remain `Opc.Ua.Vision.Client` and
`Opc.Ua.Vision.Server`.
- Use `AddOpcUaMachineVision` and `AddOpcUaMachineVisionAMCM` for model
loading and factory registration. Each has node-collection,
encodeable-factory-builder and node-state-factory-builder overloads.
These short entry points delegate to the generated implementations;
existing namespace-derived generated names remain available for
compatibility.
- Support multiple independent named systems/assets under one
namespace-owning manager. Reuse injectable providers, file binding,
bounded memory stores, optional transactional `IFileSystemProvider`
persistence, state machines, telemetry and conformance infrastructure.
- Keep one `docs/Vision.md` guide, titled `Machine Vision (OPC 40100-1
and OPC 40100-2)`, and one `Opc.Ua.Mcp.Vision` package. Include
deterministic server/client samples.
- Expose 17 Machine Vision MCP tools, including capabilities/status,
bounded relationships and observation, complete small-file
uploads/downloads, management, explicit control, conditions, and chunked
transfers. Catalogue counts: 39 Vision-specific, 43 with connection
tools, 81 Vision+Robotics, 155 Full. Every Machine Vision wire parameter
has a schema description.

**Compatibility and limits:** `AddVision()` remains Perception-only
unless released parts are selected. Standalone Part 2 uses
`AddVision().AddMachineVision()`; Machinery composition uses
`AddVisionToMachinery()`. Existing tool defaults and command signatures
are preserved. Observation owns and cleans up its subscription: 5
seconds/50 notifications by default, capped at 60 seconds/100
notifications. Complete-file tools are capped at 1 MiB; larger files use
chunked transfers. Domain Error values remain distinct from UA service
failures.

**Versioning:** the seven Vision/MachineVision/MCP packages and their
Debug variants follow the repository root version instead of being
forced to preview. Vision.OpenUsd and its descendants remain
preview-only. Development builds still inherit the root prerelease
version. No release version, tag, release branch, publication policy, or
feed promotion is changed.

**Review actions:** all fourteen review threads have action/evidence
replies and are resolved. Changes include the unified guide, Perception
namespace, separate Part 2 packages and final naming, multi-system
ownership explanation, interrupted-write cleanup, observer failure
isolation, complete MCP parameter descriptions, and explicit package
inventory. The preview-layout migration topic and the requested
ReleaseProcess caveat were removed. The test Publish collector now
retries recognized transient failures within one cancellation budget and
propagates other exceptions. Commit 69b1f5db9 applies the requested
short loader names and renames standalone hosting to `AddMachineVision`,
with callers, tests and documentation updated. The intervening master
merge is preserved.

### Verification

**Latest API naming changes, verified locally:**

| Scope | Result |
| --- | --- |
| Full Vision suite, net10 | 572 passed, 1 existing skip, 0 failed |
| Full Vision suite, net48 | 515 passed, 0 skipped, 0 failed |
| Final loader/hosting cases after cross-TFM null-guard adjustment | 9
passed, 0 failed, 0 skipped |
| Machine Vision MCP and profile cases | 198 passed, 0 failed, 0 skipped
|
| Main and MCP AOT test-project compilation | Both built with 0
warnings/errors |
| MCP AOT companion, managed execution | 5 passed, 0 failed, 0 skipped;
not claimed as native execution |

`ModelLoaderNamesRegisterTypedFactories`,
`ModelLoaderNamesRejectNullArguments`, and
`GeneratedModelsLoadTogetherAndCreateTypedInstances` cover all six
loader overloads, fluent return identities, typed instances, namespace
coexistence and null guards. Existing standalone Part 2 tests verify
idempotent registration, one-owner enforcement and null handling through
`AddMachineVision`. No source-generator implementation, CLR namespace,
wire identity or model payload was changed for these naming updates.

The current 69b1f5db9 revision is running
[CI](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38165826937),
[package
validation](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38165823359),
[MCP
usability](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38165826874),
[CodeQL](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38165826906),
and [Images
CI](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38165826884).
These runs are not claimed as completed results.

**Earlier completed verification:** all seven default-TFM package
archives were inspected after the package rename: exact IDs, 27 clean
DLLs, correct dependencies/readmes/licenses, and unchanged pinned model
hashes. [CI on
c75ec4fa6](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/38142519760)
passed, including the formerly failing Linux batch and required summary.
Its unchanged coverage gate passed at **88.56% lines, 78.96% branches,
84.28% patch** against the 75% patch floor. The later
bounded-Publish-helper regressions reproduced 13 failures before the fix
and passed afterward; the full mainline triggering fixture passed 67
tests on each of net10/net48. These are historical results, not current
full-matrix results.

<details>
<summary>Executed API naming validation commands and outcomes</summary>

Run from the worktree root using the established artifact trees.
Dependency builds were performed before the no-build full net10 run; the
focused loader/hosting selection was rebuilt and rerun after the final
null-guard adjustment.

```powershell
$files = 'C:\Users\mschier\.copilot\session-state\deb2ba10-b0e9-4a44-8783-8331bc850814\files'
$env:DOTNET_CLI_USE_MSBUILD_SERVER = '0'
$env:TEMP = 'C:\Temp\mv-deb2\followup'
$env:TMP = $env:TEMP

# Passed: 572; Failed: 0; Skipped: 1.
dotnet test tests\Opc.Ua.Vision.Tests\Opc.Ua.Vision.Tests.csproj -c Release -f net10.0 --no-build --no-restore --artifacts-path artifacts\vision-followup -p:CustomTestTarget=net10.0 -p:UseSharedCompilation=false -m:1 -nr:false --logger 'console;verbosity=minimal' --logger trx --results-directory "$files\vision-api-names-net10" -- 'NUnit.WorkDirectory=C:\Temp\mv-deb2\followup'

# Passed: 9; Failed: 0; Skipped: 0, on the final source.
dotnet test tests\Opc.Ua.Vision.Tests\Opc.Ua.Vision.Tests.csproj -c Release -f net10.0 --no-restore --artifacts-path artifacts\vision-followup -p:CustomTestTarget=net10.0 -p:UseSharedCompilation=false -m:1 -nr:false --filter 'FullyQualifiedName~MachineVisionModelTests|FullyQualifiedName~StandalonePartTwoRegistrationUsesOneOwnerAndOneModelProvider|FullyQualifiedName~StandalonePartTwoRequiresRegistrationAndRejectsCompetingOwnership' --logger 'console;verbosity=minimal' --logger trx --results-directory "$files\vision-api-names-final-model" -- 'NUnit.WorkDirectory=C:\Temp\mv-deb2\followup'

# Passed: 198; Failed: 0; Skipped: 0.
dotnet test tests\Opc.Ua.Tools.Tests\Opc.Ua.Tools.Tests.csproj -c Release -f net10.0 --no-restore --artifacts-path artifacts\vision-followup -p:CustomTestTarget=net10.0 -p:UseSharedCompilation=false -m:1 -nr:false --filter 'FullyQualifiedName~VisionMachineToolsTests|FullyQualifiedName~McpHostBuilderTests|FullyQualifiedName~McpToolProfileCompositionTests' --logger 'console;verbosity=minimal' --logger trx --results-directory "$files\vision-api-names-final-mcp" -- 'NUnit.WorkDirectory=C:\Temp\mv-deb2\followup'

$env:TEMP = 'C:\Temp\mv-deb2\followup48'
$env:TMP = $env:TEMP

# Passed: 515; Failed: 0; Skipped: 0.
dotnet test tests\Opc.Ua.Vision.Tests\Opc.Ua.Vision.Tests.csproj -c Debug -f net48 --no-restore --artifacts-path "$files\solution-net48-artifacts" -p:CustomTestTarget=net48 -p:UseSharedCompilation=false -m:1 -nr:false --logger 'console;verbosity=minimal' --logger trx --results-directory "$files\vision-api-names-net48-final" -- 'NUnit.WorkDirectory=C:\Temp\mv-deb2\followup48'
```

Opt-in long-running/stress/durable tiers have not been rerun locally;
the unqualified **all tests** checklist item remains open. Original
implementation matrix counts are not presented as current results.

</details>

## Related Issues

- Fixes #4732.

The requester reviewed and approved the implementation plan and
subsequent packaging/API decisions in the development session. The
tracking issue records the design; maintainer architectural
discussion/sign-off there is still pending. This PR remains a draft.

## 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.
- [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.
- [ ] 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.
- [x] I have addressed **all** PR feedback received.
2026-10-12 07:01:27 +00:00
Klaus Landsdorf 5d26681ac3 Machinery client: return the NodeId of each metering point measurement (#4642)
# Description

`MachineryMeasurementValue` gets an additive init-only `NodeId`; the
positional constructor is unchanged. `ReadMeteringPointAsync` fills it,
and so do `ReadMainMeteringPointAsync` and `ReadSubMetersAsync`, which
use it.

The values are now read by the NodeIds the browse returned, in one
batched Read, instead of translating each browse name back to a NodeId
first. That is one round trip less, and value and NodeId come from the
same node.

`docs/Machinery.md` shows how to subscribe to the measurements.

Tests:

- New end-to-end test
`MeteringPointMeasurementsCarryTheNodeIdOfTheirVariableAsync` against
`MachineryIntegrationServer`, covering the main metering point and the
sub-meter. It reads `BrowseName` and `Value` behind every returned
NodeId. It fails on master.
- `Opc.Ua.Machinery.Tests` 165/165 on net10.0; all target frameworks
build with 0 warnings.
- Fork CI (full matrix incl. macOS) and CodeQL: green on `afa00065a`
(the same change on master `ce88955f7`) — [CI run
36826789991](https://github.com/biancode/UA-.NETStandard/actions/runs/36826789991)
(189 jobs), [CodeQL run
36826793071](https://github.com/biancode/UA-.NETStandard/actions/runs/36826793071).
Rebasing onto master with #4607 needed one conflict resolution in
`MachineryEndToEndTests.cs` (both new tests kept);
`Opc.Ua.Machinery.Tests` passes 165/165 on net10.0 locally afterwards
and CodeQL is green on that state ([run
36979020583](https://github.com/biancode/UA-.NETStandard/actions/runs/36979020583)).
The run on the current head is in progress: [CI run
37016200389](https://github.com/biancode/UA-.NETStandard/actions/runs/37016200389).

## Related Issues

- Fixes #4641

## 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.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed. (Affected
suites on net10.0 and net8.0 locally on macOS; .NET Framework is covered
by the fork CI's Windows net48 legs.)
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.
2026-10-02 23:21:25 +02:00
Klaus LandsdorfandMarc Schier 01d6b5d26c Machinery: the OPC 40001 companion SDK with Jobs, Result Transfer, Process Values and Energy (#4578)
> Tracking issue #4576. #4577 is merged, so this branch is rebased onto
> `master` and contains only the Machinery commits.

## Summary

Second of four stacked PRs. Adds the OPC UA for Machinery series as
model
packages plus a server and a client SDK, following the companion pattern
of
DI and Robotics, and the two models it depends on that the repository
does
not carry yet.

| Package | Content |
|---|---|
| `Opc.Ua.IA` | OPC 10000-200 IA 1.01.4, **moved out of
`Opc.Ua.Robotics`** (see below) |
| `Opc.Ua.ECM` | OPC 34100 Energy Consumption Management 1.0.1 |
| `Opc.Ua.PADIM` | OPC 30081 PA-DIM 1.02.0 with the IRDI dictionary |
| `Opc.Ua.Machinery` | OPC 40001-1 Basic Building Blocks 1.04.1 |
| `Opc.Ua.Machinery.ProcessValues` | OPC 40001-2 Process Values 1.00.0 |
| `Opc.Ua.Machinery.Jobs` | OPC 40001-3 Job Management 1.0.1 |
| `Opc.Ua.Machinery.Energy` | OPC 40001-4 Energy 1.00 |
| `Opc.Ua.Machinery.Result` | OPC 40001-101 Result Transfer 1.01.0 |
| `Opc.Ua.Machinery.Server` | `AddMachinery()` /
`ConfigureMachinery(...)`, fluent machine builder, server-driven state
machines, process values, job management over the shared ISA-95 V2
binder, energy, result management incl. `GenerateFileForRead` |
| `Opc.Ua.Machinery.Client` | machine discovery, identification, state
observation, process values, job and result access |

OPC 40001-101 `ResultTransferType` redeclares `GenerateFileForRead` with
its
own arguments; the generator fix for such overrides, merged with #4577,
is
what makes it appear on the `ResultTransfer` instances.

All NodeSets are byte-identical to `UA-Nodeset/latest`. Each model
carries a
pinned `Model/*.NodeIds.csv` the generator validates, next to the
unmodified
upstream table; `tools/nodesets/derive-identifier-table.py` derives the
former from the latter.

## Changes to existing code

- **IA moves out of Robotics.** Machinery needs IA as well (OPC 40001-1
types
`Stacklight` with `BasicStacklightType`), and two assemblies generating
the
same model under the same prefix collide at any consumer referencing
both.
The NodeSet moves verbatim, so no generated type changes; the prefix
stays
`Opc.Ua.IA`. Source-compatible, but IA types now live in `Opc.Ua.IA.dll`
rather than `Opc.Ua.Robotics.dll` — binary-breaking for consumers of the
  Robotics package.
- **`samples/OpenUsd/GeneratorServer`** drops its hand-reduced Machinery
  NodeSet copy and the script that produced it (a workaround for the
generator defect fixed by #4577) and references `Opc.Ua.Machinery`
instead.
- `README.md` and `docs/README.md` list the new models; the README also
  corrects the Industrial Automation entry (Part 200, not OPC 40001-1).

## Review follow-ups

Separate commits after the docs/samples commit, so the reviewed state
stays
comparable. Each fix comes with a test that fails without it.

- Copilot review: a transition is committed only while the state machine
is
still in its source state; optional Jobs/Result namespaces the server
does
not load are reported as absent, as documented; resources of registered
machines (result transfer, job-management change pumps) are released
with
  the node manager (`DisposeMachineryResourcesAsync` for a custom DI
  manager); `NodeId`/`LocalizedText` use their null sentinels instead of
  `Nullable<T>`.
- Own review with the rubric in `.github/copilot-instructions.md`: a
failed
browse call propagates from the client instead of reading as "no
children";
  the predefined result metadata compares `CreationTime` against the
  `DateTimeUtc` sentinels (the `DateTime` conversion shifted
  `DateTime.MaxValue` by the host's UTC offset); process values below
`Monitoring` are found; a second metering point named `Main` is
rejected;
  the zero-point event is reported when the handler throws; smaller
  state-sharing and documentation corrections.

## Testing

- `tests/Opc.Ua.Machinery.Tests`: 162 tests on net8 and net10 and with
the
netstandard2.1 libraries; the integration tests connect a real client
session over `opc.tcp` to a hosted
  server.
- With IA extracted: Robotics 636, OpenUsd 921, DI 433 and
SourceGeneration
  263 tests pass.
- `UA.slnx` builds for net472, net48, netstandard2.0/2.1 and net8/9/10
with
  SDK 10.0.401 and no warnings.
- Changed-line coverage of this layer (`check-coverage.ps1`): 91.68 %
(6059 of 6609 changed lines, against `master`).
- Fork CI, full PR matrix incl. macOS, on the current master: green, all
184
jobs ([run
36582685653](https://github.com/biancode/UA-.NETStandard/actions/runs/36582685653)).
CodeQL: green ([run
36582689589](https://github.com/biancode/UA-.NETStandard/actions/runs/36582689589)).
- Beyond the in-repo tests the implementation has been validated end to
end
  against a separate, non-public OPC UA client and server.

## Reviewing

Commits are ordered so each builds: IA extraction → identifier-table
tool →
ECM/PA-DIM → Machinery models → server → client → tests → docs/samples →
GeneratorServer sample → review follow-ups. The model commits are large
only because of the
NodeSets and identifier tables; the hand-written code starts at the
server
commit.

---------

Co-authored-by: Marc Schier <marcschier@users.noreply.github.com>
2026-09-29 19:59:34 +02:00