/* ======================================================================== * Copyright (c) 2005-2026 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/ * ======================================================================*/ using System.IO; using System.Text; using System.Threading; using System.Threading.Tasks; using Opc.Ua; using Opc.Ua.Export; using Opc.Ua.Server; using Opc.Ua.Server.Fluent; using Quickstarts.ReferenceServer; namespace NodeSetImportModel { /// /// A source-generated node manager for the NodeSet import test model /// which overlays a NodeSet2 document on top of its generated nodes. /// /// /// The class is declared in the generated model namespace so the emitted /// partial can reach the model's node states, and the generator makes it /// implement INodeSetImportFactoryProvider, so the overlay is /// imported into this model's typed states. /// [NodeManager(NamespaceUri = NodeSetImportTestModel.NamespaceUri)] public sealed partial class NodeSetImportOverlayNodeManager { /// /// The Device instance the overlay document adds. /// public DeviceState OverlayDevice { get; private set; } /// /// The Value variable the overlay document supplies for the generated /// Device instance, replacing its generated child. /// public CustomValueState ReplacedDeviceValue { get; private set; } /// /// The node the generated Device.Value child had before the /// overlay replaced it. /// public NodeId DisplacedDeviceValueId { get; private set; } partial void Configure(INodeManagerBuilder builder) { ushort namespaceIndex = builder.Context.NamespaceUris.GetIndexOrAppend( NodeSetImportTestModel.NamespaceUri); DisplacedDeviceValueId = new NodeId( NodeSetImportTestModel.GeneratedDeviceValueIdentifier, namespaceIndex); // Two documents of one batch: the second one declares a parent // which only exists in the first one. builder.Import(NodeSetImportTestModel.ReadOverlayDevice()); builder.Import(NodeSetImportTestModel.ReadOverlayDeviceChildren()); OverlayDevice = builder .Node( new NodeId( NodeSetImportTestModel.OverlayDeviceIdentifier, namespaceIndex)) .Node; ReplacedDeviceValue = builder .Node( new NodeId( NodeSetImportTestModel.ReplacedDeviceValueIdentifier, namespaceIndex)) .Node; } } /// /// Captures the node manager instance the server creates so the tests can /// assert against its address space. /// public sealed class NodeSetImportOverlayNodeManagerCapturingFactory : NodeSetImportOverlayNodeManagerFactory { /// /// The manager created for the running server. /// public NodeSetImportOverlayNodeManager Manager { get; private set; } = null!; /// public override ValueTask CreateAsync( IServerInternal server, ApplicationConfiguration configuration, CancellationToken cancellationToken = default) { var manager = new NodeSetImportOverlayNodeManager(server, configuration); Manager = manager; return new ValueTask(manager); } } /// /// Constants and overlay documents shared by the NodeSet import tests. /// public static class NodeSetImportTestModel { /// /// The model namespace generated from /// Fluent/Assets/NodeSetImport.NodeSet2.xml. /// public const string NamespaceUri = "urn:opcfoundation.org:2026-09:NodeSetImport"; /// /// The DeviceType instance the model itself declares. /// public const uint GeneratedDeviceIdentifier = 2000u; /// /// The generated Device.Value child the overlay displaces. /// public const uint GeneratedDeviceValueIdentifier = 2001u; /// /// The DeviceType instance the overlay adds. /// public const uint OverlayDeviceIdentifier = 3000u; /// /// The Calibrate method the overlay adds to /// from a second document. /// public const uint OverlayDeviceCalibrateIdentifier = 3001u; /// /// The Value variable the overlay supplies for the generated /// Device instance. /// public const uint ReplacedDeviceValueIdentifier = 3300u; /// /// A document adding one more instance of the model's DeviceType, plus /// a Value variable which replaces the generated child of the model's /// own Device instance. /// public static UANodeSet ReadOverlayDevice() { return Read( """ OverlayDevice i=85 ns=1;i=1000 Value ns=1;i=1001 ns=1;i=2000 """); } /// /// A second document whose nodes hang off the instance declared by /// . /// public static UANodeSet ReadOverlayDeviceChildren() { return Read( """ Calibrate ns=1;i=3000 """); } private static UANodeSet Read(string nodes) { string xml = "\r\n" + "\r\n" + " \r\n" + " " + NamespaceUri + "\r\n" + " \r\n" + nodes + "\r\n" + ""; using var stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)); return UANodeSet.Read(stream)!; } } /// /// A reference server hosting the generated node manager which overlays a /// NodeSet2 document onto its own model. /// public sealed class NodeSetImportServer : ReferenceServer { /// /// Initializes the server and registers the overlay node manager. /// public NodeSetImportServer(ITelemetryContext telemetry) : base(telemetry) { NodeManagerFactory = new NodeSetImportOverlayNodeManagerCapturingFactory(); AddNodeManager(NodeManagerFactory); } /// /// The factory which captured the created node manager. /// public NodeSetImportOverlayNodeManagerCapturingFactory NodeManagerFactory { get; } } }