Skip to content

Add a PumpX-2000 datasheet and render every configured pump in the OpenUSD twin - #4138

Open
marcschier wants to merge 10 commits into
OPCFoundation:masterfrom
marcschier:marcschier/pump-sample-datasheet-and-diagrams
Open

Add a PumpX-2000 datasheet and render every configured pump in the OpenUSD twin#4138
marcschier wants to merge 10 commits into
OPCFoundation:masterfrom
marcschier:marcschier/pump-sample-datasheet-and-diagrams

Conversation

@marcschier

@marcschier marcschier commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Description

Two related improvements to the PumpDeviceIntegrationServer sample: an official-style product datasheet that the server is aligned to, and an OpenUSD twin that actually renders every configured pump.

Part 1 — the PumpX-2000 datasheet

The sample had no product documentation, a thin nameplate, and a duty point that was not physically self-consistent.

New: samples/PumpDeviceIntegrationServer/DATASHEET.md — a vendor-style datasheet for the fictitious SimPump Corp PumpX-2000 (clearly marked as a simulated device): nameplate mapped to OPC UA browse paths and namespaces, BEP duty point, characteristic curves, performance table, operating limits, motor/materials/dimensions, instrument list, trip points, simulation-profile formulas and a Mermaid process schematic.

Server aligned to it:

  • PumpDatasheet.cs — the datasheet as compile-time constants.
  • MaterialiseNameplate — 19 optional identification properties via generator-emitted AddXxx(context) helpers, so each carries the browse name, namespace and DataType declared by the model (21 nameplate fields per pump). Unit-level fields are derived from the pump number, so --pumps N yields a consistent nameplate per instance.
  • Curve-based simulation. Volumetric flow is the only independent variable; Δp, mass flow, efficiency and shaft power follow 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 each measurement was an independent sine and the sample published 5 kW of shaft power for 0.05 kg/s at 2 bar.
  • EURange from the datasheet; OverTempAlarm sources BearingTemperature (the correct SourceNode for an alarm whose limits are in Kelvin); Cavitation and MotorOverheat derived from suction level and bearing temperature with hysteresis.
  • PumpDatasheetConformanceTests pins nameplate, ranges, trip points, value envelope and hydraulic consistency against the document.

Part 2 — one OpenUSD twin per pump

The twin was hard-wired to a single machine. PumpPrimPath was the constant /Plant/Pumps/P101 and every pump's representation, component bindings and live bindings used it; the signal Variables lived in single fields each pump overwrote; ShaftAngle/AlarmActive both read the first pump's simulation; and only the first representation was organised into the discovery registry. --pumps 4 rendered one pump, spun by pump 1 and alarmed by pump 4.

  • Each pump now owns a PumpTwin: its prim path /Plant/Pumps/<BrowseName>, its signal Variables, its representation and its simulation. Every representation is registered for discovery.
  • A plant-level representation on the DeviceSet declares one Many component binding scoped to PumpType, so a connector composes one full-fidelity @pump.usda@</Pump> reference per configured pump. The arc is Reference, not Instance — an instanceable prim turns its descendants into a shared prototype, which cannot carry per-pump rotation or colour.
  • Each pump publishes its bay as a ThreeDCartesianCoordinates value bound to xformOp:translate, so any N lays out in a row with no per-pump stage authoring.
  • Binding set widened from 4 to 15 per pump: motor cooling fan, discharge-pressure and bearing-temperature gauge needles, a suction vessel whose liquid surface rides on the published Level, suction-line tint from fluid temperature, mass-flow/efficiency/start-count readouts, and distinct cavitation and overheat indications at the place on the machine where each fault is. Scales and offsets are derived from PumpDatasheet.
  • The geometry those bindings need is authored into the P101 master and regenerated into pump.usda. The master stays in the layer but active = false — it is the generator's input, and rendering it too would show a phantom machine no OPC UA object drives.

The hall shows the pumps, and only the pumps

