Skip to content

[SourceGeneration] Improve NodeSet to ModelDesign conversion and generated node state - #4132

Open
marcschier wants to merge 8 commits into
marcschier/wot-03-converterfrom
marcschier/wot-02-sourcegen
Open

[SourceGeneration] Improve NodeSet to ModelDesign conversion and generated node state#4132
marcschier wants to merge 8 commits into
marcschier/wot-03-converterfrom
marcschier/wot-02-sourcegen

Conversation

@marcschier

Copy link
Copy Markdown
Collaborator

Summary

  • Improves NodeSet to ModelDesign conversion for combined and multi-namespace NodeSets.
  • Fixes generated method declarations, argument resolution, object type proxies, fluent builders, generated node state, shared generation helpers, and validation.
  • Regenerates ISA95 NodeIds as generator output from these changes, not as a hand edit.

Stack context

This is PR 2 of a stacked split of integration PR #4093 into independently reviewable pieces. This PR is independent of the other stack PRs and contains no WoT content.

Validation

  • dotnet build tests\Opc.Ua.SourceGeneration.Core.Tests\Opc.Ua.SourceGeneration.Core.Tests.csproj -c Release -p:CustomTestTarget=net10.0 -v:m
  • dotnet test tests\Opc.Ua.SourceGeneration.Core.Tests\Opc.Ua.SourceGeneration.Core.Tests.csproj -c Release -p:CustomTestTarget=net10.0 --no-build
  • dotnet build tests\Opc.Ua.SourceGeneration.Core.Tests\Opc.Ua.SourceGeneration.Core.Tests.csproj -c Release -p:CustomTestTarget=net48 -v:m
  • dotnet test tests\Opc.Ua.SourceGeneration.Core.Tests\Opc.Ua.SourceGeneration.Core.Tests.csproj -c Release -p:CustomTestTarget=net48 --no-build
  • dotnet build src\Opc.Ua.ISA95\Opc.Ua.ISA95.csproj -c Release -f net10.0 -v:m
  • Confirmed ISA95 build leaves src\Opc.Ua.ISA95\Design\Common\Opc.ISA95.NodeIds.csv with no unstaged regeneration diff.

Fixes several code generation defects that show up on combined and
multi-namespace NodeSets.

A standalone node that is already a method type, with no owning parent and the
conventional MethodType name, is no longer given a synthesized declaration; that
previously emitted a spurious "<Name>MethodTypeMethodType" node. Where a NodeSet
already ships a method type declaration explicitly, the existing declaration is
reused rather than emitting two identifiers with the same name.

Method argument resolution, object type proxies, fluent builders and the node
state generator are updated accordingly, along with the shared generation
helpers and model design validation.

The ISA95 NodeIds are regenerated as a consequence of these changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
Copilot AI review requested due to automatic review settings July 30, 2026 18:10

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 improves OPC UA NodeSet → ModelDesign conversion and downstream code generation to better handle multi-namespace/combined NodeSets, method argument naming collisions, and correct runtime decoding/registration of generated nodes.

Changes:

  • Added canonical C# identifier generation and improved string-literal escaping for generated code.
  • Improved NodeSet import to better match ModelCompiler conventions (placeholder browse names, method declarations, DataTypeEncoding parenting).
  • Updated generators (NodeState/ObjectType proxies/Fluent builders/NodeManager templates) to use stable method-argument symbol allocation and correct decoding contexts; added/expanded regression tests and fixtures.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tools/Opc.Ua.SourceGeneration.Core/Shared/SourceGenerationUtils.cs Adds ToCSharpIdentifier and improves literal escaping for generated code.
