diff --git a/Libraries/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs b/Libraries/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs index 83d2070db5..0ad2cc30f6 100644 --- a/Libraries/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs +++ b/Libraries/Opc.Ua.Client.ComplexTypes/ComplexTypeSystem.cs @@ -899,7 +899,7 @@ namespace Opc.Ua.Client.ComplexTypes if (item is Schema.Binary.EnumeratedType enumeratedObject) { // 1. use Dictionary entry - var enumDefinition = enumeratedObject.ToEnumDefinition(); + var enumDefinition = enumeratedObject.ToEnumDefinition(enumeratedObject.Name); // Add EnumDefinition to cache m_dataTypeDefinitionCache[enumType.NodeId] = enumDefinition; @@ -971,12 +971,12 @@ namespace Opc.Ua.Client.ComplexTypes if (enumTypeArray is ExtensionObject[] extensionObject) { // 2. use EnumValues - enumDefinition = extensionObject.ToEnumDefinition(); + enumDefinition = extensionObject.ToEnumDefinition(name.Name); } else if (enumTypeArray is LocalizedText[] localizedText) { // 3. use EnumStrings - enumDefinition = localizedText.ToEnumDefinition(); + enumDefinition = localizedText.ToEnumDefinition(name.Name); } else { diff --git a/Libraries/Opc.Ua.Client.ComplexTypes/DataTypeDefinitionExtension.cs b/Libraries/Opc.Ua.Client.ComplexTypes/DataTypeDefinitionExtension.cs index 3807406467..38db6e8210 100644 --- a/Libraries/Opc.Ua.Client.ComplexTypes/DataTypeDefinitionExtension.cs +++ b/Libraries/Opc.Ua.Client.ComplexTypes/DataTypeDefinitionExtension.cs @@ -237,20 +237,33 @@ namespace Opc.Ua.Client.ComplexTypes /// Convert a binary schema enumerated type to an enum data type definition /// Available before OPC UA V1.04. /// - public static EnumDefinition ToEnumDefinition(this Schema.Binary.EnumeratedType enumeratedType) + public static EnumDefinition ToEnumDefinition(this Schema.Binary.EnumeratedType enumeratedType, string enumTypeName) { var enumDefinition = new EnumDefinition(); - foreach (var enumValue in enumeratedType.EnumeratedValue) + if (enumeratedType.EnumeratedValue != null) { - var enumTypeField = new EnumField + foreach (var enumValue in enumeratedType.EnumeratedValue) { - Name = enumValue.Name, - Value = enumValue.Value, - Description = enumValue.Documentation?.Text?.FirstOrDefault(), - DisplayName = enumValue.Name - }; - enumDefinition.Fields.Add(enumTypeField); + var fieldName = enumValue.Name; + if (string.IsNullOrEmpty(fieldName)) + { + if (string.IsNullOrEmpty(enumTypeName)) + { + // Here we give up because the overall type is broken + return null; + } + fieldName = $"{enumTypeName}_{enumValue.Value}"; + } + + var enumTypeField = new EnumField { + Name = fieldName, + Value = enumValue.Value, + Description = enumValue.Documentation?.Text?.FirstOrDefault(), + DisplayName = enumValue.Name + }; + enumDefinition.Fields.Add(enumTypeField); + } } return enumDefinition; @@ -260,14 +273,30 @@ namespace Opc.Ua.Client.ComplexTypes /// Convert a list of EnumValues to an enum data type definition /// Available before OPC UA V1.04. /// - public static EnumDefinition ToEnumDefinition(this ExtensionObject[] enumValueTypes) + public static EnumDefinition ToEnumDefinition(this ExtensionObject[] enumValueTypes, string enumTypeName) { var enumDefinition = new EnumDefinition(); foreach (var extensionObject in enumValueTypes) { - var enumValue = extensionObject.Body as EnumValueType; + if (extensionObject.Body is not EnumValueType enumValue) + { + // All we can do here is skip this value. Since there is no + // fallback it is better to include all other type fields if + // they are in the extension object array. + continue; + } + var name = enumValue.DisplayName.Text; + if (string.IsNullOrEmpty(name)) + { + if (string.IsNullOrEmpty(enumTypeName)) + { + // Here we give up because the overall type is broken + return null; + } + name = $"{enumTypeName}_{enumValue.Value}"; + } var enumTypeField = new EnumField { Name = name, @@ -284,7 +313,7 @@ namespace Opc.Ua.Client.ComplexTypes /// Convert a list of EnumValues to an enum data type definition /// Available before OPC UA V1.04. /// - public static EnumDefinition ToEnumDefinition(this LocalizedText[] enumFieldNames) + public static EnumDefinition ToEnumDefinition(this LocalizedText[] enumFieldNames, string enumTypeName) { var enumDefinition = new EnumDefinition(); @@ -293,6 +322,16 @@ namespace Opc.Ua.Client.ComplexTypes LocalizedText enumFieldName = enumFieldNames[ii]; var name = enumFieldName.Text; + if (string.IsNullOrEmpty(name)) + { + if (string.IsNullOrEmpty(enumTypeName)) + { + // Here we give up because the overall type is broken + return null; + } + name = $"{enumTypeName}_{ii}"; + } + var enumTypeField = new EnumField { Name = name, Value = ii, diff --git a/Libraries/Opc.Ua.Client.ComplexTypes/TypeBuilder/ComplexTypeBuilder.cs b/Libraries/Opc.Ua.Client.ComplexTypes/TypeBuilder/ComplexTypeBuilder.cs index 455132113e..6ee3cf93d5 100644 --- a/Libraries/Opc.Ua.Client.ComplexTypes/TypeBuilder/ComplexTypeBuilder.cs +++ b/Libraries/Opc.Ua.Client.ComplexTypes/TypeBuilder/ComplexTypeBuilder.cs @@ -28,6 +28,7 @@ * ======================================================================*/ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; @@ -79,16 +80,42 @@ namespace Opc.Ua.Client.ComplexTypes { throw new ArgumentNullException(nameof(enumDefinition)); } + if (typeName == null) + { + throw new ArgumentNullException(nameof(typeName)); + } + + if (string.IsNullOrEmpty(typeName.Name)) + { + // The type name should not be null or empty then the type definition + // or xml is broken. For the latter we want to go into the fallback path + // And try to read the data type definition's enum definition. + return null; + } var enumBuilder = m_moduleBuilder.DefineEnum( GetFullQualifiedTypeName(typeName), TypeAttributes.Public, typeof(int)); enumBuilder.DataContractAttribute(m_targetNamespace); - foreach (var enumValue in enumDefinition.Fields) + if (enumDefinition.Fields != null) { - var newEnum = enumBuilder.DefineLiteral(enumValue.Name, (int)enumValue.Value); - newEnum.EnumMemberAttribute(enumValue.Name, (int)enumValue.Value); + var fieldNames = new HashSet(); + foreach (EnumField enumValue in enumDefinition.Fields) + { + // Create a field from the type name and ensure it is not a duplicate + var fieldName = enumValue.Name; + if (string.IsNullOrEmpty(fieldName)) + { + // This is to be super safe, but we should never get here. + fieldName = $"{typeName.Name}_{enumValue.Value}"; + } + if (fieldNames.Add(fieldName)) + { + var newEnum = enumBuilder.DefineLiteral(fieldName, (int)enumValue.Value); + newEnum.EnumMemberAttribute(fieldName, (int)enumValue.Value); + } + } } return enumBuilder.CreateTypeInfo(); }