Three follow-up problems surfaced once the scene actually rendered N machines, all fixed here:

  • Phantom pumps. A demo ProductionLine authored three empty pump bodies on the same aisle at a different pitch, and a RemotePump stood in for a machine on another server. Neither is simulated by the server you connect to, so the viewport showed more pumps than existed and the extras never moved. /Plant/Line1 and remote-pump.usda are gone; the production line still demonstrates aggregation, but over plain topology objects with no representation. The stage now serves two assets and renders exactly the set of pumps the server simulates.

  • Phantom alarms. StatusLight defaulted to visibility = "inherited", so any machine without a live binding wore a permanent alarm halo. Indicator geometry now defaults to invisible and is only revealed by a binding.

  • Error indication. The alarm light bulb is replaced by an alarm ring — a red annulus authored around the machine and made visible while that pump is in an error condition, so the fault reads at a glance from any angle instead of depending on a small emissive lamp. The bearing overheat halo moves onto the bearing bracket, where the heat actually is.

  • Phantom placement. Only one pump was visible however many the server simulated. The bay positions were correct on the wire, but a connector accumulates Translation/Rotation/Scale into a single xformOp:transform matrix — it must, because xformOpOrder is uniform and an op order authored in the asset layer cannot be cleared from the stronger layer a connector edits — while USD evaluates only the ops named in xformOpOrder. The asset named xformOp:translate, and its root prim named nothing at all, so every matrix written to it was discarded in silence and all N pumps resolved to the origin. The two prims a Translation target drives (the pump root and the fluid surface) now declare xformOp:transform.

The stage camera is an operator viewpoint: standing in front of the row at eye height, angled 7° down, with the suction vessels behind the machines. It holds the whole row in frame for 1–8 pumps without moving.

Two deliberate non-changes, both explained in code and README:

  • The OverTempAlarm condition is not bound. The fluent alarm builder leaves the condition's state children on their standard namespace-0 declaration NodeIds (which PumpInstanceNodeIdRegressionTests pins), so every pump's alarm shares one ActiveState/Severity/AckedState node — binding them would light every ring at once. The per-pump supervision states are the alarm indication instead, and they are what drives the condition through ActivatesAlarm.
  • The plant aggregation is not dynamic. The pump set is fixed by --pumps at start-up, and declaring it dynamic made the connector's stale-prim sweep deactivate the Impeller and Bearing component prims that now live under /Plant/Pumps.

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

Related Issues

No tracking issue; sample-quality and documentation improvements to the pump reference sample.

Checklist

  • I have signed the CLA and read the CONTRIBUTING 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.

Verification

  • dotnet build sample + tests (Release, net10.0): 0 warnings, 0 errors
  • dotnet test tests/Opc.Ua.Di.Tests -f net10.0: 371/371 passed
  • dotnet test tests/Opc.Ua.OpenUsd.Tests -f net10.0: 635/635 passed
  • dotnet test tests/Opc.Ua.Di.Tests -f net48 --filter Pump: 63/63 passed
  • dotnet build tests/Opc.Ua.Di.Tests -f net472 (Debug and Release): 0 warnings, 0 errors. PumpDeviceIntegrationServer matches AppTargetFrameworks and does not exist on net472, so the test project drops its project reference and every dependent Pump fixture there; PumpDatasheetConformanceTests is now on that list too.
  • EveryConfiguredPumpIsAnIndependentTwinAsync asserts N distinct prims, no cross-pump binding targets, and N distinct shaft angles. OnlySimulatedPumpsAreComposedAsync asserts the composed set is exactly the simulated set.
  • End-to-end against a running server with --pumps 5 and the 3D viewport: five referenced pumps compose at bays 0 / 2.4 / 4.8 / 7.2 / 9.6 m; sampling the live override layer twice, ~25 s apart, shows every shaft advancing independently (Δ 3255° / 3363° / 3463° / 3528° / 3542°), and only the pump in an error condition has its AlarmRing visible. No remote-pump, Line1 or P_20x references remain in the composed stage. Live read also confirms Δp·(ṁ/ρ)/η matches the published shaft power.
  • Checked against real OpenUSD composition: composing five referenced pumps and authoring each bay as the matrix the sink writes, the previous asset resolves four of five to (0,0,0) while this one resolves each to its own bay. TransformBindingsTargetDeclaredXformOpsAsync pins the rule that produced it.
  • Code review found two defects in the live-signal path, both fixed: signal registration walked every twin on a call made once per pump, so the publish set grew as N(N+1)/2 and the first pump was re-published once per later pump; and the publish set was a plain List appended to by the runtime-add path while the 250 ms tick enumerated it, which would throw on the timer thread and stop the simulation for every pump. EveryPumpPublishesItsShaftAngleExactlyOnce pins the first.

