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;