Files
UA-.NETStandard/tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs
T
Marc Schier df44b460be Complete CreateInstanceOf node lifecycle during registration (#4360)
# Description

Generated `CreateInstanceOf<Type>` factories build typed node graphs and
assign
per-instance NodeIds, but intentionally leave the node create lifecycle
open.
Hand-written callers commonly registered those graphs without calling
`CreateAsPredefinedNode`, so `OnBeforeCreate`, `OnAfterCreate`, and
change-mask
clearing never ran.

This change:

- adds `NodeState.IsCreated` and makes lifecycle completion idempotent
per node;
- completes newly materialized children, including children created by
  `OnAfterCreate`;
- bounds late-child lifecycle convergence and fails explicitly if
callbacks
  keep adding uncreated nodes;
- completes uncreated nodes automatically before predefined-node
registration
  and behavior wiring in both async and legacy custom node managers;
- assigns/rebases async-node-manager NodeIds before lifecycle callbacks;
- emits a null-safe Debug diagnostic when registration performs
completion;
- documents the generated-factory lifecycle contract and migration
behavior;
- adds regressions for condition/alarm method binding,
finite-state-machine
  namespaces, Fluent state-node rebasing, behavior replacement, cloning,
deletion, late children, non-converging callbacks, and synchronous/async
  registration.

The tracked `src/` and `samples/` call-site audit covered 2,953 C#
files. No
additional ordering-sensitive call sites require explicit completion
beyond
the three existing DI/Robotics sites.

## Validation

- `dotnet build UA.slnx -c Release -p:CustomTestTarget=net10.0`: passed.
- `Opc.Ua.Server.Tests` net10.0: 4,811 passed, 5 skipped.
- `NodeStateTests` net10.0 and net48: 213 passed on each.
- Node-manager lifecycle and null-logger tests net48: 459 passed.
- Source-generation core tests net10.0 and net48: 3,802 passed, 8
skipped on
  each.
- DI, Robotics, ISA95, Positioning, Vision, Vision.Intent, and XRegistry
  suites passed on net10.0 and net48.
- Generated alarm lifecycle regression passed on net10.0 and net48.

The solution build reports six pre-existing CA warnings in untouched
files.
The OpenUSD suite has one pre-existing locale-sensitive failure in
`StructuredCartesianCoordinatesAreAccepted` (`1.5` expected versus `1,5`
under `en-DE`); all other OpenUSD tests passed on both tested
frameworks.

## Related Issues

- Fixes #4320

## 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.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [x] I have addressed **all** PR feedback received.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7ffdc400-0710-4a2e-a9d1-c0e1a9b3b81e
2026-09-02 13:08:49 +02:00

2529 lines
108 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.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(
{{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>
/// <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"/>
/// 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;
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>
/// <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"/>
/// 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;
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>
/// <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"/>
/// 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;
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}}
},
""");
}
}