tools/Opc.Ua.SourceGeneration.Core/Schema/NodeSetToModelDesign.cs Fixes placeholder symbolic-name mapping, method argument naming, method-type reuse, and DataTypeEncoding parenting.
tools/Opc.Ua.SourceGeneration.Core/Schema/ModelDesignValidator.cs Ensures generated method-argument code names are assigned during import/validation.
tools/Opc.Ua.SourceGeneration.Core/Schema/ModelDesignExtensions.cs Introduces scoped, collision-aware generated names for method arguments (non-serialized).
tools/Opc.Ua.SourceGeneration.Core/Generators/ObjectTypeProxyGenerator.cs Uses scoped argument identifiers and correct message context for decoding structured outputs.
tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs Uses scoped argument identifiers, correct decoding context, and handles argument value materialization from model parameters.
tools/Opc.Ua.SourceGeneration.Core/Generators/NodeManagerTemplates.cs Adds __FindByDataTypeId to support VariableFromDataTypeId(...) in fluent APIs.
tools/Opc.Ua.SourceGeneration.Core/Generators/MethodDesignArgumentResolver.cs Refactors argument resolution and assigns generated argument names as a side effect.
tools/Opc.Ua.SourceGeneration.Core/Generators/FluentBuilderGenerator.cs Adds pass-through VariableFromDataTypeId overloads and minor doc formatting.
tests/Opc.Ua.SourceGeneration.Core.Tests/Shared/SourceGenerationUtilsTests.cs Adds unit tests for new identifier/literal escaping helpers.
tests/Opc.Ua.SourceGeneration.Core.Tests/Schema/NodeSetToModelDesignTests.cs Adds regression test covering preservation of runtime argument names.
tests/Opc.Ua.SourceGeneration.Core.Tests/Schema/ModelDesignExtensionsTests.cs Adds tests for scoped method-argument name allocation and serialization behavior.
tests/Opc.Ua.SourceGeneration.Core.Tests/Resources/SameNamespaceEncoding.NodeSet2.xml Adds fixture covering DataTypeEncoding nodes with same-namespace ParentNodeId.
tests/Opc.Ua.SourceGeneration.Core.Tests/Resources/SameNamedMethodArguments.NodeSet2.xml Adds fixture covering argument naming/pathological characters and collisions.
tests/Opc.Ua.SourceGeneration.Core.Tests/Resources/MethodArgumentNamespace.NodeSet2.xml Adds fixture covering method-argument datatype namespace resolution at runtime.
tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/ObjectTypeProxyGeneratorTests.cs Adds regression test for structured output decode using session message context.
tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/NodeStateGeneratorTests.cs Adds generator-level regressions for encoding nodes and argument symbol stability.
tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/FluentBuilderGeneratorTests.cs Updates assertions to match new codegen behaviors and file names.
tests/Opc.Ua.SourceGeneration.Core.Tests/CompilerUtils.cs Adds helpers to create in-memory AdditionalText and collect generator diagnostics.
src/Opc.Ua.ISA95/Design/Common/Opc.ISA95.NodeIds.csv Regenerates NodeIds to reflect placeholder symbolic-name mapping changes.

Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs Outdated
Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.94118% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.92%. Comparing base (8a10139) to head (2048f38).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
...urceGeneration.Core/Schema/NodeSetToModelDesign.cs 67.64% 8 Missing and 3 partials ⚠️
...rceGeneration.Core/Schema/ModelDesignExtensions.cs 91.00% 1 Missing and 8 partials ⚠️
...rceGeneration.Core/Shared/SourceGenerationUtils.cs 88.09% 2 Missing and 3 partials ⚠️
...ceGeneration.Core/Generators/NodeStateGenerator.cs 96.25% 1 Missing and 2 partials ⚠️
...Ua.Server/NodeManager/NodeStateLookupExtensions.cs 75.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4132      +/-   ##
==========================================
- Coverage   80.23%   79.92%   -0.32%     
==========================================
  Files        1515     1524       +9     
  Lines      209980   210992    +1012     
  Branches    36213    36387     +174     
==========================================
+ Hits       168479   168627     +148     
- Misses      28867    29693     +826     
- Partials    12634    12672      +38     
Files with missing lines Coverage Δ
src/Opc.Ua.Di.Server/DiNodeManager.cs 69.65% <100.00%> (+0.09%) ⬆️
src/Opc.Ua.Server/Fluent/FluentNodeManagerBase.cs 63.82% <100.00%> (+0.78%) ⬆️
src/Opc.Ua.Server/Fluent/NodeManagerBuilder.cs 90.07% <100.00%> (+2.03%) ⬆️
...neration.Core/Generators/FluentBuilderGenerator.cs 86.09% <100.00%> (+0.17%) ⬆️
...on.Core/Generators/MethodDesignArgumentResolver.cs 80.00% <100.00%> (+11.57%) ⬆️
...Generation.Core/Generators/NodeManagerTemplates.cs 100.00% <100.00%> (ø)
...ration.Core/Generators/ObjectTypeProxyGenerator.cs 84.79% <100.00%> (+1.06%) ⬆️
...urceGeneration.Core/Schema/ModelDesignValidator.cs 71.97% <100.00%> (+0.01%) ⬆️
...Ua.Server/NodeManager/NodeStateLookupExtensions.cs 92.30% <75.00%> (-7.70%) ⬇️
...ceGeneration.Core/Generators/NodeStateGenerator.cs 87.37% <96.25%> (-0.17%) ⬇️
... and 3 more

... and 59 files with indirect coverage changes

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

Resolve method inputs and outputs together where callers need both, and move generated code-name assignment into the generator scopes that consume those names.

