diff --git a/src/PluginNodes/OpaqueAndNodeIdPluginNode.cs b/src/PluginNodes/OpaqueAndNodeIdPluginNode.cs new file mode 100644 index 00000000..778e15cb --- /dev/null +++ b/src/PluginNodes/OpaqueAndNodeIdPluginNode.cs @@ -0,0 +1,174 @@ +namespace OpcPlc.PluginNodes; + +using Microsoft.Extensions.Logging; +using Opc.Ua; +using OpcPlc.Helpers; +using OpcPlc.PluginNodes.Models; +using System; +using System.Collections.Generic; + +/// +/// Node with an opaque identifier (free-format byte string that might or might not be human interpretable). +/// as well as nodes of type NodeId and ExpandedNodeId with IdType of String, Numeric, Opaque, and Guid. +/// +public class OpaqueAndNodeIdPluginNode(TimeService timeService, ILogger logger) : PluginNodeBase(timeService, logger), IPluginNodes +{ + private PlcNodeManager _plcNodeManager; + private SimulatedVariableNode _node; + + public void AddOptions(Mono.Options.OptionSet optionSet) + { + // on|opaquenode + // Add node with an opaque identifier. + // Enabled by default. + } + + public void AddToAddressSpace(FolderState telemetryFolder, FolderState methodsFolder, PlcNodeManager plcNodeManager) + { + _plcNodeManager = plcNodeManager; + + FolderState folder = _plcNodeManager.CreateFolder( + telemetryFolder, + path: "Special", + name: "Special", + NamespaceType.OpcPlcApplications); + + AddNodes(folder); + } + + public void StartSimulation() + { + _node.Start(value => value + 1, periodMs: 1000); + } + + public void StopSimulation() + { + _node.Stop(); + } + + private void AddNodes(FolderState folder) + { + BaseDataVariableState variable = _plcNodeManager.CreateBaseVariable( + folder, + path: new byte[] { (byte)'a', (byte)'b', (byte)'c' }, + name: "Opaque_abc", + new NodeId((uint)BuiltInType.UInt32), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Constantly increasing value", + NamespaceType.OpcPlcApplications, + defaultValue: (uint)0); + + _node = _plcNodeManager.CreateVariableNode(variable); + + BaseDataVariableState stringNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticNodeIdString", + "ScalarStaticNodeIdString", + new NodeId("ScalarStaticNodeIdString", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "String representation of the NodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new NodeId("this is a string node id", 3) + ); + + BaseDataVariableState numericNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticNodeIdNumeric", + "ScalarStaticNodeIdNumeric", + new NodeId("ScalarStaticNodeIdNumeric", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "UInt32 representation of the NodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new NodeId(42, 3) + ); + + BaseDataVariableState opaqueNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticNodeIdOpaque", + "ScalarStaticNodeIdOpaque", + new NodeId("ScalarStaticNodeIdOpaque", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Opaque representation of the NodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new NodeId(new byte[] { 0x01, 0x02, 0x03, 0x04 }, 3) + ); + + BaseDataVariableState guidNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticNodeIdGuid", + "ScalarStaticNodeIdGuid", + new NodeId("ScalarStaticNodeIdGuid", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Guid representation of the NodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new NodeId(Guid.NewGuid(), 3) + ); + + BaseDataVariableState stringExpandedNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticExpandedNodeIdString", + "ScalarStaticExpandedNodeIdString", + new NodeId("ScalarStaticExpandedNodeIdString", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "String representation of the ExpandedNodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new ExpandedNodeId("this is a string expanded node id", 3, OpcPlc.Namespaces.OpcPlcApplications, 0) + ); + + BaseDataVariableState numericExpandedNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticExpandedNodeIdNumeric", + "ScalarStaticExpandedNodeIdNumeric", + new NodeId("ScalarStaticExpandedNodeIdNumeric", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Numeric representation of the ExpandedNodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new ExpandedNodeId(444u, 3, OpcPlc.Namespaces.OpcPlcApplications, 0) + ); + + BaseDataVariableState guidExpandedNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticExpandedNodeIdGuid", + "ScalarStaticExpandedNodeIdGuid", + new NodeId("ScalarStaticExpandedNodeIdGuid", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Guid representation of the ExpandedNodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new ExpandedNodeId(Guid.NewGuid(), 3, OpcPlc.Namespaces.OpcPlcApplications, 0) + ); + + BaseDataVariableState opaqueExpandedNodeIdVariable = _plcNodeManager.CreateBaseVariable( + folder, + "ScalarStaticExpandedNodeIdOpaque", + "ScalarStaticExpandedNodeIdOpaque", + new NodeId("ScalarStaticExpandedNodeIdOpaque", 3), + ValueRanks.Scalar, + AccessLevels.CurrentReadOrWrite, + "Opaque representation of the ExpandedNodeId", + NamespaceType.OpcPlcApplications, + defaultValue: new ExpandedNodeId(new byte[] { 0xCA, 0xFE}, 3, OpcPlc.Namespaces.OpcPlcApplications, 0) + ); + + // Add to node list for creation of pn.json. + Nodes = new List + { + PluginNodesHelper.GetNodeWithIntervals(variable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(stringNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(numericNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(opaqueNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(guidNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(stringExpandedNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(numericExpandedNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(guidExpandedNodeIdVariable.NodeId, _plcNodeManager), + PluginNodesHelper.GetNodeWithIntervals(opaqueExpandedNodeIdVariable.NodeId, _plcNodeManager), + }; + } +} diff --git a/src/PluginNodes/OpaquePluginNode.cs b/src/PluginNodes/OpaquePluginNode.cs deleted file mode 100644 index 8ec134b3..00000000 --- a/src/PluginNodes/OpaquePluginNode.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace OpcPlc.PluginNodes; - -using Microsoft.Extensions.Logging; -using Opc.Ua; -using OpcPlc.Helpers; -using OpcPlc.PluginNodes.Models; -using System.Collections.Generic; - -/// -/// Node with an opaque identifier (free-format byte string that might or might not be human interpretable). -/// -public class OpaquePluginNode(TimeService timeService, ILogger logger) : PluginNodeBase(timeService, logger), IPluginNodes -{ - private PlcNodeManager _plcNodeManager; - private SimulatedVariableNode _node; - - public void AddOptions(Mono.Options.OptionSet optionSet) - { - // on|opaquenode - // Add node with an opaque identifier. - // Enabled by default. - } - - public void AddToAddressSpace(FolderState telemetryFolder, FolderState methodsFolder, PlcNodeManager plcNodeManager) - { - _plcNodeManager = plcNodeManager; - - FolderState folder = _plcNodeManager.CreateFolder( - telemetryFolder, - path: "Special", - name: "Special", - NamespaceType.OpcPlcApplications); - - AddNodes(folder); - } - - public void StartSimulation() - { - _node.Start(value => value + 1, periodMs: 1000); - } - - public void StopSimulation() - { - _node.Stop(); - } - - private void AddNodes(FolderState folder) - { - BaseDataVariableState variable = _plcNodeManager.CreateBaseVariable( - folder, - path: new byte[] { (byte)'a', (byte)'b', (byte)'c' }, - name: "Opaque_abc", - new NodeId((uint)BuiltInType.UInt32), - ValueRanks.Scalar, - AccessLevels.CurrentReadOrWrite, - "Constantly increasing value", - NamespaceType.OpcPlcApplications, - defaultValue: (uint)0); - - _node = _plcNodeManager.CreateVariableNode(variable); - - // Add to node list for creation of pn.json. - Nodes = new List - { - PluginNodesHelper.GetNodeWithIntervals(variable.NodeId, _plcNodeManager), - }; - } -} diff --git a/tests/OpaqueAndNodeIdTests.cs b/tests/OpaqueAndNodeIdTests.cs new file mode 100644 index 00000000..e15dbd4e --- /dev/null +++ b/tests/OpaqueAndNodeIdTests.cs @@ -0,0 +1,67 @@ +namespace OpcPlc.Tests; + +using FluentAssertions; +using NUnit.Framework; + +/// +/// Tests NodeIds of various IdTypes and ExpandedNodeIds. +/// +[TestFixture] +public class OpaqueAndNodeIdTests : SubscriptionTestsBase +{ + + [Test] + public void TestNodeIdNodes() + { + var specialFolder = FindNode(ObjectsFolder, Namespaces.OpcPlcApplications, "OpcPlc", "Telemetry", "Special"); + specialFolder.Should().NotBeNull(); + + var stringNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticNodeIdString"); + stringNodeId.Should().NotBeNull(); + var stringNodeIdValue = ReadValue(stringNodeId); + stringNodeIdValue.Should().NotBeNull(); + stringNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.String); + + var numericNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticNodeIdNumeric"); + numericNodeId.Should().NotBeNull(); + var numericNodeIdValue = ReadValue(numericNodeId); + numericNodeIdValue.Should().NotBeNull(); + numericNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Numeric); + + var guidNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticNodeIdGuid"); + guidNodeId.Should().NotBeNull(); + var guidNodeIdValue = ReadValue(guidNodeId); + guidNodeIdValue.Should().NotBeNull(); + guidNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Guid); + + var opaqueNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticNodeIdOpaque"); + opaqueNodeId.Should().NotBeNull(); + var opaqueNodeIdValue = ReadValue(opaqueNodeId); + opaqueNodeIdValue.Should().NotBeNull(); + opaqueNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Opaque); + + var stringExpandedNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticExpandedNodeIdString"); + stringExpandedNodeId.Should().NotBeNull(); + var stringExpandedNodeIdValue = ReadValue(stringExpandedNodeId); + stringExpandedNodeIdValue.Should().NotBeNull(); + stringExpandedNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.String); + + var numericExpandedNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticExpandedNodeIdNumeric"); + numericExpandedNodeId.Should().NotBeNull(); + var numericExpandedNodeIdValue = ReadValue(numericExpandedNodeId); + numericExpandedNodeIdValue.Should().NotBeNull(); + numericExpandedNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Numeric); + + var guidExpandedNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticExpandedNodeIdGuid"); + guidExpandedNodeId.Should().NotBeNull(); + var guidExpandedNodeIdValue = ReadValue(guidExpandedNodeId); + guidExpandedNodeIdValue.Should().NotBeNull(); + guidExpandedNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Guid); + + var opaqueExpandedNodeId = FindNode(specialFolder, Namespaces.OpcPlcApplications, "ScalarStaticExpandedNodeIdOpaque"); + opaqueExpandedNodeId.Should().NotBeNull(); + var opaqueExpandedNodeIdValue = ReadValue(opaqueExpandedNodeId); + opaqueExpandedNodeIdValue.Should().NotBeNull(); + opaqueExpandedNodeIdValue.IdType.Should().Be(Opc.Ua.IdType.Opaque); + } +}