Give the PumpDeviceIntegrationServer sample an official-style product
datasheet for its simulated asset and align the server to it, so the
document and the running address space cannot drift apart.

New DATASHEET.md specifies the fictitious SimPump Corp PumpX-2000:
nameplate mapped to OPC UA browse paths and namespaces, best-efficiency
duty point, characteristic curves, performance table, operating limits,
motor and materials data, instrument list, alarm trip points, the
deterministic simulation profile, and a Mermaid process schematic.

The server now implements those numbers. PumpDatasheet.cs holds the
datasheet as compile-time constants; MaterialiseNameplate adds the 19
optional identification properties through the generator-emitted
AddXxx(context) helpers so each carries its declared browse name,
namespace and DataType. Both pumps become units of the same product
(SN-001, SN-002) rather than two unrelated vendors.

The simulation is rebuilt around the datasheet curves: volumetric flow is
the only independent variable and differential pressure, mass flow,
efficiency and shaft power are derived from it, so P = dp * Q / eta holds
at every tick. Previously each measurement was an independent sine and
the published duty point was not physically self-consistent (5 kW of
shaft power at 0.05 kg/s and 2 bar). Engineering ranges follow the
datasheet, OverTempAlarm reports the BearingTemperature chain instead of
a boolean, and the Cavitation and MotorOverheat supervision states are
derived from suction level and bearing temperature with hysteresis.

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

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
Copilot AI review requested due to automatic review settings July 31, 2026 06:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR adds an “official-style” PumpX-2000 datasheet to the PumpDeviceIntegrationServer sample and aligns the sample server + tests so the simulated address space, ranges, alarms, and hydraulics remain consistent with the published documentation.

Changes:

  • Added a full PumpX-2000 datasheet (markdown + Mermaid diagrams) and updated sample docs to reference it.
  • Rebuilt the sample simulation to derive all process values from datasheet curves and added compile-time datasheet constants (PumpDatasheet.cs).
  • Added conformance/regression tests that pin nameplate fields, ranges, alarm trip points, and hydraulic consistency to the datasheet.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/Opc.Ua.Di.Tests/PumpInstanceNodeIdRegressionTests.cs Extends the node-surface baseline to include newly materialized Identification properties.
tests/Opc.Ua.Di.Tests/PumpDatasheetConformanceTests.cs Adds deterministic conformance tests to prevent drift between datasheet and address space/simulation.
samples/PumpDeviceIntegrationServer/README.md Adds datasheet link + diagrams and documents the new simulation/alarm behavior.
samples/PumpDeviceIntegrationServer/PumpNodeManager.cs Materializes optional nameplate properties via generator helpers to match model-defined names/namespaces/datatypes.
samples/PumpDeviceIntegrationServer/PumpNodeManager.Configure.cs Aligns nameplate values, EURanges, alarm source/limits, and simulation math with the datasheet.
samples/PumpDeviceIntegrationServer/PumpDatasheet.cs Introduces a single source of truth for datasheet constants used by the sample and tests.
samples/PumpDeviceIntegrationServer/Program.cs Updates the declarative Pump #2 identification wiring to match the datasheet across namespaces.
samples/PumpDeviceIntegrationServer/DATASHEET.md Adds the vendor-style PumpX-2000 datasheet (nameplate, curves, ranges, alarms, simulation profile).
docs/SourceGeneratedNodeManagers.md Updates docs examples to match new naming/ranges and correct property CLR types.
docs/DeviceIntegration.md Links to the datasheet and updates the topology-element builder example identification wiring.

