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
28 changes: 23 additions & 5 deletions NodeSetToAML.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 ) );
Expand All @@ -3176,13 +3187,20 @@ 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 )
{
structureFieldAttribute.Attribute.RemoveElement( nodeIdAttribute );
}

RemoveUnwantedNodeIdAttribute(structureFieldAttribute);
RemoveNodeIdsFromDefinition(structureFieldAttribute);


fieldDefinitionAttribute.Attribute.Insert( structureFieldAttribute );
}
}
Expand Down
29 changes: 27 additions & 2 deletions SystemTest/NodeSetFiles/TestAml.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<Alias Alias="SessionDiagnosticsDataType">i=865</Alias>
<Alias Alias="SessionSecurityDiagnosticsDataType">i=868</Alias>
<Alias Alias="SubscriptionDiagnosticsDataType">i=874</Alias>
<Alias Alias="TransmitQosDataType">i=23604</Alias>
<Alias Alias="NegotiationStatus">i=24216</Alias>
<Alias Alias="LowLevelStructure">ns=1;i=3003</Alias>

Expand Down Expand Up @@ -308,6 +309,8 @@
</Definition>
</UADataType>



<UAVariableType ValueRank="-2" NodeId="ns=1;i=2003" BrowseName="1:ComplexVariableType">
<DisplayName>ComplexVariableType</DisplayName>
<References>
Expand All @@ -318,8 +321,30 @@
</References>
</UAVariableType>

<UADataType NodeId="ns=1;i=3006" BrowseName="1:PublisherQosDataType">
<DisplayName>PublisherQosDataType</DisplayName>
<Category>UAFX AutomationComponent Base</Category>
<Category>UAFX FunctionalEntity Base</Category>
<Documentation>https://reference.opcfoundation.org/UAFX/Part81/v100/docs/10.36</Documentation>
<References>
<!--<Reference ReferenceType="HasEncoding" BrowseName="Default Binary">ns=1;i=5024</Reference>
<Reference ReferenceType="HasEncoding" BrowseName="Default JSON">ns=1;i=5026</Reference>
<Reference ReferenceType="HasEncoding" BrowseName="Default XML">ns=1;i=5025</Reference>-->
<Reference ReferenceType="HasSubtype" IsForward="false">i=22</Reference>
</References>
<Definition Name="1:PublisherQosDataType">
<Field Name="QosCategory" DataType="String" IsOptional="true" MaxStringLength="123">
<Description Locale="en">Quality of Service Category</Description>
</Field>
<Field Name="DatagramQos" DataType="TransmitQosDataType" ValueRank="2" ArrayDimensions="2,3" AllowSubTypes="true">
<Description>Transmit Quality of Service</Description>
</Field>
<Field Name="NoDescription" DataType="String" IsOptional="false" MaxStringLength="321"/>
</Definition>
</UADataType>


<UAObject SymbolicName="http___opcfoundation_org_UA_FX_AML_TESTING" NodeId="ns=1;i=5000" BrowseName="1:http://opcfoundation.org/UA/FX/AML/TESTING" ParentNodeId="i=11715">
<UAObject SymbolicName="http___opcfoundation_org_UA_FX_AML_TESTING" NodeId="ns=1;i=5000" BrowseName="1:http://opcfoundation.org/UA/FX/AML/TESTING" ParentNodeId="i=11715">
<DisplayName>http://opcfoundation.org/UA/FX/AML/TESTING</DisplayName>
<References>
<Reference ReferenceType="HasComponent" IsForward="false">i=11715</Reference>
Expand Down Expand Up @@ -3538,7 +3563,7 @@
<!-- Next Numbers
ObjectType 1009
VariableType 2006
DataType 3006
DataType 3007
Object 5025
Variable 6238
-->
Expand Down
2 changes: 1 addition & 1 deletion SystemTest/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
Expand Down
175 changes: 175 additions & 0 deletions SystemTest/TestStructureFieldDefinition.cs
Original file line number Diff line number Diff line change
@@ -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
}
}