Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Stack/Opc.Ua.Types/Utils/TypeInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
57 changes: 57 additions & 0 deletions Tests/Opc.Ua.Core.Tests/Types/Constants/DataTypesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,5 +165,62 @@ public void GetBrowseName_GetIdentifier_AreInverseOperations()
Assert.AreEqual((uint)id, retrievedId);
}
}

/// <summary>
/// Test GetDataTypeId for EUInformation type returns specific DataTypeId (i=887) not Structure (i=22).
/// </summary>
[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)");
}

/// <summary>
/// Test GetDataTypeId for EUInformation instance returns specific DataTypeId.
/// </summary>
[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);
}

/// <summary>
/// Test GetDataTypeId for various well-known IEncodeable types returns their specific DataTypeIds.
/// </summary>
[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}");
}
}
}
}
Loading