Commit Graph
6 Commits
Author SHA1 Message Date
Marc SchierandCopilot a6a5275006 Add a PumpX-2000 datasheet and diagrams to the pump sample (#4138)
# Description

The `PumpDeviceIntegrationServer` sample simulates OPC 40223 pumps but
had no product documentation, a thin nameplate, and a duty point that
was not physically self-consistent. This PR gives the sample an
**official-style product datasheet** for its simulated asset and aligns
the server to it, so the document and the running address space cannot
drift apart.

### New: `samples/PumpDeviceIntegrationServer/DATASHEET.md`

A vendor-style datasheet for the fictitious *SimPump Corp PumpX-2000*
(clearly marked as a simulated device):

- Nameplate table mapped to OPC UA browse paths **and namespaces** (DI /
Machinery / Pumps)
- Best-efficiency duty point, characteristic curves and a performance
table
- Operating limits, motor / materials / dimensions data
- Instrument list (tag → browse path → unit → `EURange`) and a Mermaid
process schematic
- Alarm trip points and the deterministic simulation profile

### Server aligned to the datasheet

- `PumpDatasheet.cs` — the datasheet expressed as compile-time
constants.
- `MaterialiseNameplate` — materialises the 19 optional identification
properties through the generator-emitted `AddXxx(context)` helpers, so
each carries the browse name, namespace and DataType declared by the
model (21 nameplate fields in total per pump).
- `WithIdentification` now assigns the full datasheet nameplate to
**every** simulated unit. Product-level fields are shared; unit-level
fields (`SerialNumber`, `AssetId`, `ComponentName`, `Location`,
`FabricationNumber`) are derived from the pump number, so `--pumps N`
yields a consistent nameplate for each instance.
- **Simulation rebuilt around the datasheet curves.** Volumetric flow is
the only independent variable; differential pressure, mass flow,
efficiency and shaft power are derived from `H(Q) = 32 − 0.0104·Q²`,
`η(Q) = 72·(1 − 0.6·((Q−25)/25)²)` and `P = ρ·g·Q·H/η`, so `P = Δp·Q/η`
holds at every tick. Previously every measurement was an independent
sine and the sample published 5 kW of shaft power for 0.05 kg/s at 2 bar
Δp.
- `EURange` values now come from the datasheet; `OverTempAlarm` reports
the `BearingTemperature` chain (the correct `SourceNode` for an alarm
whose limits are in Kelvin) rather than a boolean; `Cavitation` and
`MotorOverheat` are derived from suction level and bearing temperature
with hysteresis instead of `tick % n`.

### Diagrams

The sample README gains a simulated-device summary, a link to the
datasheet, and three Mermaid diagrams: **address space**, **startup /
hosting sequence**, and **simulation + alarm dataflow**.

### Drift protection

`PumpDatasheetConformanceTests` pins the nameplate, engineering ranges,
trip points, published-value envelope and hydraulic consistency against
the datasheet, and the exact pump node-surface baseline in
`PumpInstanceNodeIdRegressionTests` covers the new identification
properties.

No changes to `src/` — this is sample, test and documentation only.

### Rebased on #4117

master has been merged into this branch. #4117 reshaped the same sample
(loop-based pump creation via `--pumps N`, `Pump_1` / `Pump_2`
BrowseNames with `Pump #n` DisplayNames, historian wiring,
`WireBoolean`, per-pump `Diagnostics` group). Both intents are
preserved:

- the datasheet nameplate is applied through upstream's per-pump
`WithIdentification(builder, pump, pumpNumber)` hook, so it scales to
`--pumps N`;
- `Program.cs` is left exactly as upstream has it — identification is
configured centrally, so the old per-pump block there is gone;
- the datasheet curve values are published through upstream's
status-code / source-timestamp plumbing, and upstream's `WireBoolean`
(TrueState/FalseState text + history) is kept;
- the node-surface baseline is the union of both identification lists;
- `docs/SourceGeneratedNodeManagers.md` was consolidated upstream into
`docs/NodeManagers.md`, so the snippet fixes were ported there.

## Related Issues

_No tracking issue; this is a documentation and sample-quality
improvement to the pump reference sample._

## 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 (post-merge)

- `dotnet build samples/PumpDeviceIntegrationServer` (net10.0): 0
warnings, 0 errors
- `dotnet build tests/Opc.Ua.Di.Tests` (net10.0): 0 warnings, 0 errors
- `dotnet test tests/Opc.Ua.Di.Tests -f net10.0`: **331/331 passed**
(includes upstream's new `PumpAddressSpaceComplianceTests` and
`DiConformanceFacetTests`)
- `dotnet test tests/Opc.Ua.Di.Tests -f net48 --filter Pump`: **44/44
passed**
- Live client read against the merged sample: Δp 214 079 Pa, ṁ 8.654
kg/s, η 69.33 %, P 2 677.4 W — `Δp·(ṁ/ρ)/η` matches the published shaft
power; `Pump_1` reads SN-001 / PMP-1001 / Feed Pump A / Bay 3 and
`Pump_2` reads SN-002 / PMP-1002 / Feed Pump B / Bay 4, exactly as
documented.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
2026-08-04 10:37:36 +02:00
Marc SchierandCopilot fb160a8f39 OpenUSD: the OPC UA to OpenUSD binding, scene materialization and connector (#4126)
**Stack 4/5** — base #4125. Part of the #4096 split; see #4096 for the
overall picture.

> Review after #4123, #4124 and #4125.

## What this is

The generic OPC UA to OpenUSD binding, scene materialization, and the
connector that drives a live stage from a running server.

## Contents

**Binding (Part 1)** — `Opc.Ua.OpenUsd` and `Opc.Ua.OpenUsd.Server`. A
server describes how its Variables drive USD prims, and serves the USD
layers it refers to as Part 5 files with a content digest.

**Scene materialization (Part 2)** — `Opc.Ua.OpenUsdScene` and
`Opc.Ua.OpenUsdScene.Server`, including the USDA writer.
- **Escaping fix**: string values, composition-arc references, variant
set names and selections, `doc` and prim names were all emitted
unescaped, so a value carrying a quote or a newline could author its own
layer syntax. Every emit site now goes through the escapers. The tests
assert the right property — not that the payload is absent (it
legitimately survives inside the escaped literal) but that no payload
becomes its own line of layer syntax.

**Client** — `Opc.Ua.OpenUsd.Client` streams live values into an
`IUsdSink`. `CompositeUsdSink` fans out to several sinks; the file sink
interpolates between server samples so motion is continuous rather than
stepped at the publishing interval.

**Connector** — `tools/Opc.Ua.OpenUsd.Connector` composes a stage from a
live server. The optional in-process viewport lives in a separate
assembly probed at run time, so the connector still runs — and says why
— when it is absent.
- **Fix**: the asset cache and PKI root moved off the shared temp
directory to `LocalApplicationData`.

**Sample** — `PumpDeviceIntegrationServer` gains an OpenUSD
representation of the pump.

## Notes for the reviewer

- The viewport assembly is net10.0-only. It is what exercises the
`Directory.Build.targets` change from 1/5: without it, the `net8.0` leg
of the per-TFM CI build fails with `CS0433` because the
`System.Threading.Lock` polyfill collides with the real type.
- `UsdAssetDelivery` hashes through `SHA256.Create` rather than the
static `HashData`, which does not exist on the net472/net48 targets this
test project also builds for.

## Validation

`dotnet build UA.slnx` — 0 errors, 0 warnings.

| Suite | Result |
| --- | --- |
| Opc.Ua.OpenUsd.Tests | 621 / 0 |
| Opc.Ua.Di.Tests | 321 / 0 |

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8cbb8cd0-f0cb-4ab0-bea2-6202fbf69485
2026-08-01 07:55:58 +02:00
Marc SchierandCopilot 8a10139b8c Fix OPC UA address-space compliance in the device integration stack (#4117)
# Description

A live OPC UA compliance audit of `samples/PumpDeviceIntegrationServer`
against the **OPC 40223 Pumps** and **OPC 10000-100 (DI)** companion
specifications found **13 address-space violations** plus a
serialization bug in the MCP tool. Every fix lands at its **root cause
in the shared stack** rather than as a workaround in the sample, so all
servers benefit.

The sample now exposes **N fully simulated pumps** (`--pumps N`) and
advertises the DI conformance facets it actually satisfies.

> This branch has been merged with `master` including **#4116** ("Add
simulation to PumpDeviceIntegrationServer and Event modelling"), which
independently reworked the same pump simulation and the same fluent
alarm builders. See [Relationship to #4116](#relationship-to-4116) below
for how the two were reconciled.

## Findings and fixes

| # | Finding | Root cause | Fix |
|---|---------|-----------|-----|
| 1 | `OverTempAlarm` created by the fluent API did not exist in the
address space | `AttachAlarm` attached the alarm with `AddChild` but
never indexed it into the node manager | Register the node; the same gap
existed in `AddObject`, `CreateInstance` and the state-machine creators
|
| 2 | Alarms were unsubscribable — `EventNotifier = 0` everywhere, no
`HasNotifier` from `Server` | No event-source wiring at all | Set
`HasCondition`, initialise
`SourceNode`/`SourceName`/`ConditionName`/`InputNode`, promote
`EventNotifier` up the ancestor chain, publish `HasNotifier` from the
`Server` object |
| 3 | Every instance NodeId was minted in the **DI standard namespace**
(`ns=4;s=5001_Pump #1`) | `DiNodeManager.New` inherited the parent's
namespace index | New `DiNodeManager.InstanceNamespaceIndex`; instances
move to the server's application namespace |
| 4 | `Server/Namespaces` described only 2 of 6 namespaces | The
NodeSet2 importer drops nodes parented to namespace 0, so companion-spec
metadata objects never reached the address space — and no route covered
the server's own namespace | New `NamespaceMetadataPublisher` walks the
`NamespaceArray` and fills version/publication date from
`ModelDependencyAttribute` |
| 5 | Vendor BrowseNames (`Diagnostics`, `LastError`, …) sat in the DI
namespace | Builders defaulted to the parent's namespace | Default to
the server namespace; **spec-defined BrowseNames are unchanged** |
| 6 | `AccessLevel="5"` from the Pumps NodeSet was reported as `1` | The
ModelDesign `AccessLevel` enum is not `[Flags]` and cannot represent
`CurrentRead \| HistoryRead` | Carry the verbatim NodeSet2 bitmask and
emit named `AccessLevels` constants |
| 7 | `Historizing = true` while `HistoryRead` returned
`BadHistoryOperationUnsupported` | Nothing reconciled the declared
history surface with the wired historians | Startup clears the
advertisement (including masking the attribute read callbacks) when no
provider resolves; the sample wires a real historian |
| 8 | Mandatory `ProductInstanceUri` unset | Sample omission | Populated
on every pump |
| 9 | Mandatory `TrueState`/`FalseState` empty on every supervision
boolean | Absent in the official NodeSet; a server must still populate
them | Populated on every pump |
| 10 | Machinery `Machines` folder empty (OPC 40001-1 §9.2 says
**shall**) | `TryAddToMachinesFolder` used `AddChild`, which
*re-parents* the device away from `DeviceSet` | Add the `Organizes`
reference instead |
| 11 | BrowseName contained a space and `#` | Sample naming | `Pump_1`
with the readable label in `DisplayName` |
| 12 | `Maintenance` group materialised but empty | Sample omission |
Populated |
| 13 | Uninitialised values reported `Good` | No initial-value status |
`BadWaitingForInitialData` until the first simulation tick |
| — | `ServerProfileArray` advertised only `StandardUA2017` |
`DiNodeManager` did not implement the existing `IConformanceContributor`
| Contribute DI 1.05 server facets computed at runtime from what is
actually wired, merged with `StandardUA2017` |
| — | MCP tool serialised boolean `false` and numeric `0` as JSON `null`
| `Variant.Null` is `default`, so `Variant.Equals` adopts the non-null
type and compares against the default value | Dispatch on `BuiltInType`
and use typed `TryGetValue` accessors instead of the prohibited
`AsBoxedObject` |

## Relationship to #4116

#4116 landed on `master` while this branch was in flight and targeted
overlapping ground. The merge is a **union of both intents**, not a
fast-forward:

- **`AlarmBuilderExtensions`** — kept both sides. #4116 contributes the
parent-must-be-an-Object guard, `SetEnableState`, and the
`HasEventSource` references; this branch contributes the `HasCondition`
reference, condition source initialisation, **registration of the alarm
with the node manager so it is browsable**, notifier promotion up the
whole ancestor chain, and root-notifier registration.
- **`SupervisionBuilderExtensions`** — took #4116's `SetAlarmActive`. It
is more spec-correct than this branch's version: it honours
`EnabledState`, resets Acked/Confirmed on activation, and computes
`Retain` per OPC 10000-9 so an unacknowledged Condition stays retained
after going inactive. This branch's null guard was kept so a malformed
alarm cannot dereference a missing `EnabledState`.
- **`PumpNodeManager.Configure.cs`** — took #4116's **push-based**
simulation (`RegisterPumpSimulation`, `CreatePumpSimulation`,
`IValueUpdater<T>`, `Initialize`/`Advance`/`Publish`, deterministic
per-pump phase offsets) as the structure, then folded in this branch's
compliance work: configurable pump count, Maintenance group,
`TrueState`/`FalseState`, historian wiring, per-pump mandatory
Identification values, and `BadWaitingForInitialData` until the first
tick.
- **README** — union, keeping #4116's address-space validation workflow
section.

Two test adjustments were needed, both because the merge legitimately
changed behaviour the tests pinned:

- `PumpHostedReferenceTests` now resolves pump NodeIds from the browse
result instead of hard-coding the DI namespace, and waits for the first
published value. `PumpCreatedAfterStartupJoinsTheLiveSimulationAsync`
was **added** to restore #4116's coverage that a pump created after
startup through `ConfigureDevicesFor` joins the live simulation.
- `FluentAlarmRegistrationIntegrationTests` no longer asserts that
`Retain` clears when an alarm goes inactive — that pinned this branch's
weaker semantics, and #4116's Part 9 behaviour is correct.

## Breaking change

Runtime-created DI device instance NodeIds move from the companion-spec
namespace to the server's application namespace, e.g.
`ns=<DI>;s=5001_Pump #1` becomes `ns=<application>;s=5001_Pump #1`. This
invalidates NodeIds cached by clients from earlier 2.0 previews. It is
**not** a model change — standard DI/Machinery/Pumps type NodeIds and
spec-defined BrowseNames are untouched.

Device integration hosting is new in 2.0, so there is nothing to migrate
from 1.5.378 and this is deliberately *not* in `docs/MigrationGuide.md`.
Anyone tracking 2.0 previews should rediscover devices by browsing
`DeviceSet` rather than persisting NodeIds, and resolve namespace
indexes from `NamespaceArray` per connection.

## Verification

Compliance is pinned by regression tests that assert **through the
server's service surface** (`BrowseAsync` / `ReadAsync` /
`HistoryReadAsync`), not by inspecting `NodeState` objects in memory.
That distinction mattered: three defects survived in-memory assertions
and were only caught through the read path — most subtly, `Historizing`
was cleared while `AccessLevel` still advertised `HistoryRead`, because
attribute read callbacks re-applied the NodeSet bits.

Post-merge on net10.0:

| Suite | Result |
|---|---|
| `Opc.Ua.Di.Tests` | 326 passed |
| `Opc.Ua.Server.Tests` | 3846 passed, 5 skipped |
| `Opc.Ua.Tools.Tests` | 288 passed |
| `Opc.Ua.History.Tests` | 506 passed, 27 skipped |
| `Opc.Ua.SourceGeneration.Core.Tests` | 3722 passed, 8 skipped |

The full solution was also built and tested on **net48** before the
merge.

Review feedback from `copilot-pull-request-reviewer` has been addressed
in commit `badad31dc`: the shared `ServerObjectState` lock was removed
(the forward `HasNotifier` edge is published through the owning node
manager instead), MCP array serialization now preserves element types,
and the pump option guards report the specific rejected option. The only
failure is
`ConfigureApplicationBuildsSharedClientAndServerConfigurationAsync`, a
pre-existing certificate-store issue that **passes when run in
isolation** and is unrelated to this change.

## Documentation

Updated `docs/DeviceIntegration.md`, `docs/MigrationGuide.md`,
`docs/HistoricalAccess.md`, `docs/DeveloperGuide.md`,
`docs/McpServer.md`, `docs/README.md`,
`samples/PumpDeviceIntegrationServer/README.md` and
`tools/Opc.Ua.Mcp/README.md`, and consolidated the four node-manager
documents into a single [`docs/NodeManagers.md`](docs/NodeManagers.md) —
overview, built-in managers (including `MasterNodeManager`), core vs
custom, registration, server address-space metadata, and source
generation — with a TOC and all inbound links repointed.

## Related Issues

No tracking issue exists for this work yet — it originated from an
ad-hoc compliance audit of the pump sample. Given the size of the change
and the runtime-breaking NodeId move, please open (or link) a tracking
issue so the design is recorded as an ADR before merging.

- Fixes #

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0d50c3fc-58b1-4c99-85a4-1e32c88666d9
2026-07-30 16:04:18 +02:00
Alexander KöpkeandCopilot 4251fec84d Add simulation to PumpDeviceIntegrationServer and Event modelling (#4116)
This pull request introduces significant improvements to the Pump Device
Integration Server sample, focusing on robust event/alarm integration,
simulation enhancements, and comprehensive test coverage. The changes
ensure that pump objects and their events are correctly wired for OPC UA
eventing, improve alarm state management and reporting, and update
documentation and tests to validate these behaviors.

**Event and Alarm Integration Improvements:**

* Pump objects and their event sub-objects now have `EventNotifier`
flags and are linked via `HasNotifier` references, ensuring they
properly participate in the OPC UA event hierarchy. Each pump is also
registered as a root notifier.
[[1]](diffhunk://#diff-6d01bd352b39d1bd0a96e8c0c11d27c066ae33a64a552bdabe023535ca833fc2R297-R307)
[[2]](diffhunk://#diff-6d01bd352b39d1bd0a96e8c0c11d27c066ae33a64a552bdabe023535ca833fc2R256-R257)
* The alarm builder now enforces that alarms are attached only to valid
object nodes, enables alarms by default, and wires `HasEventSource`
references and `EventNotifier` flags for both parent and alarm nodes.
[[1]](diffhunk://#diff-2d10b87cac23754d35da8632e9b1a23ec0c1e706689c7241da36dbc934b2666aR208-R215)
[[2]](diffhunk://#diff-2d10b87cac23754d35da8632e9b1a23ec0c1e706689c7241da36dbc934b2666aR235-R248)

**Alarm State and Reporting Enhancements:**

* Alarm state transitions now check that the alarm is enabled before
updating state, set acknowledged/confirmed flags, assign new event IDs,
update timestamps, and report events using OPC UA mechanisms.
* Alarm severity and message are set dynamically based on the active
state, and the `Retain` flag is managed according to OPC UA alarm state
requirements.

**Simulation and API Changes:**

* `CreatePumpAsync` now registers new pump instances with the
simulation, ensuring all pumps participate in the live simulation loop.
* Simulation ticks and monitored value publishing are unified and
described in the documentation, with deterministic phase offsets for
each pump.
[[1]](diffhunk://#diff-e2a596af01b60e5bf0559e8c4307604ca80889523b67221fb999ce8a7ad2bb2cL98-R134)
[[2]](diffhunk://#diff-e2a596af01b60e5bf0559e8c4307604ca80889523b67221fb999ce8a7ad2bb2cR178-R193)

**Documentation Updates:**

* The README is updated to reflect the new event/alarm integration,
simulation details, and instructions for validating the address space
with automated and manual workflows.
[[1]](diffhunk://#diff-e2a596af01b60e5bf0559e8c4307604ca80889523b67221fb999ce8a7ad2bb2cL40-R73)
[[2]](diffhunk://#diff-e2a596af01b60e5bf0559e8c4307604ca80889523b67221fb999ce8a7ad2bb2cL98-R134)
[[3]](diffhunk://#diff-e2a596af01b60e5bf0559e8c4307604ca80889523b67221fb999ce8a7ad2bb2cR178-R193)

**Test Coverage Expansion:**

* The main integration test is expanded to verify that both pumps
publish unique, changing monitored values and that alarm events are
reported and observable via OPC UA subscriptions.
[[1]](diffhunk://#diff-818e6fe65e5a28c2405bb287d9e40fcb74f1275c90755da56c60343528e17ee5L57-R58)
[[2]](diffhunk://#diff-818e6fe65e5a28c2405bb287d9e40fcb74f1275c90755da56c60343528e17ee5R165-R314)

These changes ensure the sample is a robust reference for OPC UA
eventing, simulation, and alarm integration, and that its correctness is
validated both automatically and through enhanced documentation.#
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._

## Related Issues

_Reference all GitHub issues this PR addresses. If there is no issue
yet, open one and link it here._

_If this is a relatively large or complex change, a design must have
been discussed in the related tracking issue and signed off (which
becomes the Architectural Decision Record (ADR))._

- Fixes #github-issue-number, ...

## 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.
- [ ] 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: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-30 06:05:01 +02:00
0de25de89a Change PumpDeviceIntegrationServer to use PumpType (#4066)
This pull request updates the Device Integration documentation and the
`PumpDeviceIntegrationServer` sample to introduce and demonstrate the
new `ITopologyElementBuilder<TElement>` API for working with OPC UA
Device Integration topology elements. The changes clarify the
distinction between device and topology-element builders, update sample
code to use the new builder, and improve the overall clarity and
accuracy of both the documentation and the sample implementation.

**API and Documentation Updates:**

* Introduced `ITopologyElementBuilder<TElement>` as the unified builder
for topology elements, exposing common operations such as functional
groups and topology references, and updated all references and examples
to use this builder where appropriate.
[[1]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR164-R173)
[[2]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR219-R228)
[[3]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL897-R926)
[[4]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL912-R943)
[[5]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL926-R958)
* Clarified the distinction between device builders
(`IDeviceBuilder<TDevice>`) and topology-element builders in both the
documentation and sample code, and documented the available builder
methods and their intended use.
[[1]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL33-R38)
[[2]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaL141-R146)
[[3]](diffhunk://#diff-93e580c17580d8cb3c5c4206fdf6b64c1deabc1e00e4f83d64583661a97f76eaR495-R500)

**Sample Code Modernization:**

* Refactored `samples/PumpDeviceIntegrationServer/Program.cs` to use the
new `ITopologyElementBuilder<TElement>` API for creating and configuring
the second pump, including setting identification properties and
functional groups, and removed legacy code for device health and
software update facets.
[[1]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL74-R98)
[[2]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL133-L139)
[[3]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL53-L57)
[[4]](diffhunk://#diff-7e065631de4a55cb923ba4fa9a32fffcff804ce5ef3e09a70a2eead76425b18dL37-R37)
* Cleaned up and modernized the sample project description and removed
unused or outdated fields and comments.
[[1]](diffhunk://#diff-e3976eed0bf7d7b96d6762d58e8101a0670b2ef60aa04609e8d5ff4be30f64beL8-R8)
[[2]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L58-L76)
[[3]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L110-R91)
[[4]](diffhunk://#diff-70693dd3c39308e8e25d4e009281950f860a914afcf92b2e100a0710b5bb8db1L129-R110)

**Software Update Example Improvements:**

* Updated the software update walkthrough in the documentation to use
the new builder APIs and improved the clarity of the example, including
more accurate references to the simulated device and software update
facet.
[[1]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L300-R303)
[[2]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L313-R337)
[[3]](diffhunk://#diff-5342d39e79e59ca4fc77a9d6277377841c321d17476979f612c0487c53342238L378-L379)

These changes make it easier to work with OPC UA Device Integration
topology elements, provide clearer guidance in the documentation, and
ensure the sample code reflects current best practices.# 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._

## Related Issues

_Reference all GitHub issues this PR addresses. If there is no issue
yet, open one and link it here._

_If this is a relatively large or complex change, a design must have
been discussed in the related tracking issue and signed off (which
becomes the Architectural Decision Record (ADR))._

- Fixes #github-issue-number, ...

## Checklist

_Put an `x` in the boxes that apply. You can complete these step by step
after opening the PR._

- [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.
- [ ] 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: Marc Schier <marcschier@users.noreply.github.com>
Co-authored-by: Marc Schier <marcschier@hotmail.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-22 04:06:03 +02:00
Marc Schier 7cec2d614d Move repository files to the consolidated layout 2026-07-16 06:25:54 +02:00