Clarify that description assignment already targets a NodeState-typed local, and avoid repeated XmlQualifiedName allocations for method argument browse names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
The fluent builder generator emits calls to INodeManagerBuilder.VariableFromDataTypeId
and to a NodeManagerBuilder constructor overload that carries the data-type lookup, but
the runtime side of that API was missing, so every generated node manager failed to
compile with CS1729 and CS1061.

Adds the VariableFromDataTypeId resolution to the builder interface and implementation,
along with NodeStateLookupExtensions.FindByDataType, which is the lookup the builder
delegates to and has no other consumer.

Resolution reports BadNodeIdInvalid for a null data type, BadNodeIdUnknown when nothing
matches, BadBrowseNameDuplicated when the match is ambiguous, and BadTypeMismatch when
the resolved node is not a variable. An optional browse name disambiguates a data type
that is carried by more than one variable.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
@marcschier

Copy link
Copy Markdown
Collaborator Author

Fixed the net10.0 solution build failure on this PR.

The FluentBuilderGenerator in this change emits calls to
INodeManagerBuilder.VariableFromDataTypeId and to a NodeManagerBuilder constructor
overload carrying the data-type lookup, but the runtime side of that API was not part of
this PR - it had been grouped into a later server PR that does not exist yet. Every
generated node manager therefore failed to compile:

BoilerNodeManager.NodeManager.g.cs(97,31): CS1729: 'NodeManagerBuilder' does not
  contain a constructor that takes 7 arguments
BoilerNodeManager.FluentBuilders.g.cs(140,24): CS1061: 'INodeManagerBuilder' does not
  contain a definition for 'VariableFromDataTypeId'

A generator change and the API it emits against have to land together, so the builder
API now ships here: VariableFromDataTypeId on the interface and implementation, plus
NodeStateLookupExtensions.FindByDataType, which is the lookup it delegates to and
which has no other consumer. FindByDataType was sitting unused in #4147 and has been
removed there in the same pass.

Also brings the eight NodeManagerBuilderTests cases covering the new resolution:
unique match, null data type (BadNodeIdInvalid), no match (BadNodeIdUnknown),
ambiguous match (BadBrowseNameDuplicated), browse-name disambiguation, browse-name
miss, duplicate browse name, and a non-variable node (BadTypeMismatch).

Verified locally:

  • MinimalBoilerServer and MinimalCalcServer both build clean - these are the two
    projects that were failing in CI.
  • Opc.Ua.Server.Tests net10.0: 3841 passed, 0 failed.
  • Opc.Ua.Server.Tests net48: 3840 passed, 1 failed - DurableDataValueQueueVerifyReferenceBatchingAsync,
    a known local file-lock flake unrelated to this change, verified passing in isolation.
  • 0 warnings, 0 errors on both target frameworks.

marcschier and others added 4 commits July 31, 2026 14:56
DiNodeManager constructed NodeManagerBuilder without the data-type resolver, so
VariableFromDataTypeId reported BadNodeIdUnknown ("no predefined variable has DataType")
for every DI node manager - a misleading error, since the lookup had simply never
been supplied rather than the variable being absent.

Delegates to NodeStateLookupExtensions.FindByDataType rather than hand-rolling the
scan a fourth time in this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
…ourcegen

# Conflicts:
#	src/Opc.Ua.Di.Server/DiNodeManager.cs
…ourcegen

# Conflicts:
#	tests/Opc.Ua.SourceGeneration.Core.Tests/Schema/NodeSetToModelDesignTests.cs
#	tools/Opc.Ua.SourceGeneration.Core/Generators/FluentBuilderGenerator.cs
#	tools/Opc.Ua.SourceGeneration.Core/Generators/MethodDesignArgumentResolver.cs
#	tools/Opc.Ua.SourceGeneration.Core/Generators/ObjectTypeProxyGenerator.cs
#	tools/Opc.Ua.SourceGeneration.Core/Schema/ModelDesignValidator.cs
#	tools/Opc.Ua.SourceGeneration.Core/Schema/NodeSetToModelDesign.cs
@marcschier
marcschier changed the base branch from master to marcschier/wot-03-converter August 1, 2026 11:54
The generator no longer derives placeholder accessor names by sanitising the
angle brackets of the placeholder browse name, which produced identifiers such as
AddxBinding_ for a <Binding> placeholder. It now emits the intent directly, as
AddBinding_Placeholder.

The OpenUsd and OpenUsdScene server projects still called the old sanitised names,
so they no longer compiled. Updates the thirteen call sites and the comments that
referenced them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 9e6a5abf-3299-4cd1-9855-010fedbf0ad8
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.

3 participants