Comment thread samples/PumpDeviceIntegrationServer/Program.cs Outdated
Comment thread tests/Opc.Ua.Di.Tests/PumpDatasheetConformanceTests.cs
marcschier and others added 2 commits July 31, 2026 08:41
Reconcile the PumpX-2000 datasheet work with the address-space
compliance changes from OPCFoundation#4117, which reshaped the same sample.

Conflict resolutions:

- WithIdentification: keep the upstream per-pump signature
  (builder, pump, pumpNumber) and NodeId-based lookup, and apply the
  full 21-field datasheet nameplate through it. Unit-specific fields
  (serial, asset id, component name, location, fabrication number) are
  now derived from the pump number, so every instance materialised by
  --pumps N gets a consistent nameplate instead of only the first two.
- Program.cs: take upstream. Identification is configured centrally in
  WithIdentification for every pump, so the per-pump nameplate block in
  the ConfigureDevicesFor delegate is superseded; the delegate keeps
  upstream's loop that adds the Diagnostics functional group.
- PumpSimulationState.Publish: keep upstream's status code and source
  timestamp plumbing and publish the datasheet curve values through it.
- Supervision booleans: keep upstream's WireBoolean helper (TrueState /
  FalseState text and history) and drop the alarm's MonitorVariable on
  the boolean, because the alarm now sources BearingTemperature, which
  is what its Kelvin trip points describe.
- Node-surface baseline: union of both identification lists.
- docs/SourceGeneratedNodeManagers.md was consolidated upstream into
  docs/NodeManagers.md; the datasheet-related snippet fixes were ported
  there and the old file deleted.

Documentation and tests follow the upstream rename of the pump
BrowseNames to Pump_1 / Pump_2 (DisplayName "Pump #1" / "Pump #2").

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
Address PR review feedback on PumpDatasheetConformanceTests: the
consistency check divided by the published efficiency and shaft power
without checking them first, so a sample read before the simulation had
published a coherent set of values would have produced NaN or Infinity
and failed on startup timing rather than on the hydraulic model.

Skip and retry an incomplete sample instead, and assert explicitly that
a complete sample was observed before asserting the deviation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.91%. Comparing base (8a10139) to head (edb30f4).
⚠️ Report is 5 commits behind head on master.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4138      +/-   ##
==========================================
- Coverage   80.23%   79.91%   -0.32%     
==========================================
  Files        1515     1515              
  Lines      209980   209980              
  Branches    36213    36213              
==========================================
- Hits       168479   167811     -668     
- Misses      28867    29568     +701     
+ Partials    12634    12601      -33     

see 52 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

marcschier and others added 2 commits August 1, 2026 09:58
Reconcile the PumpX-2000 datasheet work with the OpenUSD, Robotics and
Positioning drop, which added an OpenUSD twin to the same pump sample.

Conflict resolutions:

- PumpSimulationState keeps the datasheet-driven phase offset and gains
  upstream's tick interval, which the shaft integration needs.
- Publish() keeps the datasheet curve values and adds upstream's shaft
  angle integration on top of them.
- The shaft speed reference is rescaled: upstream integrated the angle
  from a NominalFlow of 0.05 kg/s, which was the pre-datasheet mass-flow
  scale. Rated mass flow is now 6.93 kg/s, so the constants move into
  PumpDatasheet as Hydraulics.RatedMassFlow and Hydraulics.RatedSpeed
  (2900 min-1, the value the datasheet already published in section 5).
  Without this the twin would spin the shaft about 139 times too fast.
- The node-surface baseline is the union of the datasheet nameplate and
  the OpenUSD representation nodes.

