From 00aa80bc713a3848a89478a03e0151dc90866b61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 05:53:41 +0000 Subject: [PATCH 1/4] Initial plan From c243b1bbba4e0e01599998f308619e0fedb72bdd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 06:07:04 +0000 Subject: [PATCH 2/4] Fix GetDataTypeId to return specific DataTypeId for IEncodeable types Modified TypeInfo.GetDataTypeId(Type) to check if IEncodeable types have a specific TypeId and return it instead of the generic Structure (i=22) DataTypeId. Added comprehensive tests for EUInformation and other well-known IEncodeable types. Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> --- Stack/Opc.Ua.Types/Utils/TypeInfo.cs | 33 +++++++++++ .../Types/Constants/DataTypesTests.cs | 57 +++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs index c030ae612..0ff2794d7 100644 --- a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs +++ b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs @@ -279,6 +279,39 @@ public static NodeId GetDataTypeId(Type type) } } + // Check if the type implements IEncodeable and has a specific TypeId + if (dataTypeId == DataTypeIds.Structure && + typeof(IEncodeable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) + { + // Try to get the TypeId from the type's property + var typeIdProperty = type.GetProperty("TypeId", BindingFlags.Public | BindingFlags.Static); + if (typeIdProperty != null && typeIdProperty.PropertyType == typeof(ExpandedNodeId)) + { + var expandedNodeId = (ExpandedNodeId)typeIdProperty.GetValue(null); + if (expandedNodeId != null && !NodeId.IsNull(expandedNodeId)) + { + return ExpandedNodeId.ToNodeId(expandedNodeId, null); + } + } + + // Fallback: try to create an instance and get its TypeId + try + { + if (type.GetConstructor(Type.EmptyTypes) != null) + { + var instance = Activator.CreateInstance(type) as IEncodeable; + if (instance?.TypeId != null) + { + return ExpandedNodeId.ToNodeId(instance.TypeId, null); + } + } + } + catch + { + // If we can't create an instance, fall back to Structure + } + } + return dataTypeId; } diff --git a/Tests/Opc.Ua.Core.Tests/Types/Constants/DataTypesTests.cs b/Tests/Opc.Ua.Core.Tests/Types/Constants/DataTypesTests.cs index 585ae737d..591e584ac 100644 --- a/Tests/Opc.Ua.Core.Tests/Types/Constants/DataTypesTests.cs +++ b/Tests/Opc.Ua.Core.Tests/Types/Constants/DataTypesTests.cs @@ -165,5 +165,62 @@ public void GetBrowseName_GetIdentifier_AreInverseOperations() Assert.AreEqual((uint)id, retrievedId); } } + + /// + /// Test GetDataTypeId for EUInformation type returns specific DataTypeId (i=887) not Structure (i=22). + /// + [Test] + public void GetDataTypeId_EUInformationType_ReturnsSpecificDataTypeId() + { + NodeId dataTypeId = DataTypes.GetDataTypeId(typeof(EUInformation)); + + Assert.IsNotNull(dataTypeId); + Assert.AreEqual(DataTypes.EUInformation, (uint)dataTypeId.Identifier); + Assert.AreEqual(0, dataTypeId.NamespaceIndex); + Assert.AreNotEqual(DataTypes.Structure, (uint)dataTypeId.Identifier, + "Should return specific EUInformation DataTypeId (i=887), not generic Structure (i=22)"); + } + + /// + /// Test GetDataTypeId for EUInformation instance returns specific DataTypeId. + /// + [Test] + public void GetDataTypeId_EUInformationInstance_ReturnsSpecificDataTypeId() + { + var euInfo = new EUInformation("unit", "http://test.org"); + NodeId dataTypeId = DataTypes.GetDataTypeId(euInfo); + + Assert.IsNotNull(dataTypeId); + Assert.AreEqual(DataTypes.EUInformation, (uint)dataTypeId.Identifier); + Assert.AreEqual(0, dataTypeId.NamespaceIndex); + } + + /// + /// Test GetDataTypeId for various well-known IEncodeable types returns their specific DataTypeIds. + /// + [Test] + public void GetDataTypeId_WellKnownEncodeableTypes_ReturnsSpecificDataTypeIds() + { + // Test various well-known types that implement IEncodeable + var testCases = new[] + { + (typeof(EUInformation), DataTypes.EUInformation), + (typeof(Range), DataTypes.Range), + (typeof(Argument), DataTypes.Argument), + (typeof(EnumValueType), DataTypes.EnumValueType), + (typeof(TimeZoneDataType), DataTypes.TimeZoneDataType) + }; + + foreach (var (type, expectedId) in testCases) + { + NodeId dataTypeId = DataTypes.GetDataTypeId(type); + + Assert.IsNotNull(dataTypeId, $"DataTypeId should not be null for {type.Name}"); + Assert.AreEqual(expectedId, (uint)dataTypeId.Identifier, + $"DataTypeId for {type.Name} should be i={expectedId}, not i={dataTypeId.Identifier}"); + Assert.AreEqual(0, dataTypeId.NamespaceIndex, + $"NamespaceIndex should be 0 for {type.Name}"); + } + } } } From 821553fc8e4b2dc73e59afc098afdd151c9198f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 06:12:52 +0000 Subject: [PATCH 3/4] Simplify GetDataTypeId fix based on code review feedback Removed unnecessary static property check and simplified the instance creation logic. Caught specific exceptions (MissingMethodException, TargetInvocationException) instead of using bare catch. Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> --- Stack/Opc.Ua.Types/Utils/TypeInfo.cs | 29 ++++++++++------------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs index 0ff2794d7..1ddb88760 100644 --- a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs +++ b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs @@ -283,32 +283,23 @@ public static NodeId GetDataTypeId(Type type) if (dataTypeId == DataTypeIds.Structure && typeof(IEncodeable).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) { - // Try to get the TypeId from the type's property - var typeIdProperty = type.GetProperty("TypeId", BindingFlags.Public | BindingFlags.Static); - if (typeIdProperty != null && typeIdProperty.PropertyType == typeof(ExpandedNodeId)) + // Try to create an instance and get its TypeId + // All well-known IEncodeable types have parameterless constructors and instance TypeId properties + try { - var expandedNodeId = (ExpandedNodeId)typeIdProperty.GetValue(null); - if (expandedNodeId != null && !NodeId.IsNull(expandedNodeId)) + var instance = Activator.CreateInstance(type) as IEncodeable; + if (instance?.TypeId != null) { - return ExpandedNodeId.ToNodeId(expandedNodeId, null); + return ExpandedNodeId.ToNodeId(instance.TypeId, null); } } - - // Fallback: try to create an instance and get its TypeId - try + catch (MissingMethodException) { - if (type.GetConstructor(Type.EmptyTypes) != null) - { - var instance = Activator.CreateInstance(type) as IEncodeable; - if (instance?.TypeId != null) - { - return ExpandedNodeId.ToNodeId(instance.TypeId, null); - } - } + // Type doesn't have a parameterless constructor, fall back to Structure } - catch + catch (TargetInvocationException) { - // If we can't create an instance, fall back to Structure + // Constructor threw an exception, fall back to Structure } } From f7b2d9450f1a910baeb6f424ce4160d3f07e4dbe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 22 Nov 2025 06:17:54 +0000 Subject: [PATCH 4/4] Add additional exception handling for GetDataTypeId Added handling for MethodAccessException and NotSupportedException to provide more comprehensive error handling when creating instances via Activator.CreateInstance. Co-authored-by: marcschier <11168470+marcschier@users.noreply.github.com> --- Stack/Opc.Ua.Types/Utils/TypeInfo.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs index 1ddb88760..d115a93fd 100644 --- a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs +++ b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs @@ -301,6 +301,14 @@ public static NodeId GetDataTypeId(Type type) { // Constructor threw an exception, fall back to Structure } + catch (MethodAccessException) + { + // No permission to create instance, fall back to Structure + } + catch (NotSupportedException) + { + // Type is not supported for instantiation, fall back to Structure + } } return dataTypeId;