diff --git a/NodeSetToAML.cs b/NodeSetToAML.cs index 80330cd..41d48f1 100644 --- a/NodeSetToAML.cs +++ b/NodeSetToAML.cs @@ -3160,35 +3160,82 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode // Now fill the data - AddModifyAttribute( structureFieldAttribute.Attribute, - "Name", "String", new Variant( field.Name ) ); - AddModifyAttribute( structureFieldAttribute.Attribute, - "ValueRank", "Int32", new Variant( field.ValueRank ) ); - AddModifyAttribute( structureFieldAttribute.Attribute, - "IsOptional", "Boolean", new Variant( field.IsOptional) ); - - SetArrayDimensions( structureFieldAttribute.Attribute, field.ArrayDimensions ); - RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "StructureFieldDefinition"); - if ( string.IsNullOrEmpty(field.ArrayDimensions)) + RemoveUnwantedAttribute(structureFieldAttribute, "Name"); + + if ( field.ValueRank == ValueRanks.Scalar || + field.ValueRank >= ValueRanks.OneDimension ) + { + AddModifyAttribute(structureFieldAttribute.Attribute, + "ValueRank", "Int32", new Variant(field.ValueRank)); + } + else { - RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "UInt32"); + RemoveUnwantedAttribute( structureFieldAttribute, "ValueRank" ); } - AddModifyAttribute( structureFieldAttribute.Attribute, - "AllowSubtypes", "Boolean", new Variant( field.AllowSubTypes ) ); - AddModifyAttribute( structureFieldAttribute.Attribute, - "MaxStringLength", "UInt32", new Variant( field.MaxStringLength ) ); + if ( field.IsOptional ) + { + AddModifyAttribute(structureFieldAttribute.Attribute, + "IsOptional", "Boolean", new Variant(true)); + } + else + { + RemoveUnwantedAttribute(structureFieldAttribute, "IsOptional"); + } - if( field.Description != null && field.Description.Length > 0 ) + if ( field.ValueRank >= ValueRanks.OneDimension && + !string.IsNullOrEmpty( field.ArrayDimensions ) ) + { + SetArrayDimensions(structureFieldAttribute.Attribute, field.ArrayDimensions); + RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], + "StructureFieldDefinition"); + } + else + { + RemoveUnwantedAttribute(structureFieldAttribute, "ArrayDimensions"); + } + + + // Max String Length is only for strings and bytestrings + // This seems to be a point for discussion. + // Do we put max string length in if it zero? + if ( field.MaxStringLength > 0 && + m_modelManager.IsTypeOf( field.DecodedDataType, Opc.Ua.DataTypeIds.String ) || + m_modelManager.IsTypeOf( field.DecodedDataType, Opc.Ua.DataTypeIds.ByteString ) ) { - LocalizedText localizedText = new LocalizedText( - field.Description[0].Locale, field.Description[ 0 ].Value ); AddModifyAttribute( structureFieldAttribute.Attribute, - "Description", "LocalizedText", new Variant( localizedText ) ); + "MaxStringLength", "UInt32", new Variant( field.MaxStringLength ) ); + } + else if ( structureFieldAttribute.Attribute[ "MaxStringLength" ] != null ) + { + RemoveUnwantedAttribute( structureFieldAttribute, "MaxStringLength" ); + } + + if (field.Description != null && field.Description.Length > 0) + { + List localizedTextList = new List(field.Description.Length); + foreach(NodeSet.LocalizedText description in field.Description) + { + localizedTextList.Add( + new Variant( + new LocalizedText(description.Locale, description.Value))); + } + Variant localizedTextArray = new Variant(localizedTextList); + + LocalizedText localizedText = new LocalizedText( + field.Description[0].Locale, field.Description[0].Value); + AddModifyAttribute(structureFieldAttribute.Attribute, + "Description", "LocalizedText", localizedTextArray, + bListOf: true); + RemoveUnwantedAttribute(structureFieldAttribute.Attribute["Description"], + "StructureFieldDefinition"); + } + else if (structureFieldAttribute.Attribute["Description"] != null) + { + RemoveUnwantedAttribute(structureFieldAttribute, "Description"); } - RemoveUnwantedAttribute(structureFieldAttribute.Attribute["Description"], - "StructureFieldDefinition"); + // Remove the NodeId from the structure Field AttributeType nodeIdAttribute = structureFieldAttribute.Attribute[ "DataType" ]; @@ -3200,8 +3247,10 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode RemoveUnwantedNodeIdAttribute(structureFieldAttribute); RemoveNodeIdsFromDefinition(structureFieldAttribute); - - fieldDefinitionAttribute.Attribute.Insert( structureFieldAttribute ); + if (structureFieldAttribute.Attribute.Count > 0) + { + fieldDefinitionAttribute.Attribute.Insert(structureFieldAttribute); + } } } } diff --git a/SystemTest/NodeSetFiles/Modified.Opc.Ua.NodeSet2.xml b/SystemTest/NodeSetFiles/Modified.Opc.Ua.NodeSet2.xml index 1cf436a..a09946b 100644 --- a/SystemTest/NodeSetFiles/Modified.Opc.Ua.NodeSet2.xml +++ b/SystemTest/NodeSetFiles/Modified.Opc.Ua.NodeSet2.xml @@ -9723,26 +9723,19 @@ - 3DCartesianCoordinates - Base Info Spatial Data - https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26 - - i=18809 - - - - No Locale - - - With Locale - - - Multiple Descriptions - Multiple Descriptions with Locale - - - - + 3DCartesianCoordinates + Base Info Spatial Data + https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26 + + i=18809 + + + + + + + + Orientation Base Info Spatial Data https://reference.opcfoundation.org/v105/Core/docs/Part5/12.27 diff --git a/SystemTest/NodeSetFiles/TestAml.xml b/SystemTest/NodeSetFiles/TestAml.xml index 28566ce..4d1e558 100644 --- a/SystemTest/NodeSetFiles/TestAml.xml +++ b/SystemTest/NodeSetFiles/TestAml.xml @@ -335,14 +335,37 @@ Quality of Service Category + Catégorie de qualité de service + Kategorie „Dienstqualität“ Transmit Quality of Service - + + + + 3DCartesianCoordinates + Base Info Spatial Data + https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26 + + i=18809 + + + + No Locale + + + With Locale + + + Multiple Descriptions + Multiple Descriptions with Locale + + + http://opcfoundation.org/UA/FX/AML/TESTING @@ -3563,7 +3586,7 @@ diff --git a/SystemTest/TestStructureFieldDefinition.cs b/SystemTest/TestStructureFieldDefinition.cs index 80761e9..e7ac1bc 100644 --- a/SystemTest/TestStructureFieldDefinition.cs +++ b/SystemTest/TestStructureFieldDefinition.cs @@ -1,10 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; using Aml.Engine.CAEX; using Aml.Engine.CAEX.Extensions; -using System.Linq; using System; -using Opc.Ua; namespace SystemTest { @@ -26,7 +23,7 @@ public void TestUnwantedAttributes(TestHelper.Uris uriId, uint nodeId) foreach( AttributeType attribute in objectToTest.Attribute ) { - if ( attribute.Name != "NodeId") + if ( attribute.Name != "NodeId" && attribute.Name != "PracticallyEmpty" ) { AttributeType structureAttribute = GetAttribute(attribute, "StructureFieldDefinition"); foreach(AttributeType definitionAttribute in structureAttribute.Attribute) @@ -50,81 +47,82 @@ public void TestUnwantedAttributes(TestHelper.Uris uriId, uint nodeId) } [TestMethod, Timeout(TestHelper.UnitTestTimeout)] - [DataRow("QosCategory","Name", "QosCategory")] - [DataRow("QosCategory", "Description", "Quality of Service Category")] - [DataRow("QosCategory", "ValueRank", "-1")] - [DataRow("QosCategory", "ArrayDimensions", null)] - [DataRow("QosCategory", "MaxStringLength", "123")] - [DataRow("QosCategory", "IsOptional", "true")] - [DataRow("QosCategory", "AllowSubtypes", "false")] - - [DataRow("DatagramQos", "Name", "DatagramQos")] - [DataRow("DatagramQos", "Description", "Transmit Quality of Service")] - [DataRow("DatagramQos", "ValueRank", "2")] - [DataRow("DatagramQos", "MaxStringLength", "0")] - [DataRow("DatagramQos", "IsOptional", "false")] - [DataRow("DatagramQos", "AllowSubtypes", "true")] - - [DataRow("NoDescription", "Name", "NoDescription")] - [DataRow("NoDescription", "Description", null)] - [DataRow("NoDescription", "ValueRank", "-1")] - [DataRow("NoDescription", "ArrayDimensions", null)] - [DataRow("NoDescription", "MaxStringLength", "321")] - [DataRow("NoDescription", "IsOptional", "false")] - [DataRow("NoDescription", "AllowSubtypes", "false")] + [DataRow("QosCategory","Name", null, "", "")] + [DataRow("QosCategory", "Description", "Quality of Service Category", "0", "en")] + [DataRow("QosCategory", "Description", "Catégorie de qualité de service", "1", "fr")] + [DataRow("QosCategory", "Description", "Kategorie „Dienstqualität“", "2", "")] + [DataRow("QosCategory", "ValueRank", "-1", "", "")] + [DataRow("QosCategory", "ArrayDimensions", null, "", "")] + [DataRow("QosCategory", "MaxStringLength", "123", "", "")] + [DataRow("QosCategory", "IsOptional", "true", "", "")] + + [DataRow("DatagramQos", "Name", null, "", "")] + [DataRow("DatagramQos", "Description", "Transmit Quality of Service", "0", "")] + [DataRow("DatagramQos", "ArrayDimensions", "2", "0", "")] + [DataRow("DatagramQos", "ArrayDimensions", "3", "1", "")] + [DataRow("DatagramQos", "ValueRank", "2", "", "")] + [DataRow("DatagramQos", "IsOptional", null, "", "")] + + [DataRow("NoDescription", "Name", null, "", "")] + [DataRow("NoDescription", "Description", null, "", "")] + [DataRow("NoDescription", "ValueRank", null, "", "")] + [DataRow("NoDescription", "ArrayDimensions", null, "", "")] + [DataRow("NoDescription", "MaxStringLength", "321", "", "")] + [DataRow("NoDescription", "IsOptional", null, "", "")] public void TestAttributeValues(string variableName, string attributeName, - string expectedValue) - { - AttributeValues(variableName, attributeName, expectedValue); - } - - [TestMethod, Timeout(TestHelper.UnitTestTimeout)] - public void TestDescriptionLocale() - { - AttributeValues("QosCategory", "Description", "Quality of Service Category", "en"); - } - - [TestMethod, Timeout(TestHelper.UnitTestTimeout)] - public void TestArrayDimensions() - { - AttributeType structured = GetStructured(TestHelper.Uris.Test, - PublisherQosDataType, "DatagramQos"); - AttributeType attribute = GetAttribute(structured, "ArrayDimensions"); - AttributeType first = GetAttribute(attribute, "0"); - Assert.AreEqual("2", first.Value, "Unexpected value for ArrayDimensions[0]."); - AttributeType second = GetAttribute(attribute, "1"); - Assert.AreEqual("3", second.Value, "Unexpected value for ArrayDimensions[1]."); - } - - public void AttributeValues(string variableName, - string attributeName, string expectedValue, - string localeId = "") + string arrayIndex, + string localeId) { AttributeFamilyType objectToTest = GetTestAttribute(TestHelper.Uris.Test, PublisherQosDataType); AttributeType variableAttribute = GetAttribute(objectToTest.Attribute, variableName); AttributeType structured = GetAttribute(variableAttribute, "StructureFieldDefinition"); - AttributeType attribute = GetAttribute(structured.Attribute, attributeName); - Assert.AreEqual(expectedValue, attribute.Value, - $"Unexpected value for {variableName}.{attributeName} in {structured.Name}."); - if (!string.IsNullOrEmpty(localeId)) + if (string.IsNullOrEmpty(expectedValue)) + { + // attributeName should not exist + Assert.IsNull(structured.Attribute[attributeName], + $"Attribute {attributeName} exists in {variableName} when it should not."); + } + else { - AttributeType locale = GetAttribute(attribute.Attribute, localeId); - Assert.AreEqual(expectedValue, locale.Value, - $"Unexpected locale value for {variableName}.{attributeName} in {structured.Name}."); + AttributeType attribute = GetAttribute(structured.Attribute, attributeName); + + if (!string.IsNullOrEmpty(arrayIndex)) + { + attribute = GetAttribute(attribute, arrayIndex); + } + + Assert.AreEqual(expectedValue, attribute.Value, + $"Unexpected value for {variableName}.{attributeName} in {structured.Name}."); + + if (!string.IsNullOrEmpty(localeId)) + { + AttributeType locale = GetAttribute(attribute.Attribute, localeId); + Assert.AreEqual(expectedValue, locale.Value, + $"Unexpected locale value for {variableName}.{attributeName} in {structured.Name}."); + } } } + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + public void TestUnwantedStructureAttribute() + { + AttributeFamilyType objectToTest = GetTestAttribute(TestHelper.Uris.Test, PublisherQosDataType); + AttributeType emptyAttribute = GetAttribute(objectToTest.Attribute, "PracticallyEmpty"); + Assert.IsNull(emptyAttribute.Attribute["StructureFieldDefinition"], + "Unexpected StructureFieldDefinition found in PracticallyEmpty"); + } + #endregion - #region Helpers + #region Helpers private CAEXDocument GetDocument() {