diff --git a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs
index c030ae612..d115a93fd 100644
--- a/Stack/Opc.Ua.Types/Utils/TypeInfo.cs
+++ b/Stack/Opc.Ua.Types/Utils/TypeInfo.cs
@@ -279,6 +279,38 @@ 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 create an instance and get its TypeId
+ // All well-known IEncodeable types have parameterless constructors and instance TypeId properties
+ try
+ {
+ var instance = Activator.CreateInstance(type) as IEncodeable;
+ if (instance?.TypeId != null)
+ {
+ return ExpandedNodeId.ToNodeId(instance.TypeId, null);
+ }
+ }
+ catch (MissingMethodException)
+ {
+ // Type doesn't have a parameterless constructor, fall back to Structure
+ }
+ catch (TargetInvocationException)
+ {
+ // 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;
}
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}");
+ }
+ }
}
}