The sample README picks up the OpenUSD twin section from upstream; its
address-space and dataflow diagrams and the console transcript are
refreshed for the merged server.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
The twin was hard-wired to a single machine: PumpPrimPath was the
constant /Plant/Pumps/P101 and every pump's representation, component
bindings and live bindings used it, so N pumps drove one prim. The signal
Variables lived in single fields that each pump overwrote, ShaftAngle and
AlarmActive both read m_primarySimulation - the first pump - and only the
first representation was organised into the discovery registry. A server
started with --pumps 4 rendered one pump, spun by pump 1 and alarmed by
pump 4.

Each pump now owns a PumpTwin: its prim path (/Plant/Pumps/<BrowseName>),
its signal Variables, its representation and its simulation. Every
representation is registered in the Representations folder, and a
plant-level representation on the DeviceSet declares one Many component
binding scoped to PumpType, so a connector composes one full-fidelity
pump.usda reference per configured pump. The arc is Reference rather than
Instance because an instanceable prim turns its descendants into a shared
prototype, which cannot carry per-pump rotation or colour. Each pump
publishes its bay as a ThreeDCartesianCoordinates value bound to
xformOp:translate, so any number of pumps lays out in a row without the
stage authoring anything per pump.

The binding set now covers the machine rather than four signals. Added:
motor cooling fan, discharge-pressure and bearing-temperature gauge
needles, a suction vessel whose liquid surface rides on the published
level, suction-line tint from fluid temperature, mass-flow, efficiency
and start-count readouts, and distinct cavitation and motor-overheat
indications at the place on the machine where each fault is. Scales and
offsets are derived from PumpDatasheet, so a datasheet change moves the
needles with it. Efficiency is a readout rather than a colour: the
DisplayColor ramp models a temperature.

The geometry those bindings need is authored into the P101 master and
regenerated into pump.usda and remote-pump.usda. Beacon and StatusLight
move into the component asset, because signalling is now per machine. The
master is left in the layer but deactivated: it is the generator's input,
and rendering it too would show a phantom machine no OPC UA object
drives.

The OverTempAlarm condition is deliberately not bound. The fluent alarm
builder leaves the condition's state children on their standard
namespace-0 declaration NodeIds, so every pump's alarm shares one
ActiveState, Severity and AckedState node and binding them would light
every beacon at once. The per-pump supervision states are the alarm
indication instead, and they are what drives the condition through
ActivatesAlarm in the first place.

The plant aggregation is not declared dynamic. The configured pump set is
fixed by --pumps at start-up, the ProductionLine already demonstrates
dynamic add/remove, and declaring it dynamic made the connector's
stale-prim sweep deactivate the Impeller and Bearing component prims that
now live under /Plant/Pumps.

Verified against a running server: with --pumps 4 the connector composes
four referenced pumps at bays 0/2.4/4.8/7.2 m, each with its own shaft
angle, fan, gauge needles, casing colour, fluid level and alarm halos.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
@marcschier marcschier changed the title Add a PumpX-2000 datasheet and diagrams to the pump sample Add a PumpX-2000 datasheet and render every configured pump in the OpenUSD twin Aug 1, 2026
marcschier and others added 5 commits August 1, 2026 13:40
Three things made the rendered hall misread.

The aggregation demo shared the main aisle. Its line pumps sit on 1.2 m
centres while the configured pumps march along the same axis on 2.4 m
centres, so the two rows interleaved and the hall showed more machines
than a client could browse - the reported "5 pumps when only 4 started".
Line1 now stands in its own aisle behind the suction vessels, which is
also what it is: a composition demo, not part of the configured line.

Every machine wore a permanent alarm halo. StatusLight was authored
visibility = "inherited", so a pump nothing is bound to - an aggregated
line pump, or the one federated from another server - stood there showing
an alarm it never raised. It now defaults to invisible and only a live
supervision binding reveals it, which is what makes the halo mean
something.

