Files
UA-.NETStandard/tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs
romanett d547747e22 Generate ReferenceNodeManager address space from a NodeSet2 model (#4220)
# Description

The CTT/compliance `ReferenceNodeManager` built its ~6,000 node address
space entirely in imperative C#. This PR migrates it to a
source-generated `[NodeManager]` partial driven by an exported NodeSet2
model (`ReferenceServer.NodeSet2.xml`), mirroring the
`MinimalBoilerServer` sample and completing the goal of issue #4204.

The address space is now materialized from the model by the source
generator. The large orchestration body was kept but converted from
create-based to **find-instead-of-create**: each `Create*` primitive now
looks up the model-materialized node via `FindPredefinedNode<T>` and
re-applies runtime values, DataAccess metadata, and write handlers,
rather than constructing nodes from scratch. This keeps the
deterministic value seeding and behavior wiring intact while the
structure comes from the model.

## Notable details for reviewers

- **Idempotent reference wiring.** The mesh references in the
`References` folder are already present in the model, so the historic
manual `AddReference` calls would throw on duplicates. Added a small
`AddReferenceIfMissing` helper so those calls are safe.
- **Non-string NodeIds.** The `NodeIds` folder assigns numeric / GUID /
opaque NodeIds. The model exports those nodes with their final ids, so
find-by-string-path no longer works; added `CreateVariableById(NodeId)`
to locate them by their real id.
- **Generator bug fix (worth a careful look).**
`ModelDesignExtensions.GetValueRankAsCode` reconstructed `ValueRank` as
`dimensions.Length + 1`, so a node with `ArrayDimensions="3,3"` was
emitted as `ValueRank = 3`. This affected **any** NodeSet2 with a fixed
multi-dimensional array (rank-3 matrices, `BadTypeMismatch` on
whole-matrix writes, wrong decoded dimensions). Corrected to
`dimensions.Length` and updated the unit tests that had codified the
off-by-one.

## Validation

- `ReferenceServerTest`: 44/44 on net10.0 and net48
- `Opc.Ua.SourceGeneration.Core.Tests`: pass on
net8.0/net9.0/net10.0/net48
- Integration on net10.0: ComplexTypes, Client, History, Sessions,
Subscriptions all pass
- Clean build, no new analyzer warnings

## Related Issues

- Fixes #4204

## Checklist

_Put an `x` in the boxes that apply. You can complete these step by step
after opening the PR._

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [x] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [ ] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [x] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
2026-08-15 05:22:31 -07:00

2506 lines
107 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>
/// 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.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(
{{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(
{{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(
{{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.ChildPath}});
}
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.ChildPath}} = {{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)
{
{{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}} =
({{Tokens.ClassName}})global::Opc.Ua.CoreUtils.Clone({{Tokens.BrowseName}});
""");
/// <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}}
if (forInstance)
{
{{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;
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.DataType = {{Tokens.DataTypeIdConstant}};
state.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
{{Tokens.ValueCode}}
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
if (forInstance)
{
{{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}}
if (forInstance)
{
{{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}}
if (forInstance)
{
{{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;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
state.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}};
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
// {{Tokens.ModellingRuleId}}
state.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;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
state.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}};
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
// {{Tokens.ModellingRuleId}}
state.DataType = {{Tokens.DataTypeIdConstant}};
state.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
state.AccessLevel = {{Tokens.AccessLevelValue}};
state.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
state.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
state.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>
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;
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}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.Executable = {{Tokens.ExecutableValue}};
state.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>
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;
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}});
}
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.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>
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;
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}});
}
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
{{Tokens.ModellingRuleId}}
state.DataType = {{Tokens.DataTypeIdConstant}};
state.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
state.AccessLevel = {{Tokens.AccessLevelValue}};
state.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
state.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
state.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;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
{{Tokens.InstanceNodeIdOverride}}
state.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}});
state.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.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;
nodeState.SymbolicName = {{Tokens.SymbolicNameSymbol}};
nodeState.NodeId = {{Tokens.NodeIdConstant}};
{{Tokens.InstanceNodeIdOverride}}
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
state.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}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.DataType = {{Tokens.DataTypeIdConstant}};
state.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
state.AccessLevel = {{Tokens.AccessLevelValue}};
state.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
state.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
state.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;
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}});
state.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.Executable = {{Tokens.ExecutableValue}};
state.UserExecutable = {{Tokens.ExecutableValue}};
{{Tokens.ReleaseStatusValue}}
{{Tokens.CategoriesValue}}
{{Tokens.SpecificationValue}}
if (forInstance)
{
{{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;
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}});
}
state.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.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;
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}});
}
state.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.TypeDefinitionId = {{Tokens.TypeDefinitionId}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
{{Tokens.ModellingRuleId}}
state.DataType = {{Tokens.DataTypeIdConstant}};
state.ValueRank = {{Tokens.ValueRank}};
{{Tokens.ArrayDimensions}}
state.AccessLevel = {{Tokens.AccessLevelValue}};
state.UserAccessLevel = {{Tokens.UserAccessLevelValue}};
state.MinimumSamplingInterval = {{Tokens.MinimumSamplingIntervalValue}};
state.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;
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}});
}
state.NumericId = {{Tokens.NumericIdValue}};
{{Tokens.DescriptionValue}}
nodeState.WriteMask = {{Tokens.WriteMaskValue}};
nodeState.UserWriteMask = {{Tokens.UserWriteMaskValue}};
state.ReferenceTypeId = {{Tokens.ReferenceTypeId}};
state.Executable = {{Tokens.ExecutableValue}};
state.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(
$$"""
state.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(
$$"""
state.MethodDeclarationId = {{Tokens.MethodDeclarationId}};
""");
/// <summary>
/// Template for array dimensions assignment
/// </summary>
public static readonly TemplateString ArrayDimensions = TemplateString.Parse(
$$"""
state.ArrayDimensions = {{Tokens.ArrayDimensions}};
""");
/// <summary>
/// Template for value assignment
/// </summary>
public static readonly TemplateString VariantArrayOfValue = TemplateString.Parse(
$$"""
state.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}}
},
""");
}
}