Commit Graph

4 Commits

Author SHA1 Message Date
Marc Schier da7412c429 Add native xRegistry events and distinct Resource versions (#4396)
## Summary

- support the xRegistry 0.6.0 distinct Resource/Version hierarchy:
  `Registry -> Group -> Resource -> Versions -> Version`
- instantiate stable logical Resource nodes separately from exact
Version nodes
  while preserving existing Version NodeIds
- delegate logical Resource FileType operations and Version-view
metadata to the
selected default Version with handle pinning and shared writer
reservations
- make Resource and Version delete behavior role-stable and use
`MetaEpoch` /
  `Epoch` respectively
- use stable Resource SourceNodes, exact Version SourceNodes and the
  `Server -> Registry -> Group -> Resource -> Version` notifier chain
- keep a model-metadata-gated client fallback for 0.5 flat Servers; 0.6
Servers
  expose no legacy aliases
- target `HasWoTProjection` at the real stable logical Resource node
- use `AsyncCustomNodeManager` for the registration, fast-path, and
federation managers,
  with awaited lifecycle, node mutations, event delivery, and cleanup
- handle startup rollback, reentrant deletion callbacks, undisclosed
file handles,
  and retryable cleanup without blocking unrelated file operations

## Authoritative dependencies

- companion specification: OPCF-Members/spec-drafts#32 at
  `d3399fca025025a73e5a6b599f850a81d985c060`
- OPC UA binding: xregistry/spec#568
- xRegistry event semantics: xregistry/spec#563, merged as
  `66ee31e47fbadff6ab982ae1516f7d9065e46b64`
- companion NodeSet SHA-256:
  `369f5f3f1814b333e4eeb52bfb4b6a730cdff372505a5f91a54c94f0016ac4cf`

## Review feedback

- keep the WoT Connectivity information-model designation at `1.1`;
draft revisions update the publication date without changing the model
version
- eliminate all 198 unique CA1861 diagnostics across 54 files and remove
seven obsolete project suppressions
- fix the analyzer-clean touched-path CA1307 and the hierarchy follow-up
CA1846/CA1851 diagnostics without dropping legacy-TFM compatibility

- recover the reconcile queue after callback faults/cancellation and
continue accepted pending changes
- remove migration guidance for the xRegistry managers, which are new in
2.0

## Compatibility

- public WoT Connectivity model designation remains `1.1`
- generated event-record ABI, constructors, options, NodeIds, and stored
documents remain compatible
- the generic xRegistry managers are new in 2.0 and implement
`IAsyncNodeManager`;
use `IAsyncNodeManagerFactory` and the async lifecycle hooks (see
`docs\XRegistry.md`)
- persistence schema and document blobs remain compatible unless noted
below

## Validation

- merge validation at `980422f76` with upstream `e8bec27d5`: 454
targeted WoT/materialization/model/registry cases and all 327 XRegistry
cases passed on both net10.0 and net48; all 29 net10 aggregation sample
cases passed
- feedback regressions at `308e8ba74`: 55 targeted queue and live
registry event/projection/lifecycle cases passed on each of net10.0 and
net48
- prior CI: all 266 checks passed at `6489a0908`; the unchanged Ubuntu
Core certificate-counter test and dependent jobs passed on the one-time
retry ([run 34117304035, attempt
2](https://github.com/OPCFoundation/UA-.NETStandard/actions/runs/34117304035/attempts/2))
- `Opc.Ua.XRegistry.Tests` — 327 passed on net10.0 and net48 for
migration commit `32c914aaf`
- `Opc.Ua.WotCon.Tests` net10.0 — 1348 passed
- `Opc.Ua.WotCon.Bindings.Tests` net10.0 — 683 passed
- `Opc.Ua.SourceGeneration.Core.Tests` net10.0 — 3807 passed, 8 skipped
- `Opc.Ua.SourceGeneration.Tests` net10.0 — 145 passed
- `Opc.Ua.SourceGeneration.Stack.Tests` net10.0 — 94 passed
- affected XRegistry and WoT client/server projects build successfully
- xRegistry NodeSet is byte-identical to the companion artifact
- repository-wide CA1861 inventory — 198 unique diagnostics across 54
files reduced to zero
- targeted changed-fixture validation — 1,098 passed, 3 skipped, 0
failed
- constrained full `UA.slnx` net10.0 Release build — CA1861 = 0
- `Opc.Ua.XRegistry.Client` net472 and netstandard2.1 builds — 0
warnings, 0 errors
- independent shared-array safety review — no significant issues
- `git diff --check`

The final review regressions cover concurrent reconcile/open, synthetic
logical
handle collisions across default switches, cross-session Close
isolation,
shared writer reservations, logical FileType property/change
propagation,
logical Labels delegation, role-fixed deletion, event sources,
model-version
gating, and stable `HasWoTProjection` targets.

The companion specification and protocol binding are reviewed in the
linked PRs above.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 5c79a1ed-6dab-4522-a9fc-6548caa78ad5
Copilot-Session: b605797f-a97a-4cd0-8f12-016d33c2fd88
Copilot-Session: 7ef369a5-2307-4f71-8a11-15af0e72f240
Copilot-Session: e716d455-8397-4d22-9c5a-398847ab7317
2026-09-08 04:46:40 +00:00
Marc Schier c84c168585 Merge OpenUSD scene packages (#4384)
# Description

Merge the draft OpenUSD Scene Materialization projects into the existing
OpenUSD core and server
packages:

- move the scene companion model, document/value model, and `.usda`
conversion APIs into
  `Opc.Ua.OpenUsd`;
- move discovery, materialization, export, and Part 1 interop into
`Opc.Ua.OpenUsd.Server`;
- rename the CLR APIs to `Opc.Ua.OpenUsd.Scene`,
`Opc.Ua.OpenUsd.Scene.Conversion`, and
  `Opc.Ua.OpenUsd.Server.Scene`;
- remove the `Opc.Ua.OpenUsdScene` and `Opc.Ua.OpenUsdScene.Server`
projects and NuGet package IDs;
- update the connector, the consolidated OpenUSD test project, package
READMEs, and developer docs.

This is an intentional source- and binary-breaking cleanup for the draft
Scene packages. The OPC UA
Scene model URI, version, NodeIds, and wire behavior remain unchanged.

## Related Issues

- Fixes #4383

## Validation

- `UA.slnx` Release builds completed for `CustomTestTarget=net10.0` and
`CustomTestTarget=net48`.
- OpenUSD tests passed on net10.0 (915) and net48 (879), excluding the
existing host-culture
assertion; the isolated assertion passed under invariant globalization.
- The generated Release pack contains the expected 75 packages, emits
neither retired Scene
package, and passes
`.azurepipelines\validate-source-generator-packages.ps1`.
- The two surviving packages expose the new nested namespaces and
contain no retired package
  dependencies.

## Checklist

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

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-09-02 12:06:52 +00:00
Marc Schier 0f257c05f0 OpenUSD scene: replace object in the USD value model with a UsdValue union (#4160)
Fixes #4151.

## The problem

```csharp
public object? Value { get; set; }
public SortedList<double, object?> TimeSamples { get; }
public IDictionary<string, object?> Metadata { get; }
```

`object` in public API, and every consumer forced into type tests and
casts. It was also why `UsdSceneExporter` had to call
`Variant.AsBoxedObject(Variant.BoxingBehavior.Legacy)`, itself against
guidance.

`Variant` cannot stand in for it. The USD value model is recursive and
**ragged**: tuples (`float3`), arrays, *arrays of tuples* (`color3f[]`),
matrices authored as a tuple of row tuples, plus asset paths and prim
path references that must round-trip as their own syntax.
`Variant`/`MatrixOf<T>` cover rectangular scalar arrays but not the
nested "array of tuple" shape.

## What this does

Introduces `UsdValue`, a readonly struct scoping a value to the shapes a
`.usda` document can express, and converts the model to it end to end -
reader, writer, coercion and signature all speak `UsdValue`, so there is
one representation rather than conversions at the edges.

- `INullable`, so absence is `UsdValue.Null` / `.IsNull` - never
`UsdValue?`.
- `TryGet*` accessors only. No boxing accessor, nothing returning
`object`.
- `ArrayOf<UsdValue>` for the recursive tuple/array/matrix cases.
- `Tuple` and `Array` stay distinct kinds because USD prints `(1, 2, 3)`
and `[1, 2, 3]` differently, as do `Token` and `String`.
- `Decoerce` now takes the `Variant` directly and reads it through its
typed accessors, which is what **removes the `AsBoxedObject` call**.

**The attribute's `TypeName` stays authoritative for rendering**, so the
kinds add type safety without changing a single byte of emitted `.usda`.
That is what makes this provably lossless: the existing fidelity and
round-trip suites pass **with their expectations untouched**.

## Three defects the tests caught

Worth calling out, because each would have shipped silently:

1. **Georeference dual authoring** read values through an `object`-typed
`TryToDouble`, which stopped seeing CLR primitives once values became
`UsdValue`.
2. **A `double3` scalar was emitted as `[1.0, 2.0, 3.0]`** instead of
`(1.0, 2.0, 3.0)`, because the writer picked brackets from the value's
kind. The shape is decided by the TypeName, not the container - so
`UsdVal` now renders a composite as a parenthesised tuple unless the
type is an array type. This is the same contract the H-1 regression
guard documents.
3. **`UsdValue` did not load at all on .NET Framework.** Storing
components as `ArrayOf<UsdValue>` made the struct layout recursive
(`UsdValue` -> `ArrayOf<UsdValue>` -> `ReadOnlyMemory<UsdValue>`);
CoreCLR resolves that, the net48 type loader throws `TypeLoadException`
on every use. Only the net48 run surfaced it - net10.0 was entirely
green. The payload is now a plain array, a reference type, which breaks
the cycle while the public accessors still hand out `ArrayOf<UsdValue>`.

## Review feedback addressed

- `UsdValue.GetHashCode` folds the dictionary entries (an
order-independent sum of key/value hashes) instead of only the entry
count, so same-size dictionaries no longer all collide.
- `UsdValue.ToString` renders a `Dictionary` as its entries ordered by
key instead of falling through to the empty-string default, which the
metadata fallback paths rely on.
- A `uint64` above `long.MaxValue` is no longer cast to `long` (which
wrapped `ulong.MaxValue` into `-1`) - it is preserved as its invariant
decimal text for scalars, arrays and matrices alike. `TryAsUInt64` reads
that form back, and `UsdaReader` now parses an over-long integral
literal as a token holding its exact digits instead of throwing
`OverflowException`, so the value survives the whole round trip.

## Validation

- Full `dotnet build UA.slnx -m:1 /p:UseSharedCompilation=false`: **0
errors, 0 warnings**.
- `Opc.Ua.OpenUsd.Tests`: **772 passing on net10.0 and on net48** (632
existing + 140 new tests), with 99.0% patch coverage over the changed
source lines.
- `Opc.Ua.Di.Tests` 364, `Opc.Ua.Robotics.Tests` 103 - unchanged.
- No `object`/`object?` left in the `Opc.Ua.OpenUsdScene` public API
except the `Equals(object?)` override; no `AsBoxedObject`/`AsBoxedValue`
anywhere in the OpenUSD assemblies.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8cbb8cd0-f0cb-4ab0-bea2-6202fbf69485
Copilot-Session: d089bd1c-b795-4f2b-a872-eb3090c11536
2026-08-03 18:46:42 +00:00
Marc Schier 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