The camera was a gantry shot down the line, framed for a single pump at
the origin. It is now an operator's viewpoint: eye height 1.65 m in the
aisle on the discharge side, pitched 7 degrees below horizontal, framing
every configured pump at a three-quarter angle with the vessels behind
them and the back aisle further back again. The framing was solved
against the projection rather than guessed, and holds from one pump up to
eight, so it does not need retuning for --pumps N. The floor is widened
to match; the old one ended at y = 4.2 and left the far pumps hanging
over black.

Verified in the viewport with --pumps 5: five machines in shot, each
turning at its own speed, no alarm showing unless its own supervision
state asserts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
The hall contained geometry that no longer belonged there. A demo
production line authored three empty pump bodies plus a "remote" pump
representing a machine on another server, none of which the connected
server simulates, so the viewport showed more pumps than the server had
and the extra ones never moved.

Remove them. `/Plant/Line1` and `remote-pump.usda` are gone, the
production line now aggregates plain topology objects without a
representation, and the stage serves two assets instead of three. What
renders is exactly the set of pumps the server simulates.

Replace the alarm light bulb with an alarm ring. A red annulus is
authored around each pump and made visible while the pump is in an error
condition, so the fault reads at a glance from any angle instead of
depending on a small emissive lamp. The bearing overheat halo moves onto
the bearing bracket where the heat actually is, and the differential
pressure lamp binding retires with the lamp.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
Only one pump was visible in the viewport no matter how many the server
simulated. The bay positions were correct on the wire - each pump
published its own translation and the connector wrote it - but nothing
moved, so five machines rendered on top of each other.

A connector accumulates Translation, Rotation and Scale into a single
xformOp:transform matrix rather than authoring the three ops separately.
It has to: xformOpOrder is uniform, so an op order authored in the asset
layer cannot be cleared from the stronger layer the connector edits. USD
in turn evaluates only the ops named in xformOpOrder. The pump asset
named xformOp:translate, and its root prim named nothing at all, so every
matrix written to it was discarded in silence.

Declare xformOp:transform on the two prims a Translation target drives:
the pump root, which the bay layout positions, and the suction vessel's
fluid surface, which rides on the published level. The surface keeps its
authored base height by baking it into the identity matrix.

Add TransformBindingsTargetDeclaredXformOpsAsync, which resolves every
transform binding against the served asset and fails when a bound prim
does not declare the op a connector will author. Checked against real
OpenUSD composition: with the previous asset four of five pumps resolve
to (0,0,0); with this one each resolves to its own bay.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
The net472 build of Opc.Ua.Di.Tests failed with four CS0234/CS0246
errors, and took the CodeQL analysis down with it as a configuration
error.

PumpDeviceIntegrationServer matches AppTargetFrameworks and so does not
exist on net472; the test project already drops its project reference and
every Pump fixture that depends on it there. PumpDatasheetConformanceTests
was added to this branch without being added to that list, so on net472 it
alone still reached for Opc.Ua.Pumps, PumpNodeManager and PumpState.

Add it to the exclusion list next to the fixtures it belongs with.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
Two defects found in code review, both in the OpenUSD live-signal path.

Signal registration ran once per pump as it was materialised, but walked
every twin in the dictionary - and the pump being materialised was
already in that dictionary. So pump 1 was registered N times for N pumps,
pump 2 N-1 times, and so on. The publish set grew as N(N+1)/2, and every
tick re-wrote those Variables and called ClearChangeMasks on them once
per duplicate. Register the pump that was passed in instead.

The publish set was a plain List, unlike the sibling collections that
were made concurrent for exactly this reason. CreatePumpAsync adds a pump
to an already-running simulation, so appending to that list raced the
250 ms tick enumerating it: the tick would throw and stop the simulation
for every pump. Hold it as an immutable snapshot that TrackSignal
replaces under the existing registration lock, which leaves the tick both
allocation-free and lock-free.

EveryPumpPublishesItsShaftAngleExactlyOnce pins the first defect; it
fails with the previous registration loop.

Also drop a stale reference to the deleted remote-pump.usda from the
Plant.usda header.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: fe608993-f3c9-4779-a6b9-9a9eabfc24ba
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants