diff --git a/NodeSetToAML.cs b/NodeSetToAML.cs
index 611b1e8..80330cd 100644
--- a/NodeSetToAML.cs
+++ b/NodeSetToAML.cs
@@ -2818,18 +2818,24 @@ private void ProcessReferenceType(ref InterfaceClassLibType icl, NodeId nodeId)
MinimizeNodeId( nodeIdAttribute );
}
- private void RemoveUnwantedNodeIdAttribute( AttributeType attribute )
+
+ private void RemoveUnwantedAttribute(AttributeType attributeType, string attributeName)
{
- if (attribute != null)
+ if (attributeType != null)
{
- AttributeType unwantedNodeIdAttribute = attribute.Attribute["NodeId"];
- if (unwantedNodeIdAttribute != null)
+ AttributeType unwantedAttribute = attributeType.Attribute[attributeName];
+ if (unwantedAttribute != null)
{
- attribute.Attribute.RemoveElement(unwantedNodeIdAttribute);
+ attributeType.Attribute.RemoveElement(unwantedAttribute);
}
}
}
+ private void RemoveUnwantedNodeIdAttribute(AttributeType attribute)
+ {
+ RemoveUnwantedAttribute(attribute, "NodeId");
+ }
+
private void RemoveNodeIdsFromDefinition(AttributeType attribute)
{
if (attribute != null)
@@ -3162,6 +3168,11 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
"IsOptional", "Boolean", new Variant( field.IsOptional) );
SetArrayDimensions( structureFieldAttribute.Attribute, field.ArrayDimensions );
+ RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "StructureFieldDefinition");
+ if ( string.IsNullOrEmpty(field.ArrayDimensions))
+ {
+ RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "UInt32");
+ }
AddModifyAttribute( structureFieldAttribute.Attribute,
"AllowSubtypes", "Boolean", new Variant( field.AllowSubTypes ) );
@@ -3176,6 +3187,9 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
"Description", "LocalizedText", new Variant( localizedText ) );
}
+ RemoveUnwantedAttribute(structureFieldAttribute.Attribute["Description"],
+ "StructureFieldDefinition");
+
// Remove the NodeId from the structure Field
AttributeType nodeIdAttribute = structureFieldAttribute.Attribute[ "DataType" ];
if( nodeIdAttribute != null )
@@ -3183,6 +3197,10 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
structureFieldAttribute.Attribute.RemoveElement( nodeIdAttribute );
}
+ RemoveUnwantedNodeIdAttribute(structureFieldAttribute);
+ RemoveNodeIdsFromDefinition(structureFieldAttribute);
+
+
fieldDefinitionAttribute.Attribute.Insert( structureFieldAttribute );
}
}
diff --git a/SystemTest/NodeSetFiles/TestAml.xml b/SystemTest/NodeSetFiles/TestAml.xml
index 96acbf3..28566ce 100644
--- a/SystemTest/NodeSetFiles/TestAml.xml
+++ b/SystemTest/NodeSetFiles/TestAml.xml
@@ -40,6 +40,7 @@
i=865
i=868
i=874
+ i=23604
i=24216
ns=1;i=3003
@@ -308,6 +309,8 @@
+
+
ComplexVariableType
@@ -318,8 +321,30 @@
+
+ PublisherQosDataType
+ UAFX AutomationComponent Base
+ UAFX FunctionalEntity Base
+ https://reference.opcfoundation.org/UAFX/Part81/v100/docs/10.36
+
+
+ i=22
+
+
+
+ Quality of Service Category
+
+
+ Transmit Quality of Service
+
+
+
+
+
-
+
http://opcfoundation.org/UA/FX/AML/TESTING
i=11715
@@ -3538,7 +3563,7 @@
diff --git a/SystemTest/TestHelper.cs b/SystemTest/TestHelper.cs
index 419358c..ff15a36 100644
--- a/SystemTest/TestHelper.cs
+++ b/SystemTest/TestHelper.cs
@@ -79,7 +79,7 @@ public static CAEXDocument GetReadOnlyDocument( string filename )
{
foreach( FileInfo fileInfo in RetrieveFiles() )
{
- if( fileInfo.Name.Equals( filename ) )
+ if( fileInfo.Name.Equals( filename, StringComparison.OrdinalIgnoreCase ) )
{
AutomationMLContainer container = new AutomationMLContainer( fileInfo.FullName,
System.IO.FileMode.Open, FileAccess.Read );
diff --git a/SystemTest/TestStructureFieldDefinition.cs b/SystemTest/TestStructureFieldDefinition.cs
new file mode 100644
index 0000000..80761e9
--- /dev/null
+++ b/SystemTest/TestStructureFieldDefinition.cs
@@ -0,0 +1,175 @@
+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
+{
+ [TestClass]
+ public class TestStructureFieldDefinition
+ {
+ CAEXDocument m_document = null;
+
+ #region Tests
+ private const uint PublisherQosDataType = 3006;
+
+ [TestMethod, Timeout(TestHelper.UnitTestTimeout)]
+ [DataRow(TestHelper.Uris.Root, Opc.Ua.DataTypes.AggregateConfiguration, DisplayName = "Root AggregateConfiguration")]
+ [DataRow(TestHelper.Uris.Test, PublisherQosDataType, DisplayName = "AutomationComponent PublisherQosDataType")]
+
+ public void TestUnwantedAttributes(TestHelper.Uris uriId, uint nodeId)
+ {
+ AttributeFamilyType objectToTest = GetTestAttribute(uriId, nodeId);
+
+ foreach( AttributeType attribute in objectToTest.Attribute )
+ {
+ if ( attribute.Name != "NodeId")
+ {
+ AttributeType structureAttribute = GetAttribute(attribute, "StructureFieldDefinition");
+ foreach(AttributeType definitionAttribute in structureAttribute.Attribute)
+ {
+ // Make sure there is no NodeId for the StructureFieldDefinition
+ // It's always Opc.Ua.DataTypes.StructureField (101)
+ Assert.IsFalse(definitionAttribute.Name.Contains("NodeId"));
+ // Make sure the structure field does not have a NodeId either.
+ // It's always the known datatype of the field.
+ Assert.IsNull(definitionAttribute.Attribute["NodeId"]);
+ if ( definitionAttribute.Name.Equals("Description"))
+ {
+ // Make sure there is no structure field Definition.
+ // It's always a known datatype of localized text
+ Assert.IsNull(definitionAttribute.Attribute["StructureFieldDefinition"],
+ "Unexpected StructureFieldDefinition found in Description: " + structureAttribute.Name);
+ }
+ }
+ }
+ }
+ }
+
+ [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")]
+
+ 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 = "")
+ {
+ 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))
+ {
+ AttributeType locale = GetAttribute(attribute.Attribute, localeId);
+ Assert.AreEqual(expectedValue, locale.Value,
+ $"Unexpected locale value for {variableName}.{attributeName} in {structured.Name}.");
+ }
+ }
+
+
+
+ #endregion
+
+ #region Helpers
+
+ private CAEXDocument GetDocument()
+ {
+ if( m_document == null )
+ {
+ m_document = TestHelper.GetReadOnlyDocument( "TestAml.xml.amlx" );
+ }
+ Assert.IsNotNull( m_document, "Unable to retrieve Document" );
+ return m_document;
+ }
+
+ public AttributeFamilyType GetTestAttribute( TestHelper.Uris uriId, uint nodeId )
+ {
+ CAEXDocument document = GetDocument();
+ string amlId = TestHelper.BuildAmlId("", uriId, nodeId.ToString() );
+ Console.WriteLine( "Looking for " + amlId );
+ CAEXObject initialObject = document.FindByID( amlId );
+ Assert.IsNotNull( initialObject, "Unable to find Initial Object" );
+ AttributeFamilyType theObject = initialObject as AttributeFamilyType;
+ Assert.IsNotNull( theObject, "Unable to Cast Initial Object" );
+ return theObject;
+ }
+
+ public AttributeType GetAttribute(AttributeType attributeType, string attributeName)
+ {
+ Assert.IsNotNull(attributeType, "AttributeType is null");
+ return GetAttribute(attributeType.Attribute, attributeName);
+ }
+
+ public AttributeType GetAttribute( AttributeSequence attributes, string attributeName)
+ {
+ Assert.IsNotNull(attributes, "AttributeType is null");
+ AttributeType result = attributes[attributeName];
+ Assert.IsNotNull(result, "Unable to find Attribute " + attributeName);
+ return result;
+ }
+
+ public AttributeType GetStructured(TestHelper.Uris uriId, uint nodeId, string variableName)
+ {
+ AttributeFamilyType objectToTest = GetTestAttribute(uriId, nodeId);
+ AttributeType variableAttribute = GetAttribute(objectToTest.Attribute, variableName);
+ AttributeType structured = GetAttribute(variableAttribute, "StructureFieldDefinition");
+ return structured;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file