Files
romanett 442889cba5 Fix source generator audit findings; conform structure-field matrices and type metadata to OPC UA 1.05 (#4569)
# Description

This PR fixes a bug audit of the source generation projects
(`tools/Opc.Ua.SourceGeneration*`: model loading, NodeSet import, all
emitters, the Roslyn hosts and packaging). It also fixes a max-effort
code review of those fixes, and a check of every spec-relevant change
against the OPC UA 1.05 text.

- **Scope:** about 85 audit findings, 48 review findings and 32
spec-grounding items were addressed. Each fix has a regression test that
fails without it.
- **Wire change:** it includes one deliberate, stack-wide wire change:
**structure-field matrices now follow OPC 10000-6 5.2.5 Table 28**.

Most changes are internal correctness fixes. The sections below list
every change a user can observe: wire formats, generated code and API,
generated address-space content, diagnostics and runtime behaviour.

## Behaviour / user-facing changes

### Wire format (encoders)
- **Binary inline matrices** (OPC 10000-6 5.2.5 Table 28).
- **Layout:** a multi-dimensional *structure field* is encoded as
`Int32[] Dimensions`, then the values **without a length prefix**, with
at least 2 dimensions.
- **Empty and null:** any dimension <= 0 means no values follow, and a
null matrix is `-1`. An empty matrix is written as `[0,0]`.
- **Compatibility:** this is **not compatible with earlier builds of
this SDK** for matrix fields. It applies to generated types,
`[DataType]` types, `Structure`, client ComplexTypes and
`WriteEncodeableMatrix`.
  - **Unchanged:** Variant matrices (5.2.2.16) keep their old encoding.
- **Declared rank decides matrix vs array.** A 1-D `MatrixOf<T>` in an
array field or a Variable value is written as an array again.
Matrix-declared fields go through the new public
`EncoderExtensions.WriteInlineMatrixValue`, which generated code also
uses. A non-empty 1-D value in a matrix field throws `BadEncodingError`.
- **Decoder hardening:**
- Before allocating, the decoder bounds all matrix dimensions against
overflow, `MaxArrayLength` and the remaining message length.
  - The decoder rejects:
    - a matrix with fewer than 2 dimensions;
- a populated matrix whose rank differs from the field's declared rank;
    - a union SwitchField greater than the number of fields;
    - an EncodingMask with unassigned bits set.
- **Encoder `MaxArrayLength`:** the encoder enforces `MaxArrayLength`
for inline matrices, as it already did for arrays.
- **XML structure fields** (5.3.1, 5.3.4, 5.3.5).
- **Fields:** `Structure` and client ComplexTypes write fields like
typed fields (`<A>1</A>`, array elements directly) instead of a Variant
body.
- **Matrix fields:** `Dimensions` and `Elements` go directly inside the
field element, with no `<Matrix>` wrapper.
- **Empty matrices:** written as nil, because XML dimensions must be
greater than 0.
  - **Compatibility:** decoders still accept the old shapes.
- **Enum fixes:** enum array elements with value 0 are no longer
dropped, and enum array element names are fixed.
- **Other encoding fixes:**
- A null `DiagnosticInfo` structure field writes `0x00` instead of
nothing.
- A null scalar field in a runtime `Structure` is written as its default
instead of zero bytes, which used to shift every following field.
- A Variant holding an empty matrix encodes as an empty array instead of
throwing.
  - `Variant.Copy` keeps the matrix identity of null and empty matrices.
- **NodeSet export and the DataContract surrogate** write Variable
values with the Variant rules again. A null or empty `MatrixOf` variable
no longer produces invalid XML or unreadable NodeSets.
- **`JsonEncoder.CanOmitFields`** is `true` only for Compact encoding.
Verbose and RawData JSON include default-valued fields.

### `[DataType]` source-generated types
- **Default-value handling** (OPC 10000-6 5.4.1, Table 1).
- **Encode:** a field is omitted only when its value *and* its declared
default are the type's default (0, false, null). Fields with a
non-default initializer or constructor default, and empty strings, are
always written, so configuration files get slightly larger.
- **JSON decode:** a field missing from JSON decodes to the type
default.
- **XML decode:** a field missing from XML keeps its declared default,
which older configuration files rely on.
- **Why:** this fixes silent round-trip loss, for example
`SamplingInterval = 0` read back as `-1`.
- **Nested `[DataType]` types:** they are now generated inside their
containing types. Without an explicit id, their default TypeId and
XmlName are qualified (e.g. `s=Outer.Foo`); **wire identity change** for
nested types. Colliding activator names get a `_2` suffix.
- **Activators:** they are `internal` when the type, or any containing
type, is not public (was CS0060).
- **Matrix properties:**
  - `MatrixOf<T>` properties use the inline matrix encoding.
- Matrices of non-sealed structures, or with
`StructureHandling.ExtensionObject`, are written as ExtensionObjects.
- Their published ValueRank is 2, because the rank can't be known from
`MatrixOf<T>`.
- **Published StructureDefinitions:**
  - They include base-class fields.
  - Nullable properties are no longer marked optional.
  - Inline structure fields name their concrete type.
- **`[Flags]` enums:** they are published as plain enumerations with
their member values, which matches how they are encoded.
- **Other generated code:**
- `Clone` deep-copies arrays and matrices of structures, Variants,
ExtensionObjects and DataValues.
- `IsEqual` and `GetHashCode` are consistent: EncodingMask, active union
member and NaN handling agree.
- Pooled `Reuse()` restores the freshly constructed state through the
new `protected virtual ResetForReuse()`, and returns an instance to its
runtime type's pool.
- **Enum capture:** enum member values are captured culture-invariantly.
Negative values were lost on cultures that use U+2212 as the minus sign.

### Model-driven generator: generated code, API and file names
- **Removed API:** generated `*_Placeholder` proxy accessors (e.g.
`PowerTrainTypeClient.GetMotorIdentifier_PlaceholderAsync`) and fluent
`X_Placeholder()` accessors are removed; they could never resolve
anything.
- Proxy accessors now look children up by BrowseName with the right
namespace.
- `RoboticsClient` finds motors and gears by type and drives via
`IsDrivenBy`. Its topology snapshots used to be empty.
- **Renamed files:** generated files for a bound `[NodeManager]` are now
named `{Namespace}.{Class}.NodeManager.g.cs` (also `.Factory` and
`.FluentBuilders`).
- **Generated NodeState code:**
- Attributes are set through `baseState`/`nodeState` locals, so children
named like base attributes (e.g. `DataType`, `EventNotifier`) compile.
- Children that hide base members are declared `public new`, using names
reflected from the runtime.
  - `global::` qualification is used throughout.
- **Proxies and fluent builders:** a proxy whose supertype is excluded
(e.g. Draft) derives from the nearest emitted ancestor. Fluent wrappers
and NodeState factories fall back the same way; they used to fail with
CS0246.
- **Derived structures:**
  - The optional-field `EncodingMask` continues across inheritance.
- A derived structure over a base type from another model no longer
relies on `base.ResetForReuse`.
- **Default values:**
- NodeId, QualifiedName and ExpandedNodeId defaults resolve by namespace
URI (`context.NamespaceUris.GetIndexOrAppend`) and server URI.
- StatusCode, negative enum, `double`/`float` (shortest round-trip
literal) and escaped string defaults now compile.
- **Event records:**
- Vendor event records have `Decoder.GetStandardFields(NamespaceTable)`,
and their browse paths use the model's namespace.
- A field whose namespace is unknown to the session no longer matches a
namespace-0 field.
  - Companion `StandardFields` is `[Obsolete]`.
- Vendor simple subtypes such as `Percent : Double` map to their
built-in type.
- **Other fixes:**
- `MethodState.CallAsync` passes the real `objectId` to synchronous
`OnCallMethod2` handlers; it used to pass `NodeId.Null`.
- Enumeration fields with an explicit `Identifier` keep it, instead of
taking a trailing `_<digits>`.
  - Explicit NumericIds are no longer reused by automatic numbering.
  - Dependencies are loaded in topological order.

### Generated address-space content
- **Arguments:**
- Methods without arguments no longer get empty
`InputArguments`/`OutputArguments` properties (OPC 10000-3 5.7).
- About 2,660 automatically assigned namespace-0 NodeIds of 15000 and
above shift. Every NodeId in `StandardTypes.csv` is unchanged.
- **Permissions:** type nodes and method declarations on types now carry
the model's `RolePermissions`/`AccessRestrictions`.
- **Restored nodes:** NodeSet import keeps nodes that were silently
dropped before:
- `NamespaceMetadata` objects and the NodeSet's own TypeDictionary
variables;
  - ISA95 placeholders linked only non-hierarchically;
  - instances whose parent is in another namespace.
- **Import details:**
  - ValueRank > 1 without ArrayDimensions keeps its rank.
  - `svr=` indexes map correctly.
  - Invalid MinimumSamplingInterval values import as -1, not 0.
  - Localized texts prefer the invariant or English translation.
- **StructureDefinitions:**
- They include inherited fields, including for subtypes without their
own fields (e.g. `AnonymousIdentityToken` now publishes PolicyId).
  - The StructureType kind covers inherited AllowSubTypes fields.
  - Unions ignore IsOptional.
- StructureFields never publish ValueRank 0; a OneOrMoreDimensions field
without dimensions becomes rank 2.
- Fields with ValueRank -2/-3 are published as scalar BaseDataType,
which is what is on the wire.
- **OptionSets:**
  - Implicit fields get the next free bit.
- `OptionSetValues` uses null for undefined bits instead of "Reserved",
and structure OptionSets publish all their bits (no 32/96-bit cap).
- Numeric OptionSets are limited to their base type's width, and UInt64
bit 63 works.
- **Schemas:**
- Structures with inline-matrix fields are left out of the `.bsd`
(5.2.5).
- In the BSD, Variant-encoded and non-structure subtyped fields are
described as `ua:Variant`.
- In the `.xsd`, matrix fields are typed `ua:Matrix` with
`Dimensions`/`Elements`, and `EncodingMask` is required.

### NodeSet / model input handling
- **Model selection:**
- The newest NodeSet of a model is chosen by `ModelVersion` (SemVer 2.0
precedence), then PublicationDate. The `Version` attribute, which Annex
F says is not for comparison, is only a tie-break.
- The order is total and does not depend on input order; referenced
assemblies use the same rule.
- **Default names:**
- Invalid default Name/Prefix values are repaired (`201301ISA95` becomes
`_201301ISA95`, `class` becomes `class_`).
  - Valid names are unchanged.
  - Sanitising no longer depends on the OS.
- **`ModelUri` metadata** that doesn't match the NodeSet raises a
warning instead of silently losing the model.
- **Input detection:**
- Only XML files whose root is `ModelDesign` or `UANodeSet` are treated
as models.
- A malformed NodeSet stays a NodeSet input, so it no longer breaks the
ModelDesign pass.
- **`IdentifierFile` paths:** they are normalized (`./`, `../`), and the
suffix match requires a folder boundary.
- **Dependency payloads** (`ModelDependencyV1`) carry field IsOptional,
AllowSubTypes, ArrayDimensions and the producer's namespace tables in
skippable trailers, so old and new readers stay compatible.

### Roslyn host, diagnostics, packaging
- **New diagnostics:**
- MODELGEN036: `[NodeManager]` target not supported (nested, generic,
file-local or global namespace).
- MODELGEN037: `[DataType]` target not supported (struct, generic,
abstract, file-local or global namespace).
- MODELGEN038: the definition of an encodeable base type can't be found.
- **New validator errors:**
- a structure with both optional and subtyped fields (OPC 10000-6 F.13);
  - multi-bit or duplicate OptionSet masks.
- **Diagnostic locations:** `[DataType]`/`[NodeManager]` diagnostics
point at real source locations again, so `#pragma` and `.editorconfig`
apply. MODELGEN010 is the exception.
- **Incremental caching:** pipeline values compare by value, so
unrelated edits no longer regenerate the whole model or all
`[DataType]`s.
- **`ModelSourceGeneratorIgnore`** on a ModelDesign input is honoured;
the design still resolves as a dependency.
- **Cancellation:** a cancelled generation run propagates
`OperationCanceledException` instead of reporting a MODELGEN003 error.
- **Stack package:** the `Stack` NuGet package now ships
`build/OPCFoundation.NetStandard.Opc.Ua.SourceGeneration.Stack.props`,
so `StackSourceGeneratorMode` and `StackSourceGeneratorExclude` reach
the compiler.

## Related Issues

- No tracking issue. The audit, review and spec-grounding reports are
available on request. Contains the matrix wire-format change discussed
for D-5 (OPC 10000-6 5.2.5 Table 28).

## 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.
- [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.
(`docs/SourceGeneratedDataTypes.md`, `docs/NodeManagers.md`, generator
readme)
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings. (`UA.slnx` 0 warnings / 0 errors on net48 and
net10.0)
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed. The
affected suites were run on net10.0: SourceGeneration Core/Roslyn/Stack,
Types, Core.Encoders, Encoders.Fuzz, Client, Client.ComplexTypes,
PubSub, Core, Server, GDS, Configuration, Di, Robotics, ISA95,
Positioning, OpenUsd, XRegistry. net48 was built; its test legs, and the
post-rebase verification, run in CI.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:47:40 +02:00

2542 lines
109 KiB
C#

/* ========================================================================
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
*
* OPC Foundation MIT License 1.00
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* The complete license agreement can be found here:
* http://opcfoundation.org/License/MIT/1.00/
* ======================================================================*/
namespace Opc.Ua.SourceGeneration
{
/// <summary>
/// Template strings
/// </summary>
internal static class NodeStateTemplates
{
/// <summary>
/// Main file template for predefined nodes code generation
/// </summary>
public static readonly TemplateString File = TemplateString.Parse(
$$"""
{{Tokens.CodeHeader}}
{{Tokens.ListOfImports}}
namespace {{Tokens.NamespacePrefix}}
{
{{Tokens.ListOfTypes}}
{{Tokens.ListOfTypeActivators}}
}
""");
/// <summary>
/// File template for the model's NodeSet import factory provider
/// </summary>
public static readonly TemplateString NodeSetImportSupport_File = TemplateString.Parse(
$$"""
{{Tokens.CodeHeader}}
#pragma warning disable RCS0056 // A generated identifier can make a line exceed 120 characters
namespace {{Tokens.NamespacePrefix}}
{
{{Tokens.ListOfTypes}}
}
""");
/// <summary>
/// Main file template for predefined nodes code generation
/// </summary>
public static readonly TemplateString Extensions_File = TemplateString.Parse(
$$"""
{{Tokens.CodeHeader}}
{{Tokens.ListOfImports}}
namespace {{Tokens.NamespacePrefix}}
{
/// <summary>
/// Extensions that add functionality from the {{Tokens.NamespaceUri}} namespace.
/// </summary>
public static partial class {{Tokens.Namespace}}Extensions
{
/// <summary>
/// Creates and returns all node states for the {{Tokens.NamespaceUri}} namespace.
/// </summary>
/// <param name="nodes">The collection to add the node states to.</param>
/// <param name="context">The system context to use for initialization.</param>
/// <returns>Original collection with node states added.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public static global::Opc.Ua.NodeStateCollection Add{{Tokens.Namespace}}(
this global::Opc.Ua.NodeStateCollection nodes,
global::Opc.Ua.ISystemContext context)
{
{{Tokens.ListOfNodeStateInitializers}}
return nodes;
}
/// <summary>
/// Adds all nodestate activators of the {{Tokens.NamespaceUri}}
/// namespace to a INodeStateFactoryBuilder.
/// </summary>
/// <param name="builder">The node state factory builder.</param>
/// <returns>The builder passed as parameter for chaining.</returns>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public static global::Opc.Ua.INodeStateFactoryBuilder Add{{Tokens.Namespace}}(
this global::Opc.Ua.INodeStateFactoryBuilder builder)
{
{{Tokens.ListOfActivatorRegistrations}}
return builder;
}
{{Tokens.ListOfNodeStateInstanceFactories}}
{{Tokens.ListOfNodeStateTypeFactories}}
}
}
""");
/// <summary>
/// Object Type node state
/// </summary>
public static readonly TemplateString ObjectType_Class = TemplateString.Parse(
$$"""
/// <summary>
/// The {{Tokens.ClassName}} ObjectType state.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public partial class {{Tokens.ClassName}}State :
{{Tokens.BaseClassName}}State{{Tokens.BaseT}}
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}State"/> class.
/// </summary>
public {{Tokens.ClassName}}State(global::Opc.Ua.NodeState? parent)
: base(parent)
{
}
{{Tokens.ElementNamespaceOverride}}
{{Tokens.ListOfProperties}}
{{Tokens.ListOfNonMandatoryChildren}}
/// <inheritdoc/>
public override bool DeepEquals(global::Opc.Ua.NodeState node)
{
if (!(node is {{Tokens.ClassName}}State state) || !base.DeepEquals(state))
{
return false;
}
{{Tokens.ListOfEqualityComparers}}
return true;
}
/// <inheritdoc/>
public override int DeepGetHashCode()
{
int hashCode = base.DeepGetHashCode();
{{Tokens.ListOfChildHashes}}
return hashCode;
}
{{Tokens.ListOfChildOperations}}
/// <inheritdoc/>
protected override void CopyTo(global::Opc.Ua.NodeState target)
{
if (!(target is {{Tokens.ClassName}}State state))
{
return;
}
{{Tokens.ListOfChildCopies}}
base.CopyTo(target);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeState CreateCopy()
{
return new {{Tokens.ClassName}}State(null);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeId GetDefaultTypeDefinitionId(
global::Opc.Ua.NamespaceTable namespaceUris)
{
return global::Opc.Ua.NodeId.Create(
global::{{Tokens.NamespacePrefix}}.ObjectTypes.{{Tokens.TypeName}},
{{Tokens.NamespaceUri}},
namespaceUris);
}
/// <inheritdoc/>
protected override void Initialize(global::Opc.Ua.ISystemContext context)
{
base.Initialize(context);
Initialize(
context,
context.CreateInstanceOf{{Tokens.SymbolicId}}());
InitializeOptionalChildren(context);
}
/// <inheritdoc/>
protected override void Initialize(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
/// <inheritdoc/>
protected override void InitializeOptionalChildren(
global::Opc.Ua.ISystemContext context)
{
base.InitializeOptionalChildren(context);
{{Tokens.InitializeOptionalChildren}}
}
{{Tokens.ListOfFields}}
}
""");
/// <summary>
/// Method type node state
/// </summary>
public static readonly TemplateString MethodType_Class = TemplateString.Parse(
$$"""
/// <summary>
/// The {{Tokens.ClassName}} MethodType.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public partial class {{Tokens.ClassName}} : global::Opc.Ua.MethodState
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}"/> class.
/// </summary>
public {{Tokens.ClassName}}(global::Opc.Ua.NodeState? parent)
: base(parent)
{
}
/// <inheritdoc/>
public new static global::Opc.Ua.NodeState Construct(global::Opc.Ua.NodeState parent)
{
return new {{Tokens.ClassName}}(parent);
}
/// <inheritdoc/>
public {{Tokens.ClassName}}MethodCallHandler? OnCall;
/// <inheritdoc/>
public {{Tokens.ClassName}}MethodAsyncCallHandler? OnCallAsync;
{{Tokens.ListOfProperties}}
{{Tokens.ListOfNonMandatoryChildren}}
/// <inheritdoc/>
public override bool DeepEquals(global::Opc.Ua.NodeState node)
{
if (!(node is {{Tokens.ClassName}} state) || !base.DeepEquals(state))
{
return false;
}
{{Tokens.ListOfEqualityComparers}}
return true;
}
/// <inheritdoc/>
public override int DeepGetHashCode()
{
int hashCode = base.DeepGetHashCode();
{{Tokens.ListOfChildHashes}}
return hashCode;
}
{{Tokens.ListOfChildOperations}}
/// <inheritdoc/>
protected override void CopyTo(global::Opc.Ua.NodeState target)
{
if (!(target is {{Tokens.ClassName}} state))
{
return;
}
{{Tokens.ListOfChildCopies}}
base.CopyTo(target);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeState CreateCopy()
{
return new {{Tokens.ClassName}}(null);
}
/// <inheritdoc/>
protected override void Initialize(global::Opc.Ua.ISystemContext context)
{
base.Initialize(context);
Initialize(
context,
context.CreateInstanceOf{{Tokens.SymbolicId}}());
InitializeOptionalChildren(context);
}
/// <inheritdoc/>
protected override void InitializeOptionalChildren(
global::Opc.Ua.ISystemContext context)
{
base.InitializeOptionalChildren(context);
{{Tokens.InitializeOptionalChildren}}
}
/// <inheritdoc/>
protected override global::Opc.Ua.ServiceResult? Call(
global::Opc.Ua.ISystemContext _context,
global::Opc.Ua.NodeId _objectId,
global::Opc.Ua.ArrayOf<global::Opc.Ua.Variant> _inputArguments,
global::System.Collections.Generic.List<global::Opc.Ua.Variant> _outputArguments)
{
if (OnCall == null)
{
return base.Call(_context, _objectId, _inputArguments, _outputArguments);
}
global::Opc.Ua.ServiceResult? _result = null;
{{Tokens.ListOfInputArguments}}
{{Tokens.ListOfOutputDeclarations}}
if (OnCall != null)
{
{{Tokens.OnCallImplementation}}
}
{{Tokens.ListOfOutputArguments}}
return _result;
}
/// <inheritdoc/>
protected override async
global::System.Threading.Tasks.ValueTask<global::Opc.Ua.ServiceResult?>
CallAsync(
global::Opc.Ua.ISystemContext _context,
global::Opc.Ua.NodeId _objectId,
global::Opc.Ua.ArrayOf<global::Opc.Ua.Variant> _inputArguments,
global::System.Collections.Generic.List<global::Opc.Ua.Variant> _outputArguments,
global::System.Threading.CancellationToken cancellationToken = default)
{
if (OnCall == null && OnCallAsync == null)
{
return await base.CallAsync(
_context,
_objectId,
_inputArguments,
_outputArguments,
cancellationToken).ConfigureAwait(false);
}
{{Tokens.ClassName}}Result? _result = null;
{{Tokens.ListOfInputArguments}}
if (OnCallAsync != null)
{
{{Tokens.OnCallAsyncImplementation}}
}
else if (OnCall != null)
{
return Call(_context, _objectId, _inputArguments, _outputArguments);
}
{{Tokens.ListOfOutputArgumentsFromResult}}
return _result?.ServiceResult;
}
{{Tokens.ListOfFields}}
}
/// <summary>
/// Represents the result of a {{Tokens.ClassName}} method call.
/// </summary>
public partial class {{Tokens.ClassName}}Result
{
public global::Opc.Ua.ServiceResult ServiceResult { get; set; }
{{Tokens.ListOfResultProperties}}
}
/// <summary>
/// Handles the {{Tokens.ClassName}} method call.
/// </summary>
public delegate global::Opc.Ua.ServiceResult {{Tokens.ClassName}}MethodCallHandler(
{{Tokens.OnCallDeclaration}}
/// <summary>
/// Handles the asynchronous {{Tokens.ClassName}} method call.
/// </summary>
public delegate
global::System.Threading.Tasks.ValueTask<{{Tokens.ClassName}}Result>
{{Tokens.ClassName}}MethodAsyncCallHandler(
{{Tokens.OnCallAsyncDeclaration}}
""");
/// <summary>
/// Variable type node state
/// </summary>
public static readonly TemplateString VariableType_Class = TemplateString.Parse(
$$"""
/// <summary>
/// The {{Tokens.ClassName}} VariableType state.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public partial class {{Tokens.ClassName}}State :
{{Tokens.BaseClassName}}State{{Tokens.BaseT}}
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}State"/> class.
/// </summary>
public {{Tokens.ClassName}}State(global::Opc.Ua.NodeState? parent) : base(parent)
{
}
{{Tokens.ListOfProperties}}
{{Tokens.ListOfNonMandatoryChildren}}
/// <inheritdoc/>
public override bool DeepEquals(global::Opc.Ua.NodeState node)
{
if (!(node is {{Tokens.ClassName}}State state) || !base.DeepEquals(state))
{
return false;
}
{{Tokens.ListOfEqualityComparers}}
return true;
}
/// <inheritdoc/>
public override int DeepGetHashCode()
{
int hashCode = base.DeepGetHashCode();
{{Tokens.ListOfChildHashes}}
return hashCode;
}
{{Tokens.ListOfChildOperations}}
/// <inheritdoc/>
protected override void CopyTo(global::Opc.Ua.NodeState target)
{
if (!(target is {{Tokens.ClassName}}State state))
{
return;
}
{{Tokens.ListOfChildCopies}}
base.CopyTo(target);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeState CreateCopy()
{
return new {{Tokens.ClassName}}State(null);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeId GetDefaultTypeDefinitionId(
global::Opc.Ua.NamespaceTable namespaceUris)
{
return global::Opc.Ua.NodeId.Create(
global::{{Tokens.NamespacePrefix}}.VariableTypes.{{Tokens.TypeName}},
{{Tokens.NamespaceUri}},
namespaceUris);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeId GetDefaultDataTypeId(
global::Opc.Ua.NamespaceTable namespaceUris)
{
return global::Opc.Ua.NodeId.Create(
global::{{Tokens.DataTypeNamespacePrefix}}.DataTypes.{{Tokens.DataType}},
{{Tokens.DataTypeNamespaceUri}},
namespaceUris);
}
/// <inheritdoc/>
protected override int GetDefaultValueRank()
{
return {{Tokens.ValueRank}};
}
/// <inheritdoc/>
protected override void Initialize(global::Opc.Ua.ISystemContext context)
{
base.Initialize(context);
Initialize(
context,
context.CreateInstanceOf{{Tokens.SymbolicId}}());
// InitializeOptionalChildren(context);
}
/// <inheritdoc/>
protected override void Initialize(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
/// <inheritdoc/>
protected override void InitializeOptionalChildren(
global::Opc.Ua.ISystemContext context)
{
base.InitializeOptionalChildren(context);
{{Tokens.InitializeOptionalChildren}}
}
{{Tokens.ListOfFields}}
}
{{Tokens.TypedVariableType}}
{{Tokens.VariableTypeValue}}
""");
/// <summary>
/// Factory methods for variable states with typed values
/// </summary>
public static readonly TemplateString FactoriesForVariableTypeWithTypedValue = TemplateString.Parse(
$$"""
/// <summary>
/// Creates a new instance of the {{Tokens.ClassName}}State class with a built-in value type
/// and associated TBuilder to extract the value from the Variant and create a new Variant
/// from it.
/// </summary>
public static {{Tokens.ClassName}}State<T> For<T, TBuilder>(global::Opc.Ua.NodeState? parent)
where TBuilder : struct, global::Opc.Ua.IVariantBuilder<T>
{
return new {{Tokens.ClassName}}State<T>.Implementation<TBuilder>(parent);
}
""");
/// <summary>
/// Typed variable type node state
/// </summary>
public static readonly TemplateString VariableTypeWithTypedValue_Class = TemplateString.Parse(
$$"""
/// <summary>
/// The {{Tokens.ClassName}} VariableType state.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public abstract class {{Tokens.ClassName}}State<T> :
{{Tokens.ClassName}}State
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}State{T}"/> class.
/// </summary>
protected {{Tokens.ClassName}}State(global::Opc.Ua.NodeState? parent)
: base(parent)
{
}
/// <summary>
/// Creates a new instance of the <see cref="{{Tokens.ClassName}}State{T}"/>
/// class with a built-in value type and associated TBuilder to extract the
/// value from the Variant and create a new Variant from it.
/// </summary>
/// <typeparam name="TBuilder">The builder to use for T</typeparam>
public static {{Tokens.ClassName}}State<T> With<TBuilder>(
global::Opc.Ua.NodeState? parent = null)
where TBuilder : struct, global::Opc.Ua.IVariantBuilder<T>
{
return new Implementation<TBuilder>(parent);
}
/// <inheritdoc/>
public new abstract T Value { get; set; }
/// <inheritdoc/>
protected override void Initialize(global::Opc.Ua.ISystemContext context)
{
base.Initialize(context);
base.Initialize<T>(context);
}
/// <inheritdoc/>
protected override void Initialize(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState source)
{
InitializeOptionalChildren(context);
base.Initialize(context, source);
}
/// <summary>
/// Adds builder which extracts T from Variant or creates new Variant with type T
/// This is public so it can be overridden by classes outside of the namespace
/// </summary>
/// <typeparam name="TBuilder">The builder to use for T</typeparam>
public class Implementation<TBuilder> : {{Tokens.ClassName}}State<T>
where TBuilder : struct, global::Opc.Ua.IVariantBuilder<T>
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}State{T}"/> class.
/// </summary>
public Implementation(global::Opc.Ua.NodeState? parent)
: base(parent)
{
m_builder = new TBuilder();
Value = default(T);
}
/// <inheritdoc/>
public override T Value
{
get => m_builder.GetValue(WrappedValue);
set => WrappedValue = m_builder.WithValue(value);
}
/// <inheritdoc/>
protected override global::Opc.Ua.NodeState CreateCopy()
{
return new Implementation<TBuilder>(Parent);
}
private readonly TBuilder m_builder;
}
}
""");
/// <summary>
/// Variable type value field methods
/// </summary>
public static readonly TemplateString VariableType_ValueMethods = TemplateString.Parse(
$$"""
/// <summary>
/// Handles the read of the {{Tokens.ChildName}} variable.
/// </summary>
private global::Opc.Ua.ServiceResult OnRead_{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState node,
global::Opc.Ua.NumericRange indexRange,
global::Opc.Ua.QualifiedName dataEncoding,
ref global::Opc.Ua.Variant value,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
EnterLock();
try
{
DoBeforeReadProcessing(context, node);
var childVariable = m_variable?.{{Tokens.ChildPath}};
if (childVariable != null && global::Opc.Ua.StatusCode.IsBad(childVariable.StatusCode))
{
value = global::Opc.Ua.Variant.Null;
statusCode = childVariable.StatusCode;
return new global::Opc.Ua.ServiceResult(statusCode);
}
if (m_value != null)
{
value = global::Opc.Ua.Variant.{{Tokens.VariantFrom}}(
({{Tokens.ChildDataType}})m_value.{{Tokens.PropertyName}});
}
var result = Read(
context,
node,
indexRange,
dataEncoding,
ref value,
ref statusCode,
ref timestamp);
if (childVariable != null && global::Opc.Ua.ServiceResult.IsNotBad(result))
{
timestamp = childVariable.Timestamp;
if (statusCode != childVariable.StatusCode)
{
statusCode = childVariable.StatusCode;
result = new global::Opc.Ua.ServiceResult(statusCode);
}
}
return result;
}
finally
{
ExitLock();
}
}
/// <summary>
/// Handles the write of the {{Tokens.ChildName}} variable.
/// </summary>
private global::Opc.Ua.ServiceResult OnWrite_{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState node,
global::Opc.Ua.NumericRange indexRange,
global::Opc.Ua.QualifiedName dataEncoding,
ref global::Opc.Ua.Variant value,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
EnterLock();
try
{
if (!value.{{Tokens.VariantTryGet}}(out {{Tokens.ChildDataType}} newValue))
{
newValue = default;
}
UpdateChildVariableStatus(m_variable.{{Tokens.ChildPath}}, ref statusCode, ref timestamp);
m_value.{{Tokens.PropertyName}} = {{Tokens.ValueWrite}};
UpdateParent(context, ref statusCode, ref timestamp);
}
finally
{
ExitLock();
}
return global::Opc.Ua.ServiceResult.Good;
}
""");
/// <summary>
/// Variable type node state
/// </summary>
public static readonly TemplateString VariableTypeValue_Class = TemplateString.Parse(
$$"""
/// <summary>
/// The {{Tokens.ClassName}} VariableType value.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public class {{Tokens.ClassName}}Value : global::Opc.Ua.BaseVariableValue
{
/// <summary>
/// Initializes a new instance of the <see cref="{{Tokens.ClassName}}Value"/> class.
/// </summary>
public {{Tokens.ClassName}}Value(
{{Tokens.ClassName}}State variable,
{{Tokens.DataType}}? value,
global::System.Threading.Lock? dataLock)
: base(dataLock)
{
m_value = value;
if (m_value == null)
{
m_value = new {{Tokens.DataType}}();
}
Initialize(variable);
}
/// <summary>
/// Gets the variable associated with the value.
/// </summary>
public {{Tokens.ClassName}}State Variable => m_variable;
/// <summary>
/// Gets or sets the value.
/// </summary>
public {{Tokens.DataType}} Value
{
get => m_value;
set => m_value = value;
}
/// <summary>
/// Initializes the value.
/// </summary>
private void Initialize({{Tokens.ClassName}}State variable)
{
EnterLock();
try
{
m_variable = variable;
variable.Value = m_value;
variable.OnReadValue = OnReadValue;
variable.OnWriteValue = OnWriteValue;
global::Opc.Ua.BaseVariableState? instance = null;
global::System.Collections.Generic.List<global::Opc.Ua.BaseInstanceState> updateList =
new global::System.Collections.Generic.List<global::Opc.Ua.BaseInstanceState>();
updateList.Add(variable);
{{Tokens.ListOfChildInitializers}}
SetUpdateList(updateList);
}
finally
{
ExitLock();
}
}
/// <inheritdoc/>
protected global::Opc.Ua.ServiceResult OnReadValue(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState node,
global::Opc.Ua.NumericRange indexRange,
global::Opc.Ua.QualifiedName dataEncoding,
ref global::Opc.Ua.Variant value,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
EnterLock();
try
{
DoBeforeReadProcessing(context, node);
if (m_value != null)
{
value = global::Opc.Ua.Variant.{{Tokens.VariantFrom}}(m_value);
}
return Read(context, node, indexRange, dataEncoding, ref value, ref statusCode, ref timestamp);
}
finally
{
ExitLock();
}
}
/// <inheritdoc/>
private global::Opc.Ua.ServiceResult OnWriteValue(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState node,
global::Opc.Ua.NumericRange indexRange,
global::Opc.Ua.QualifiedName dataEncoding,
ref global::Opc.Ua.Variant value,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
EnterLock();
try
{
if (!value.{{Tokens.VariantTryGet}}(out {{Tokens.DataType}} newValue))
{
newValue = default;
}
if ({{Tokens.ValueComparison}})
{
UpdateChildrenChangeMasks(context, ref newValue, ref statusCode, ref timestamp);
Timestamp = timestamp;
m_value = {{Tokens.ValueWrite}};
m_variable.UpdateChangeMasks(global::Opc.Ua.NodeStateChangeMasks.Value);
}
}
finally
{
ExitLock();
}
return global::Opc.Ua.ServiceResult.Good;
}
/// <summary>
/// Updates the change masks for the children.
/// </summary>
private void UpdateChildrenChangeMasks(
global::Opc.Ua.ISystemContext context,
ref {{Tokens.DataType}} newValue,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
{{Tokens.ListOfUpdateChildrenChangeMasks}}
}
/// <summary>
/// Updates the parent variable status.
/// </summary>
private void UpdateParent(
global::Opc.Ua.ISystemContext context,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
Timestamp = timestamp;
m_variable.UpdateChangeMasks(global::Opc.Ua.NodeStateChangeMasks.Value);
m_variable.ClearChangeMasks(context, false);
}
/// <summary>
/// Updates the status of a child variable.
/// </summary>
private void UpdateChildVariableStatus(
global::Opc.Ua.BaseVariableState child,
ref global::Opc.Ua.StatusCode statusCode,
ref global::Opc.Ua.DateTimeUtc timestamp)
{
if (child == null)
{
return;
}
child.StatusCode = statusCode;
if (timestamp == global::Opc.Ua.DateTimeUtc.MinValue)
{
timestamp = global::Opc.Ua.DateTimeUtc.Now;
}
child.Timestamp = timestamp;
}
{{Tokens.ListOfChildMethods}}
private {{Tokens.DataType}}? m_value;
private {{Tokens.ClassName}}State? m_variable;
}
""");
/// <summary>
/// Initialize optional child
/// </summary>
public static readonly TemplateString InitializeOptionalChild = TemplateString.Parse(
$$"""
if ({{Tokens.ChildName}} != null && global::Opc.Ua.NodeState.NeedsOptionalInitialization({{Tokens.ChildName}}))
{
{{Tokens.ChildName}}.Create(
context,
context.Create{{Tokens.SymbolicId}}(this, true));
}
""");
/// <summary>
/// Property template
/// </summary>
public static readonly TemplateString Property = TemplateString.Parse(
$$"""
/// <summary>
/// {{Tokens.ChildName}} property
/// </summary>
{{Tokens.AccessorSymbol}} {{Tokens.ClassName}}? {{Tokens.ChildName}}
{
get => {{Tokens.FieldName}};
set
{
if (!object.ReferenceEquals({{Tokens.FieldName}}, value))
{
ChangeMasks |= global::Opc.Ua.NodeStateChangeMasks.Children;
}
{{Tokens.FieldName}} = value;
}
}
""");
/// <summary>
/// Override property template
/// </summary>
public static readonly TemplateString PropertyOverride = TemplateString.Parse(
$$"""
/// <summary>
/// {{Tokens.ChildName}} property
/// </summary>
{{Tokens.AccessorSymbol}} {{Tokens.ClassName}}? {{Tokens.ChildName}}
{
get => ({{Tokens.ClassName}})base.{{Tokens.ChildName}};
set => base.{{Tokens.ChildName}} = value;
}
""");
/// <summary>
/// Add Optional component template
/// </summary>
public static readonly TemplateString OptionalMethod = TemplateString.Parse(
$$"""
/// <summary>
/// Adds the optional {{Tokens.ChildName}} child and returns this
/// instance so calls can be chained, e.g.
/// <c>parent.Add{{Tokens.ChildName}}(context).Add{{Tokens.ChildName}}Other(context)...</c>.
/// Idempotent: if {{Tokens.ChildName}} already exists the existing
/// child is kept and only its NodeId is overwritten (when a
/// non-null <paramref name="nodeId"/> is supplied).
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="nodeId">
/// Optional NodeId for the newly-added child. When
/// <see cref="global::Opc.Ua.NodeId.Null"/> (the default), the
/// child keeps the type-level NodeId assigned by the generated
/// factory unless <paramref name="context"/>'s
/// <see cref="global::Opc.Ua.ISystemContext.NodeIdFactory"/> is
/// non-null, in which case a fresh NodeId is minted via the
/// factory.
/// </param>
{{Tokens.AccessorSymbol}} {{Tokens.OwnerClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeId nodeId = default)
{
if ({{Tokens.ChildName}} == null)
{
{{Tokens.ClassName}} state = context.Create{{Tokens.SymbolicId}}(this, true);
{{Tokens.ChildName}} = state;
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.NodeId previousNodeId = nodeState.NodeId;
if (!nodeId.IsNull)
{
nodeState.NodeId = nodeId;
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
state,
previousNodeId,
this);
}
else if (context.NodeIdFactory != null &&
nodeState.NodeId.Equals({{Tokens.NodeIdConstant}}))
{
previousNodeId =
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceNodeId(context, state);
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
state,
previousNodeId,
this);
}
}
else if (!nodeId.IsNull)
{
((global::Opc.Ua.NodeState){{Tokens.ChildName}}).NodeId = nodeId;
}
return this;
}
/// <summary>
/// Adds the optional {{Tokens.ChildName}} child and invokes
/// <paramref name="configure"/> on the (just-added or pre-existing)
/// typed child. Returns this instance for fluent chaining.
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="configure">
/// Mutates the typed child in-place. Invoked after the child slot
/// is materialised so the callback can rely on the typed child
/// being non-null.
/// </param>
/// <param name="nodeId">
/// Optional NodeId for the newly-added child. See the
/// <see cref="Add{{Tokens.ChildName}}(global::Opc.Ua.ISystemContext, global::Opc.Ua.NodeId)"/>
/// overload for the default behaviour.
/// </param>
public {{Tokens.OwnerClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::System.Action<{{Tokens.ClassName}}> configure,
global::Opc.Ua.NodeId nodeId = default)
{
Add{{Tokens.ChildName}}(context, nodeId);
configure({{Tokens.ChildName}}!);
return this;
}
/// <summary>
/// Conditionally adds the optional {{Tokens.ChildName}} child
/// and invokes <paramref name="configure"/> on the typed child.
/// When <paramref name="condition"/> is <c>false</c> the child
/// is not materialised and the callback is not invoked.
/// Returns this instance for fluent chaining.
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="condition">
/// Whether to add and configure the child. When <c>false</c>,
/// the call is a no-op.
/// </param>
/// <param name="configure">
/// Mutates the typed child in-place. Only invoked when
/// <paramref name="condition"/> is <c>true</c>.
/// </param>
/// <param name="nodeId">
/// Optional NodeId for the newly-added child.
/// </param>
public {{Tokens.OwnerClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
bool condition,
global::System.Action<{{Tokens.ClassName}}> configure,
global::Opc.Ua.NodeId nodeId = default)
{
if (!condition)
{
return this;
}
Add{{Tokens.ChildName}}(context, nodeId);
configure({{Tokens.ChildName}}!);
return this;
}
/// <summary>
/// Adds the optional {{Tokens.ChildName}} child and invokes the
/// transform <paramref name="configure"/> on the typed child; the
/// callback's return value replaces the typed slot (so the
/// caller can wrap or substitute the child). Returns this
/// instance for fluent chaining.
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="configure">
/// Transforms the typed child. The returned instance is stored
/// in the typed slot; pass back the same instance to keep it
/// unchanged or a new instance to replace it.
/// </param>
/// <param name="nodeId">
/// Optional NodeId for the newly-added child.
/// </param>
public {{Tokens.OwnerClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::System.Func<{{Tokens.ClassName}}, {{Tokens.ClassName}}> configure,
global::Opc.Ua.NodeId nodeId = default)
{
Add{{Tokens.ChildName}}(context, nodeId);
{{Tokens.ChildName}} = configure({{Tokens.ChildName}}!);
return this;
}
/// <summary>
/// Conditionally adds the optional {{Tokens.ChildName}} child and
/// invokes the transform <paramref name="configure"/> on the
/// typed child. When <paramref name="condition"/> is <c>false</c>
/// the child is not materialised and the callback is not invoked.
/// Returns this instance for fluent chaining.
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="condition">
/// Whether to add and configure the child. When <c>false</c>,
/// the call is a no-op.
/// </param>
/// <param name="configure">
/// Transforms the typed child. Only invoked when
/// <paramref name="condition"/> is <c>true</c>.
/// </param>
/// <param name="nodeId">
/// Optional NodeId for the newly-added child.
/// </param>
public {{Tokens.OwnerClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
bool condition,
global::System.Func<{{Tokens.ClassName}}, {{Tokens.ClassName}}> configure,
global::Opc.Ua.NodeId nodeId = default)
{
if (!condition)
{
return this;
}
Add{{Tokens.ChildName}}(context, nodeId);
{{Tokens.ChildName}} = configure({{Tokens.ChildName}}!);
return this;
}
""");
/// <summary>
/// Place holder method template
/// </summary>
public static readonly TemplateString PlaceHolderMethod = TemplateString.Parse(
$$"""
/// <summary>
/// Add a placeholder {{Tokens.ChildName}} as child
/// </summary>
{{Tokens.AccessorSymbol}} {{Tokens.ClassName}} Add{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.QualifiedName browseName)
{
{{Tokens.ClassName}} state =
context.Create{{Tokens.SymbolicId}}(this, browseName);
ReplaceChild(context, state);
return state;
}
""");
/// <summary>
/// Find child methods
/// </summary>
public static readonly TemplateString ChildOperations = TemplateString.Parse(
$$"""
/// <inheritdoc/>
public override void GetChildren(
global::Opc.Ua.ISystemContext context,
global::System.Collections.Generic.IList<global::Opc.Ua.BaseInstanceState> children)
{
{{Tokens.ListOfFindChildren}}
base.GetChildren(context, children);
}
{{Tokens.ListOfCreateOrReplaceChild}}
/// <inheritdoc/>
protected override global::Opc.Ua.BaseInstanceState? FindChild(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.QualifiedName browseName,
bool createOrReplace,
global::Opc.Ua.BaseInstanceState? replacement,
bool assignInstanceNodeIds = true)
{
global::Opc.Ua.BaseInstanceState? instance = null;
switch (browseName.Name)
{
{{Tokens.ListOfFindChildCase}}
}
if (instance != null)
{
return instance;
}
return base.FindChild(
context, browseName, createOrReplace, replacement, assignInstanceNodeIds);
}
/// <inheritdoc/>
protected override void RemoveExplicitlyDefinedChild(global::Opc.Ua.BaseInstanceState child)
{
{{Tokens.ListOfRemoveChild}}
base.RemoveExplicitlyDefinedChild(child);
}
""");
/// <summary>
/// Find child case template. Uses a literal string with the
/// runtime browse name (which may differ from the symbolic
/// name when the design overrides <c>&lt;opc:BrowseName&gt;</c>,
/// as in <c>Boiler.InputPipe</c> → <c>"PipeX001"</c>).
/// Avoids depending on <c>{Namespace}.BrowseNames.{ChildName}</c>
/// constants so cross-namespace inherited children compile
/// even when the foreign namespace's generated
/// <c>BrowseNames</c> class doesn't carry a constant for that
/// name.
/// </summary>
public static readonly TemplateString FindChildCase = TemplateString.Parse(
$$"""
case "{{Tokens.ChildBrowseNameLiteral}}":
{
instance = !createOrReplace ?
{{Tokens.ChildName}} : CreateOrReplace{{Tokens.ChildName}}(
context, replacement, assignInstanceNodeIds);
break;
}
""");
/// <summary>
/// Create or replace a child on the class. The optional
/// <c>assignInstanceNodeIds</c> parameter lets the generated type and
/// child factories opt out of per-instance NodeId assignment: they
/// build declaration subtrees whose NodeIds must stay at their
/// type-level values (the enclosing
/// <c>CreateInstanceOf&lt;Type&gt;</c> factory rebases the finished
/// subtree in one pass). Every other caller - the generated
/// <c>FindChild</c> plumbing behind <c>NodeState.CreateChild</c> /
/// <c>NodeState.ReplaceChild</c> and hand-written node managers -
/// materialises children onto an already-instantiated tree and
/// therefore gets per-instance NodeIds by default.
/// </summary>
public static readonly TemplateString CreateOrReplaceChild = TemplateString.Parse(
$$"""
/// <summary>
/// Create or replace the mandatory {{Tokens.ChildName}} child
/// </summary>
/// <param name="context">The system context.</param>
/// <param name="replacement">
/// The child to adopt or to copy onto the existing child. When
/// <c>null</c> a new child is created.
/// </param>
/// <param name="assignInstanceNodeIds">
/// When <c>true</c> (the default) and the context supplies a
/// <see cref="global::Opc.Ua.ISystemContext.NodeIdFactory"/>, a
/// child that still carries a null or type-level NodeId - and its
/// descendants - are rebased onto per-instance NodeIds so multiple
/// instances of the same type never collide. A NodeId the caller
/// already assigned is never overwritten.
/// </param>
{{Tokens.AccessorSymbol}} {{Tokens.ClassName}} CreateOrReplace{{Tokens.ChildName}}(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.BaseInstanceState? replacement,
bool assignInstanceNodeIds = true)
{
if (replacement is {{Tokens.ClassName}} typedReplacement)
{
// a replacement of the matching type is used directly,
// replacing any child that may already exist.
{{Tokens.ChildName}} = typedReplacement;
}
else if ({{Tokens.ChildName}} == null)
{
{{Tokens.ClassName}} child = {{Tokens.ClassFactory}}(this);
// Stamp the child's identity so a path based NodeIdFactory
// can derive a per-instance NodeId for it below, and so
// NodeState.CreateChild returns a child that carries the
// browse name it was asked for.
global::Opc.Ua.NodeState childNode = child;
childNode.SymbolicName = "{{Tokens.BrowseNameLiteral}}";
childNode.DisplayName = new global::Opc.Ua.LocalizedText(
"{{Tokens.ChildBrowseNameLiteral}}");
if (context.NamespaceUris != null)
{
childNode.BrowseName = new global::Opc.Ua.QualifiedName(
"{{Tokens.ChildBrowseNameLiteral}}",
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
}
if (replacement != null)
{
child.Create(context, replacement);
}
{{Tokens.ChildName}} = child;
}
else if (replacement != null)
{
// an existing child is replaced by copying the replacement
// onto it, keeping the strongly typed instance.
{{Tokens.ChildName}}.Create(context, replacement);
}
if (assignInstanceNodeIds && context.NodeIdFactory != null)
{
global::Opc.Ua.NodeState childState = {{Tokens.ChildName}};
if (childState.NodeId.IsNull ||
childState.NodeId.Equals({{Tokens.NodeIdConstant}}))
{
global::Opc.Ua.NodeId previousNodeId =
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceNodeId(
context,
childState);
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
childState,
previousNodeId,
this);
}
}
return {{Tokens.ChildName}};
}
""");
/// <summary>
/// Find children method template
/// </summary>
public static readonly TemplateString FindChildren = TemplateString.Parse(
$$"""
if ({{Tokens.FieldName}} != null)
{
children.Add({{Tokens.FieldName}});
}
""");
/// <summary>
/// Remove child template
/// </summary>
public static readonly TemplateString RemoveChild = TemplateString.Parse(
$$"""
if (object.ReferenceEquals({{Tokens.FieldName}}, child))
{
{{Tokens.FieldName}} = null;
return;
}
""");
/// <summary>
/// Clone child template
/// </summary>
public static readonly TemplateString CloneChild = TemplateString.Parse(
$$"""
state.{{Tokens.BrowseName}} =
global::Opc.Ua.NodeState.CloneChild({{Tokens.BrowseName}}, state);
""");
/// <summary>
/// Compare child template
/// </summary>
public static readonly TemplateString CompareChild = TemplateString.Parse(
$$"""
if (!global::Opc.Ua.NodeStateComparer.Default.Equals(
state.{{Tokens.BrowseName}},
{{Tokens.BrowseName}}))
{
return false;
}
""");
/// <summary>
/// Hash child template
/// </summary>
public static readonly TemplateString HashChild = TemplateString.Parse(
$$"""
hashCode = (hashCode * 16777619) ^
global::Opc.Ua.NodeStateComparer.Default.GetHashCode({{Tokens.BrowseName}});
""");
/// <summary>
/// Encodeable activator
/// </summary>
public static readonly TemplateString ActivatorClass = TemplateString.Parse(
$$"""
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("{{Tokens.Tool}}", "{{Tokens.Version}}")]
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute()]
public sealed class {{Tokens.StateClassName}}Activator : global::Opc.Ua.NodeStateActivator
{
/// <summary>
/// The singleton instance of the activator.
/// </summary>
public static readonly {{Tokens.StateClassName}}Activator Instance
= new {{Tokens.StateClassName}}Activator();
/// <inheritdoc/>
protected override global::Opc.Ua.NodeState CreateInstance(
global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent)
{
return context.CreateInstanceOf{{Tokens.SymbolicId}}(parent);
}
}
""");
/// <summary>
/// Nodestate activator registration
/// </summary>
public static readonly TemplateString ActivatorRegistration = TemplateString.Parse(
$$"""
// Register node state factory for {{Tokens.BrowseName}} {{Tokens.NodeClass}}
builder = builder.RegisterType(
{{Tokens.NodeClass}}Ids.{{Tokens.SymbolicId}},
{{Tokens.StateClassName}}Activator.Instance);
""");
/// <summary>
/// Template for a single node state creation call
/// </summary>
public static readonly TemplateString Add = TemplateString.Parse(
$$"""
// Add {{Tokens.SymbolicName}} predefined node
{
global::Opc.Ua.NodeState state = Create{{Tokens.SymbolicId}}(context);
state.CreateAsPredefinedNode(context);
nodes.Add(state);
}
""");
/// <summary>
/// Template for a single method state creation call.
/// </summary>
public static readonly TemplateString AddMethod = TemplateString.Parse(
$$"""
// Add {{Tokens.SymbolicName}} predefined node
{
global::Opc.Ua.NodeState state =
context.CreateInstanceOf{{Tokens.SymbolicId}}();
state.CreateAsPredefinedNode(context);
nodes.Add(state);
}
""");
/// <summary>
/// Template for BaseObjectTypeState creation
/// </summary>
public static readonly TemplateString Create_ObjectType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} ObjectType node state.
/// </summary>
internal static global::Opc.Ua.BaseObjectTypeState Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = false)
{
var state = new global::Opc.Ua.BaseObjectTypeState();
global::Opc.Ua.NodeState nodeState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.SuperTypeId = {{Tokens.SuperTypeId}};
state.IsAbstract = {{Tokens.IsAbstract}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
nodeState.IsPartOfTypeHierarchy = true;
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for BaseDataVariableTypeState creation
/// </summary>
public static readonly TemplateString Create_VariableType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} VariableType node state.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}();
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseVariableTypeState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.SuperTypeId = {{Tokens.SuperTypeId}};
baseState.IsAbstract = {{Tokens.IsAbstract}};
baseState.DataType = {{Tokens.DataTypeIdConstant}};
baseState.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
nodeState.IsPartOfTypeHierarchy = true;
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for ReferenceTypeState creation
/// </summary>
public static readonly TemplateString Create_ReferenceType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} ReferenceType node state.
/// </summary>
internal static global::Opc.Ua.ReferenceTypeState Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = false)
{
var state = new global::Opc.Ua.ReferenceTypeState();
global::Opc.Ua.NodeState nodeState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.SuperTypeId = {{Tokens.SuperTypeId}};
state.IsAbstract = {{Tokens.IsAbstract}};
state.Symmetric = {{Tokens.SymmetricValue}};
{{Tokens.InverseNameValue}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for DataTypeState creation
/// </summary>
public static readonly TemplateString Create_DataType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} DataType node state.
/// </summary>
internal static global::Opc.Ua.DataTypeState Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = false)
{
var state = new global::Opc.Ua.DataTypeState();
global::Opc.Ua.NodeState nodeState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.SuperTypeId = {{Tokens.SuperTypeId}};
state.IsAbstract = {{Tokens.IsAbstract}};
state.DataTypeDefinition = {{Tokens.DataTypeDefinition}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for BaseObjectState creation
/// </summary>
public static readonly TemplateString Create_Object = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} Object node state.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = true)
{
var state = {{Tokens.StateClassFactory}}(null);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseObjectState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
baseState.NumericId = {{Tokens.NumericIdValue}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
// {{Tokens.ModellingRuleId}}
baseState.EventNotifier = {{Tokens.EventNotifier}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for BaseVariableState (Property or DataVariable) creation
/// </summary>
public static readonly TemplateString Create_Variable = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} Variable node state.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = true)
{
var state = {{Tokens.StateClassFactory}}(null);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseVariableState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
baseState.NumericId = {{Tokens.NumericIdValue}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
// {{Tokens.ModellingRuleId}}
baseState.DataType = {{Tokens.DataTypeIdConstant}};
baseState.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
baseState.AccessLevel = {{Tokens.AccessLevelValue}};
baseState.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
baseState.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
baseState.Historizing = {{Tokens.HistorizingValue}};
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for ViewState creation
/// </summary>
public static readonly TemplateString Create_View = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} View node state.
/// </summary>
internal static global::Opc.Ua.ViewState Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
bool forInstance = true)
{
var state = new global::Opc.Ua.ViewState();
global::Opc.Ua.NodeState nodeState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText({{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.EventNotifier = {{Tokens.EventNotifier}};
state.ContainsNoLoops = {{Tokens.ContainsNoLoopsValue}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for MethodState creation
/// </summary>
public static readonly TemplateString Create_InstanceOfMethodType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates an instance of the {{Tokens.SymbolicName}} Method node state.
/// </summary>
/// <remarks>
/// The returned node graph has not completed its create lifecycle.
/// Node manager registration completes it automatically. Call
/// <see cref="global::Opc.Ua.NodeState.CreateAsPredefinedNode(global::Opc.Ua.ISystemContext)"/>
/// before registration when configuration depends on
/// <c>OnBeforeCreate</c> or <c>OnAfterCreate</c>.
/// </remarks>
public static {{Tokens.StateClassName}} CreateInstanceOf{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent = null,
global::Opc.Ua.QualifiedName browseName = default)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.MethodState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
}
else
{
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
{{Tokens.MethodDeclarationId}}
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.Executable = {{Tokens.ExecutableValue}};
baseState.UserExecutable = {{Tokens.ExecutableValue}};
{{Tokens.ListOfInputArguments}}
{{Tokens.ListOfOutputArguments}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!browseName.IsNull && context.NodeIdFactory != null)
{
global::Opc.Ua.NodeId previousNodeId =
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceNodeId(context, state);
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
state,
previousNodeId);
}
return state;
}
""");
/// <summary>
/// Creates a MethodState InputArguments or OutputArguments property.
/// </summary>
public static readonly TemplateString Create_MethodArguments = TemplateString.Parse(
$$"""
global::Opc.Ua.PropertyState<global::Opc.Ua.ArrayOf<global::Opc.Ua.Argument>> {{Tokens.PropertyName}} =
state.CreateOrReplace{{Tokens.BrowseName}}(context, null, assignInstanceNodeIds: false);
{{Tokens.PropertyName}}.SymbolicName = global::Opc.Ua.BrowseNames.{{Tokens.BrowseName}};
{{Tokens.PropertyName}}.BrowseName =
global::Opc.Ua.QualifiedName.From(global::Opc.Ua.BrowseNames.{{Tokens.BrowseName}});
{{Tokens.PropertyName}}.DisplayName =
new global::Opc.Ua.LocalizedText(global::Opc.Ua.BrowseNames.{{Tokens.BrowseName}});
{{Tokens.PropertyName}}.TypeDefinitionId = global::Opc.Ua.VariableTypeIds.PropertyType;
{{Tokens.PropertyName}}.ReferenceTypeId = global::Opc.Ua.ReferenceTypeIds.HasProperty;
{{Tokens.PropertyName}}.ModellingRuleId =
new global::Opc.Ua.NodeId(global::Opc.Ua.Objects.ModellingRule_Mandatory);
{{Tokens.PropertyName}}.DataType = global::Opc.Ua.DataTypeIds.Argument;
{{Tokens.PropertyName}}.ValueRank = global::Opc.Ua.ValueRanks.OneDimension;
{{Tokens.PropertyName}}.ArrayDimensions = global::Opc.Ua.ArrayOf.Wrapped(0u);
{{Tokens.PropertyName}}.Value = global::Opc.Ua.ArrayOf.ToArrayOf(
new global::Opc.Ua.Argument[]
{
{{Tokens.ListOfValues}}
});
""");
/// <summary>
/// Overrides the value of an existing MethodState InputArguments or
/// OutputArguments property while leaving the identity of the property
/// node created by its own child factory untouched.
/// </summary>
public static readonly TemplateString Assign_MethodArgumentValues = TemplateString.Parse(
$$"""
global::Opc.Ua.PropertyState<global::Opc.Ua.ArrayOf<global::Opc.Ua.Argument>> {{Tokens.PropertyName}} =
state.CreateOrReplace{{Tokens.BrowseName}}(context, null, assignInstanceNodeIds: false);
{{Tokens.PropertyName}}.Value = global::Opc.Ua.ArrayOf.ToArrayOf(
new global::Opc.Ua.Argument[]
{
{{Tokens.ListOfValues}}
});
""");
/// <summary>
/// Template for object type instance creation (instantiate an object type)
/// </summary>
public static readonly TemplateString Create_InstanceOfObjectType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates an instance of the {{Tokens.TypeName}} type.
/// </summary>
/// <remarks>
/// The returned node graph has not completed its create lifecycle.
/// Node manager registration completes it automatically. Call
/// <see cref="global::Opc.Ua.NodeState.CreateAsPredefinedNode(global::Opc.Ua.ISystemContext)"/>
/// before registration when configuration depends on
/// <c>OnBeforeCreate</c> or <c>OnAfterCreate</c>.
/// </remarks>
public static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent = null,
global::Opc.Ua.QualifiedName browseName = default)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseObjectState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
}
else
{
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.EventNotifier = {{Tokens.EventNotifier}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!browseName.IsNull && context.NodeIdFactory != null)
{
global::Opc.Ua.NodeId previousNodeId =
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceNodeId(context, state);
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
state,
previousNodeId);
}
return state;
}
""");
/// <summary>
/// Template variable type instance creation (instantiate a variable type)
/// </summary>
public static readonly TemplateString Create_InstanceOfVariableType = TemplateString.Parse(
$$"""
/// <summary>
/// Creates an instance of the {{Tokens.TypeName}} variable type.
/// </summary>
/// <remarks>
/// The returned node graph has not completed its create lifecycle.
/// Node manager registration completes it automatically. Call
/// <see cref="global::Opc.Ua.NodeState.CreateAsPredefinedNode(global::Opc.Ua.ISystemContext)"/>
/// before registration when configuration depends on
/// <c>OnBeforeCreate</c> or <c>OnAfterCreate</c>.
/// </remarks>
public static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent = null,
global::Opc.Ua.QualifiedName browseName = default)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseVariableState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
}
else
{
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
{{Tokens.ModellingRuleId}}
baseState.DataType = {{Tokens.DataTypeIdConstant}};
baseState.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
baseState.AccessLevel = {{Tokens.AccessLevelValue}};
baseState.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
baseState.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
baseState.Historizing = {{Tokens.HistorizingValue}};
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!browseName.IsNull && context.NodeIdFactory != null)
{
global::Opc.Ua.NodeId previousNodeId =
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceNodeId(context, state);
global::Opc.Ua.NodeInstanceExtensions.AssignInstanceChildNodeIds(
context,
state,
previousNodeId);
}
return state;
}
""");
/// <summary>
/// Template for child object state creation
/// </summary>
public static readonly TemplateString Create_ChildObject = TemplateString.Parse(
$$"""
/// <summary>
/// Creates an instance of the {{Tokens.SymbolicName}} object node child component.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseObjectState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
{{Tokens.InstanceNodeIdOverride}}
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
baseState.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.EventNotifier = {{Tokens.EventNotifier}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for child variable state creation
/// </summary>
public static readonly TemplateString Create_ChildVariable = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} child Variable node child component.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseVariableState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
{{Tokens.InstanceNodeIdOverride}}
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
baseState.NumericId = {{Tokens.NumericIdValue}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.DataType = {{Tokens.DataTypeIdConstant}};
baseState.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
baseState.AccessLevel = {{Tokens.AccessLevelValue}};
baseState.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
baseState.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
baseState.Historizing = {{Tokens.HistorizingValue}};
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
{{Tokens.MethodDeclarationId}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for child method state creation
/// </summary>
public static readonly TemplateString Create_ChildMethod = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} child Method node child component.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.MethodState baseState = state;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
{{Tokens.InstanceNodeIdOverride}}
{{Tokens.MethodDeclarationId}}
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
baseState.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.Executable = {{Tokens.ExecutableValue}};
baseState.UserExecutable = {{Tokens.ExecutableValue}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
{{Tokens.ListOfInputArguments}}
{{Tokens.ListOfOutputArguments}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for child object state creation
/// </summary>
public static readonly TemplateString Create_ChildObject_Placeholder = TemplateString.Parse(
$$"""
/// <summary>
/// Creates an instance of the {{Tokens.SymbolicName}} object placeholder child.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
global::Opc.Ua.QualifiedName browseName = default,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseObjectState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
forInstance = true;
}
else
{
// Create the state for the type parent
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
baseState.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.EventNotifier = {{Tokens.EventNotifier}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for child variable state creation
/// </summary>
public static readonly TemplateString Create_ChildVariable_Placeholder = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} child Variable object placeholder child.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
global::Opc.Ua.QualifiedName browseName = default,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.BaseVariableState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
forInstance = true;
}
else
{
// Create the state for the type parent
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
baseState.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
{{Tokens.ModellingRuleId}}
baseState.DataType = {{Tokens.DataTypeIdConstant}};
baseState.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
baseState.AccessLevel = {{Tokens.AccessLevelValue}};
baseState.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
baseState.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
baseState.Historizing = {{Tokens.HistorizingValue}};
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for child method state creation
/// </summary>
public static readonly TemplateString Create_ChildMethod_Placeholder = TemplateString.Parse(
$$"""
/// <summary>
/// Creates the {{Tokens.SymbolicName}} child Method object placeholder child.
/// </summary>
internal static {{Tokens.StateClassName}} Create{{Tokens.SymbolicId}}(
this global::Opc.Ua.ISystemContext context,
global::Opc.Ua.NodeState parent,
global::Opc.Ua.QualifiedName browseName = default,
bool forInstance = false)
{
var state = {{Tokens.StateClassFactory}}(parent);
global::Opc.Ua.NodeState nodeState = state;
global::Opc.Ua.MethodState baseState = state;
nodeState.NodeId = {{Tokens.NodeIdConstant}};
if (!browseName.IsNull)
{
nodeState.SymbolicName = browseName.Name;
nodeState.BrowseName = browseName;
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(browseName.Name);
forInstance = true;
}
else
{
// Create the state for the type parent
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.BrowseName = new global::Opc.Ua.QualifiedName(
{{Tokens.BrowseNameSymbol}},
context.NamespaceUris.GetIndexOrAppend({{Tokens.BrowseNameNamespaceUri}}));
nodeState.DisplayName = new global::Opc.Ua.LocalizedText(
{{Tokens.DisplayName}});
}
baseState.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
baseState.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
baseState.Executable = {{Tokens.ExecutableValue}};
baseState.UserExecutable = {{Tokens.ExecutableValue}};
{{Tokens.MethodDeclarationId}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
{{Tokens.AccessRestrictionsValue}}
{{Tokens.ListOfRolePermissions}}
{{Tokens.ListOfReferences}}
{{Tokens.ListOfChildNodeStates}}
{{Tokens.ListOfInputArguments}}
{{Tokens.ListOfOutputArguments}}
if (!forInstance)
{
{{Tokens.ModellingRuleId}}
{{Tokens.ListOfOptionalChildNodeStates}}
}
return state;
}
""");
/// <summary>
/// Template for role permissions collection initialization
/// </summary>
public static readonly TemplateString ListOfRolePermissions = TemplateString.Parse(
$$"""
nodeState.RolePermissions = new global::Opc.Ua.RolePermissionType[]
{
{{Tokens.ListOfRolePermissions}}
};
""");
/// <summary>
/// Wrapper template for the singleton-instance NodeId override
/// emitted right after the type-level <c>nodeState.NodeId</c>
/// assignment in <c>Create_ChildObject</c> / <c>Create_ChildVariable</c>
/// / <c>Create_ChildMethod</c>. Renders only when at least one
/// singleton-instance branch is collected; otherwise the token
/// expands to nothing.
/// </summary>
public static readonly TemplateString InstanceNodeIdOverride = TemplateString.Parse(
$$"""
if (forInstance && parent != null)
{
{{Tokens.ListOfInstanceNodeIdBranches}}
}
""");
/// <summary>
/// One per-singleton branch of the singleton-instance
/// <c>nodeState.NodeId</c> override dispatch. Rebinds
/// <c>nodeState.NodeId</c> to <see cref="Tokens.NodeIdConstant"/>
/// when the parent owner matches <see cref="Tokens.ParentNodeIdConstant"/>.
/// </summary>
public static readonly TemplateString InstanceNodeIdBranch = TemplateString.Parse(
$$"""
{{Tokens.IfOrElseIf}} (parent.NodeId.Equals({{Tokens.ParentNodeIdConstant}}))
{
nodeState.NodeId = {{Tokens.NodeIdConstant}};
}
""");
/// <summary>
/// Template for role permission
/// </summary>
public static readonly TemplateString RolePermission = TemplateString.Parse(
$$"""
new global::Opc.Ua.RolePermissionType
{
RoleId = {{Tokens.RoleIdConstant}},
Permissions = {{Tokens.PermissionsValue}}
},
""");
/// <summary>
/// Template for description assignment
/// </summary>
public static readonly TemplateString Description = TemplateString.Parse(
$$"""
nodeState.Description = new global::Opc.Ua.LocalizedText({{Tokens.DescriptionValue}});
""");
/// <summary>
/// Template for inverse name assignment
/// </summary>
public static readonly TemplateString InverseName = TemplateString.Parse(
$$"""
state.InverseName = new global::Opc.Ua.LocalizedText({{Tokens.InverseNameValue}});
""");
/// <summary>
/// Template for release status assignment
/// </summary>
public static readonly TemplateString ReleaseStatus = TemplateString.Parse(
$$"""
nodeState.ReleaseStatus = {{Tokens.ReleaseStatusValue}};
""");
/// <summary>
/// Template for categories assignment
/// </summary>
public static readonly TemplateString Categories = TemplateString.Parse(
$$"""
nodeState.Categories = {{Tokens.CategoriesValue}};
""");
/// <summary>
/// Template for specification assignment
/// </summary>
public static readonly TemplateString Specification = TemplateString.Parse(
$$"""
nodeState.Specification = {{Tokens.SpecificationValue}};
""");
/// <summary>
/// Template for access restrictions assignment
/// </summary>
public static readonly TemplateString AccessRestrictions = TemplateString.Parse(
$$"""
nodeState.AccessRestrictions = {{Tokens.AccessRestrictionsValue}};
""");
/// <summary>
/// Template for method declaration id assignment
/// </summary>
public static readonly TemplateString MethodDeclarationId = TemplateString.Parse(
$$"""
baseState.MethodDeclarationId = {{Tokens.MethodDeclarationId}};
""");
/// <summary>
/// Template for array dimensions assignment
/// </summary>
public static readonly TemplateString ArrayDimensions = TemplateString.Parse(
$$"""
baseState.ArrayDimensions = {{Tokens.ArrayDimensions}};
""");
/// <summary>
/// Template for value assignment
/// </summary>
public static readonly TemplateString VariantArrayOfValue = TemplateString.Parse(
$$"""
baseState.WrappedValue = global::Opc.Ua.Variant.FromStructure(
global::Opc.Ua.ArrayOf.ToArrayOf(new {{Tokens.DataType}}[]
{
{{Tokens.ListOfValues}}
}));
""");
/// <summary>
/// Template for value assignment
/// </summary>
public static readonly TemplateString ArgumentValue = TemplateString.Parse(
$$"""
new global::Opc.Ua.Argument
{
Name = {{Tokens.Name}},
DataType = {{Tokens.DataType}},
ValueRank = {{Tokens.ValueRank}},
ArrayDimensions = {{Tokens.ArrayDimensions}},
Description = {{Tokens.Description}}
},
""");
}
}