# Description The shared AI deployment contracts and instance registration; DI software-upload outcomes, digest handling and simulator; bounded Positioning/Pumps reads; PubSub raw-data/matrix and security-provider handling; Vision read-quality contracts; xRegistry resource writes and instance projection; OpenUSD authoring, embedded viewport and retained-frame capture APIs; standalone sample CLI controls; and the generic Debug/RID package-policy and fork/Dependabot coverage-comment fixes. The Linux native prerequisite step is retained for **Vision tests only**. ## Related Issues - Related tracking issue: [#4461](https://github.com/OPCFoundation/UA-.NETStandard/issues/4461), without automatic closure. - Source work: [#4462](https://github.com/OPCFoundation/UA-.NETStandard/pull/4462), which remains intact for the coordinator's later integration. - Immediate stacked predecessor: [#4804](https://github.com/OPCFoundation/UA-.NETStandard/pull/4804). ## 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.
OPC 40223 Pumps samples
Two console applications that show the Opc.Ua.Pumps* libraries end to end:
a server that publishes pumps with the fluent builders, and a client that
discovers and reads them with PumpsClient.
| Sample | What it does |
|---|---|
| PumpsServer | Serves a duty/standby pair of cooling-water pumps and simulates them once a second |
| PumpsClient | Discovers every pump, prints the whole pump, then streams live values and supervision changes |
The Pumps developer guide explains every API used here.
Run
In two terminals:
dotnet run --project samples/Pumps/PumpsServer -- --port 62547
dotnet run --project samples/Pumps/PumpsClient -- --insecure --seconds 30
--insecure makes the client accept the server's self-signed certificate;
never use it in production. The client also takes --url <endpoint> (default
opc.tcp://localhost:62547/PumpsServer) and --seconds <n> (how long to
stream, 0 to skip).
The client prints something like:
Pump CoolingPump_A (ns=1;i=3788608060)
Acme Pumps CW-80, serial CW80-0042, built 2024/3 in DE
location: Plant 1 / Cooling skid
Design:
ClockwiseRotation True
MaximumAllowableContinuousSpeed 3000 1/min
MaximumAllowableWorkingPressure 1.6E+06 Pa
Implementation:
PumpBestEfficiency 78 %
RatedFlow 25 kg/s
RatedSpeed 2900 1/min
SystemRequirements:
Fluid Cooling water
MaximumFlow 32 kg/s
MinimumFlow 6 kg/s
Measurements:
BearingTemperature 27.92 °C
DifferentialPressure 367831 Pa [0 … 1600000]
MassFlow 21.58 kg/s [0 … 32]
NumberOfStarts 0
PumpEfficiency 75.08 % [0 … 100]
PumpPowerInput 9.64 kW
Speed 2821.17 1/min [0 … 3000]
Measurements/DriveEndBearing:
OverallVibrationVelocityRMS 2.88 mm/s [0 … 11.2]
ReferenceStandardForVibrationMeasurement ISO 10816-3
Signals:
PumpOperation True
StandBy False
MultiPump: role Master, mode RedundancyOperation, 1 of 2 pumps in operation
supervision: no active signals
maintenance: OperatingState, Level1, failure False, operating time 12480 h
port Suction: InletConnection, direction In, DN100 PN16 flange
port Discharge: OutletConnection, direction Out, DN80 PN16 flange
port Motor: Drive, direction -, no category
Motor/Measurements:
MotorCurrent 18.84 A
MotorTemperature 35.92 °C
document OperationManualLink: https://pumps.example.com/manuals/cw-80.pdf
document TechnicalDataLink: https://pumps.example.com/datasheets/cw-80.pdf
Pump CoolingPump_B (ns=1;i=1919434908)
…
Polled 7 measurements with PumpReadOptions.ValuesOnly; MassFlow = 21.58
Watching 8 values for 45s (the pumps swap duty every 90 s; cavitation appears once a minute) …
00:04:19 CoolingPump_A DriveEndBearing vRMS 2.91
00:04:19 CoolingPump_A Cavitation False
00:04:19 CoolingPump_B PumpOperation False
00:04:19 CoolingPump_B Cavitation False
00:04:19 CoolingPump_A MassFlow 21.75
00:04:19 CoolingPump_B MassFlow 0
…
00:04:56 CoolingPump_A DriveEndBearing vRMS 7.1
00:04:56 CoolingPump_A Cavitation True
00:05:02 CoolingPump_A Cavitation False
NodeIds and namespace indices are whatever the server assigned; only the browse names are stable.
What the server shows
| File | Shows |
|---|---|
Program.cs |
AddPumps() and ConfigurePumps(...) on the generic host |
PumpFleet.cs |
CreatePumpAsync, WithNameplate, every group builder (Design, Implementation, SystemRequirements, Measurements, Signals, Supervision(...), MultiPump, MaintenanceCategory(...), AddPort, Documentation), engineering units and ranges |
PumpFleet.cs |
A vibration measurement from the <Vibration> placeholder with AddVibration, and the PumpUnit holder of the builders the simulation writes through |
PumpSimulation.cs |
A BackgroundService driving values, a duty/standby exchange every 90 s, and a cavitation episode once a minute |
PumpDatasheet.cs |
Static pump data and UNECE engineering units built per OPC 10000-8 §5.6.3 |
Runtime updates. The simulation writes through the builders it kept at
configuration time. Every Set/SetAnalog/SetDiscrete publishes the
change, so subscribed clients see each update without further code.
What the client shows
Code in Program.cs |
Shows |
|---|---|
connection.Session.Pumps(telemetry) |
Creating the client; registers the OPC 40223 structured types |
pumps.IsSupported |
Detecting whether the server publishes OPC 40223 |
EnumeratePumpsAsync |
Discovery from the DI DeviceSet and the Machinery Machines folder |
ReadPumpAsync → PumpSnapshot |
The whole pump in one pass; each group has its own Read…Async too |
PumpValueSet / PumpValue |
Values with engineering unit and range, AsDouble, AsString |
PumpValueSet.Groups |
Nested groups such as the DriveEndBearing vibration measurement |
PumpSupervisionStatus.ActiveSignals |
Only the raised supervision signals |
PumpMaintenanceData |
StateOfTheItem, MaintenanceLevel, HasFailure |
ReadMeasurementsAsync(…, PumpReadOptions.ValuesOnly, …) |
Cheap polling once the metadata is known |
Streaming.SubscribeDataChangesAsync(value.NodeId, …) |
Subscribing to a reading; PumpValue.NodeId is the node that carries it, also for discrete signals |
SampleSession.cs is the session setup. It
keeps the ApplicationInstance alive until the session closes, because the
instance owns the CertificateManager the session validates with.
Publish as NativeAOT
Both samples are NativeAOT-compatible on net10.0:
dotnet publish samples/Pumps/PumpsServer -c Release -f net10.0 -r osx-arm64
Troubleshooting
| Symptom | Fix |
|---|---|
Client reports BadCertificateUntrusted |
Pass --insecure for the demo, or trust the server certificate in the client's PKI (under the local application data folder, OPC Foundation/PumpsClient/pki) |
The server publishes no pumps. |
Wait for the server log line Materialised pump … before starting the client |
| Port already in use | Start the server with --port <n> and the client with --url opc.tcp://localhost:<n>/PumpsServer |