Skip to content

Commit 38458c2

Browse files
128 structure field selections (#134)
* Remove AllowSubtypes * Remove MaxStringLength Only there if non zero and string or bytestring or subtypes * Address ValueRank and ArrayDimensions * Description is now array of Localized Text * Remove IsOptional if not required * Finalize Structure Field Issue 128
1 parent 1c89ec2 commit 38458c2

File tree

4 files changed

+168
-105
lines changed

4 files changed

+168
-105
lines changed

NodeSetToAML.cs

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3160,35 +3160,82 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
31603160

31613161

31623162
// Now fill the data
3163-
AddModifyAttribute( structureFieldAttribute.Attribute,
3164-
"Name", "String", new Variant( field.Name ) );
3165-
AddModifyAttribute( structureFieldAttribute.Attribute,
3166-
"ValueRank", "Int32", new Variant( field.ValueRank ) );
3167-
AddModifyAttribute( structureFieldAttribute.Attribute,
3168-
"IsOptional", "Boolean", new Variant( field.IsOptional) );
3169-
3170-
SetArrayDimensions( structureFieldAttribute.Attribute, field.ArrayDimensions );
3171-
RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "StructureFieldDefinition");
3172-
if ( string.IsNullOrEmpty(field.ArrayDimensions))
3163+
RemoveUnwantedAttribute(structureFieldAttribute, "Name");
3164+
3165+
if ( field.ValueRank == ValueRanks.Scalar ||
3166+
field.ValueRank >= ValueRanks.OneDimension )
3167+
{
3168+
AddModifyAttribute(structureFieldAttribute.Attribute,
3169+
"ValueRank", "Int32", new Variant(field.ValueRank));
3170+
}
3171+
else
31733172
{
3174-
RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"], "UInt32");
3173+
RemoveUnwantedAttribute( structureFieldAttribute, "ValueRank" );
31753174
}
31763175

3177-
AddModifyAttribute( structureFieldAttribute.Attribute,
3178-
"AllowSubtypes", "Boolean", new Variant( field.AllowSubTypes ) );
3179-
AddModifyAttribute( structureFieldAttribute.Attribute,
3180-
"MaxStringLength", "UInt32", new Variant( field.MaxStringLength ) );
3176+
if ( field.IsOptional )
3177+
{
3178+
AddModifyAttribute(structureFieldAttribute.Attribute,
3179+
"IsOptional", "Boolean", new Variant(true));
3180+
}
3181+
else
3182+
{
3183+
RemoveUnwantedAttribute(structureFieldAttribute, "IsOptional");
3184+
}
31813185

3182-
if( field.Description != null && field.Description.Length > 0 )
3186+
if ( field.ValueRank >= ValueRanks.OneDimension &&
3187+
!string.IsNullOrEmpty( field.ArrayDimensions ) )
3188+
{
3189+
SetArrayDimensions(structureFieldAttribute.Attribute, field.ArrayDimensions);
3190+
RemoveUnwantedAttribute(structureFieldAttribute.Attribute["ArrayDimensions"],
3191+
"StructureFieldDefinition");
3192+
}
3193+
else
3194+
{
3195+
RemoveUnwantedAttribute(structureFieldAttribute, "ArrayDimensions");
3196+
}
3197+
3198+
3199+
// Max String Length is only for strings and bytestrings
3200+
// This seems to be a point for discussion.
3201+
// Do we put max string length in if it zero?
3202+
if ( field.MaxStringLength > 0 &&
3203+
m_modelManager.IsTypeOf( field.DecodedDataType, Opc.Ua.DataTypeIds.String ) ||
3204+
m_modelManager.IsTypeOf( field.DecodedDataType, Opc.Ua.DataTypeIds.ByteString ) )
31833205
{
3184-
LocalizedText localizedText = new LocalizedText(
3185-
field.Description[0].Locale, field.Description[ 0 ].Value );
31863206
AddModifyAttribute( structureFieldAttribute.Attribute,
3187-
"Description", "LocalizedText", new Variant( localizedText ) );
3207+
"MaxStringLength", "UInt32", new Variant( field.MaxStringLength ) );
3208+
}
3209+
else if ( structureFieldAttribute.Attribute[ "MaxStringLength" ] != null )
3210+
{
3211+
RemoveUnwantedAttribute( structureFieldAttribute, "MaxStringLength" );
3212+
}
3213+
3214+
if (field.Description != null && field.Description.Length > 0)
3215+
{
3216+
List<Variant> localizedTextList = new List<Variant>(field.Description.Length);
3217+
foreach(NodeSet.LocalizedText description in field.Description)
3218+
{
3219+
localizedTextList.Add(
3220+
new Variant(
3221+
new LocalizedText(description.Locale, description.Value)));
3222+
}
3223+
Variant localizedTextArray = new Variant(localizedTextList);
3224+
3225+
LocalizedText localizedText = new LocalizedText(
3226+
field.Description[0].Locale, field.Description[0].Value);
3227+
AddModifyAttribute(structureFieldAttribute.Attribute,
3228+
"Description", "LocalizedText", localizedTextArray,
3229+
bListOf: true);
3230+
RemoveUnwantedAttribute(structureFieldAttribute.Attribute["Description"],
3231+
"StructureFieldDefinition");
3232+
}
3233+
else if (structureFieldAttribute.Attribute["Description"] != null)
3234+
{
3235+
RemoveUnwantedAttribute(structureFieldAttribute, "Description");
31883236
}
31893237

3190-
RemoveUnwantedAttribute(structureFieldAttribute.Attribute["Description"],
3191-
"StructureFieldDefinition");
3238+
31923239

31933240
// Remove the NodeId from the structure Field
31943241
AttributeType nodeIdAttribute = structureFieldAttribute.Attribute[ "DataType" ];
@@ -3200,8 +3247,10 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
32003247
RemoveUnwantedNodeIdAttribute(structureFieldAttribute);
32013248
RemoveNodeIdsFromDefinition(structureFieldAttribute);
32023249

3203-
3204-
fieldDefinitionAttribute.Attribute.Insert( structureFieldAttribute );
3250+
if (structureFieldAttribute.Attribute.Count > 0)
3251+
{
3252+
fieldDefinitionAttribute.Attribute.Insert(structureFieldAttribute);
3253+
}
32053254
}
32063255
}
32073256
}

SystemTest/NodeSetFiles/Modified.Opc.Ua.NodeSet2.xml

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9723,26 +9723,19 @@
97239723
<Definition Name="CartesianCoordinates" />
97249724
</UADataType>
97259725
<UADataType NodeId="i=18810" BrowseName="3DCartesianCoordinates" SymbolicName="ThreeDCartesianCoordinates">
9726-
<DisplayName>3DCartesianCoordinates</DisplayName>
9727-
<Category>Base Info Spatial Data</Category>
9728-
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26</Documentation>
9729-
<References>
9730-
<Reference ReferenceType="HasSubtype" IsForward="false">i=18809</Reference>
9731-
</References>
9732-
<Definition Name="3DCartesianCoordinates" SymbolicName="ThreeDCartesianCoordinates">
9733-
<Field Name="X" DataType="i=11" >
9734-
<Description>No Locale</Description>
9735-
</Field>
9736-
<Field Name="Y" DataType="i=11" >
9737-
<Description Locale="en">With Locale</Description>
9738-
</Field>
9739-
<Field Name="Z" DataType="i=11" >
9740-
<Description>Multiple Descriptions</Description>
9741-
<Description Locale="en">Multiple Descriptions with Locale</Description>
9742-
</Field>
9743-
</Definition>
9744-
</UADataType>
9745-
<UADataType NodeId="i=18811" BrowseName="Orientation" IsAbstract="true">
9726+
<DisplayName>3DCartesianCoordinates</DisplayName>
9727+
<Category>Base Info Spatial Data</Category>
9728+
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26</Documentation>
9729+
<References>
9730+
<Reference ReferenceType="HasSubtype" IsForward="false" BrowseName="CartesianCoordinates">i=18809</Reference>
9731+
</References>
9732+
<Definition Name="3DCartesianCoordinates" SymbolicName="ThreeDCartesianCoordinates">
9733+
<Field Name="X" DataType="i=11" />
9734+
<Field Name="Y" DataType="i=11" />
9735+
<Field Name="Z" DataType="i=11" />
9736+
</Definition>
9737+
</UADataType>
9738+
<UADataType NodeId="i=18811" BrowseName="Orientation" IsAbstract="true">
97469739
<DisplayName>Orientation</DisplayName>
97479740
<Category>Base Info Spatial Data</Category>
97489741
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part5/12.27</Documentation>

SystemTest/NodeSetFiles/TestAml.xml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,14 +335,37 @@
335335
<Definition Name="1:PublisherQosDataType">
336336
<Field Name="QosCategory" DataType="String" IsOptional="true" MaxStringLength="123">
337337
<Description Locale="en">Quality of Service Category</Description>
338+
<Description Locale="fr">Catégorie de qualité de service</Description>
339+
<Description>Kategorie „Dienstqualität“</Description>
338340
</Field>
339341
<Field Name="DatagramQos" DataType="TransmitQosDataType" ValueRank="2" ArrayDimensions="2,3" AllowSubTypes="true">
340342
<Description>Transmit Quality of Service</Description>
341343
</Field>
342-
<Field Name="NoDescription" DataType="String" IsOptional="false" MaxStringLength="321"/>
344+
<Field Name="NoDescription" DataType="String" IsOptional="false" ValueRank="-2" MaxStringLength="321"/>
345+
<Field Name="PracticallyEmpty" DataType="i=12" ValueRank="-2"/>
343346
</Definition>
344347
</UADataType>
345348

349+
<UADataType NodeId="ns=1;i=3007" BrowseName="3DCartesianCoordinates" SymbolicName="ThreeDCartesianCoordinates">
350+
<DisplayName>3DCartesianCoordinates</DisplayName>
351+
<Category>Base Info Spatial Data</Category>
352+
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part5/12.26</Documentation>
353+
<References>
354+
<Reference ReferenceType="HasSubtype" IsForward="false">i=18809</Reference>
355+
</References>
356+
<Definition Name="3DCartesianCoordinates" SymbolicName="ThreeDCartesianCoordinates">
357+
<Field Name="X" DataType="i=11" >
358+
<Description>No Locale</Description>
359+
</Field>
360+
<Field Name="Y" DataType="i=11" >
361+
<Description Locale="en">With Locale</Description>
362+
</Field>
363+
<Field Name="Z" DataType="i=11" >
364+
<Description>Multiple Descriptions</Description>
365+
<Description Locale="en">Multiple Descriptions with Locale</Description>
366+
</Field>
367+
</Definition>
368+
</UADataType>
346369

347370
<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">
348371
<DisplayName>http://opcfoundation.org/UA/FX/AML/TESTING</DisplayName>
@@ -3563,7 +3586,7 @@
35633586
<!-- Next Numbers
35643587
ObjectType 1009
35653588
VariableType 2006
3566-
DataType 3007
3589+
DataType 3008
35673590
Object 5025
35683591
Variable 6238
35693592
-->

SystemTest/TestStructureFieldDefinition.cs

Lines changed: 58 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
using Microsoft.VisualStudio.TestTools.UnitTesting;
2-
using System.Collections.Generic;
32
using Aml.Engine.CAEX;
43
using Aml.Engine.CAEX.Extensions;
5-
using System.Linq;
64
using System;
7-
using Opc.Ua;
85

96
namespace SystemTest
107
{
@@ -26,7 +23,7 @@ public void TestUnwantedAttributes(TestHelper.Uris uriId, uint nodeId)
2623

2724
foreach( AttributeType attribute in objectToTest.Attribute )
2825
{
29-
if ( attribute.Name != "NodeId")
26+
if ( attribute.Name != "NodeId" && attribute.Name != "PracticallyEmpty" )
3027
{
3128
AttributeType structureAttribute = GetAttribute(attribute, "StructureFieldDefinition");
3229
foreach(AttributeType definitionAttribute in structureAttribute.Attribute)
@@ -50,81 +47,82 @@ public void TestUnwantedAttributes(TestHelper.Uris uriId, uint nodeId)
5047
}
5148

5249
[TestMethod, Timeout(TestHelper.UnitTestTimeout)]
53-
[DataRow("QosCategory","Name", "QosCategory")]
54-
[DataRow("QosCategory", "Description", "Quality of Service Category")]
55-
[DataRow("QosCategory", "ValueRank", "-1")]
56-
[DataRow("QosCategory", "ArrayDimensions", null)]
57-
[DataRow("QosCategory", "MaxStringLength", "123")]
58-
[DataRow("QosCategory", "IsOptional", "true")]
59-
[DataRow("QosCategory", "AllowSubtypes", "false")]
60-
61-
[DataRow("DatagramQos", "Name", "DatagramQos")]
62-
[DataRow("DatagramQos", "Description", "Transmit Quality of Service")]
63-
[DataRow("DatagramQos", "ValueRank", "2")]
64-
[DataRow("DatagramQos", "MaxStringLength", "0")]
65-
[DataRow("DatagramQos", "IsOptional", "false")]
66-
[DataRow("DatagramQos", "AllowSubtypes", "true")]
67-
68-
[DataRow("NoDescription", "Name", "NoDescription")]
69-
[DataRow("NoDescription", "Description", null)]
70-
[DataRow("NoDescription", "ValueRank", "-1")]
71-
[DataRow("NoDescription", "ArrayDimensions", null)]
72-
[DataRow("NoDescription", "MaxStringLength", "321")]
73-
[DataRow("NoDescription", "IsOptional", "false")]
74-
[DataRow("NoDescription", "AllowSubtypes", "false")]
50+
[DataRow("QosCategory","Name", null, "", "")]
51+
[DataRow("QosCategory", "Description", "Quality of Service Category", "0", "en")]
52+
[DataRow("QosCategory", "Description", "Catégorie de qualité de service", "1", "fr")]
53+
[DataRow("QosCategory", "Description", "Kategorie „Dienstqualität“", "2", "")]
54+
[DataRow("QosCategory", "ValueRank", "-1", "", "")]
55+
[DataRow("QosCategory", "ArrayDimensions", null, "", "")]
56+
[DataRow("QosCategory", "MaxStringLength", "123", "", "")]
57+
[DataRow("QosCategory", "IsOptional", "true", "", "")]
58+
59+
[DataRow("DatagramQos", "Name", null, "", "")]
60+
[DataRow("DatagramQos", "Description", "Transmit Quality of Service", "0", "")]
61+
[DataRow("DatagramQos", "ArrayDimensions", "2", "0", "")]
62+
[DataRow("DatagramQos", "ArrayDimensions", "3", "1", "")]
63+
[DataRow("DatagramQos", "ValueRank", "2", "", "")]
64+
[DataRow("DatagramQos", "IsOptional", null, "", "")]
65+
66+
[DataRow("NoDescription", "Name", null, "", "")]
67+
[DataRow("NoDescription", "Description", null, "", "")]
68+
[DataRow("NoDescription", "ValueRank", null, "", "")]
69+
[DataRow("NoDescription", "ArrayDimensions", null, "", "")]
70+
[DataRow("NoDescription", "MaxStringLength", "321", "", "")]
71+
[DataRow("NoDescription", "IsOptional", null, "", "")]
7572

7673
public void TestAttributeValues(string variableName,
7774
string attributeName,
78-
string expectedValue)
79-
{
80-
AttributeValues(variableName, attributeName, expectedValue);
81-
}
82-
83-
[TestMethod, Timeout(TestHelper.UnitTestTimeout)]
84-
public void TestDescriptionLocale()
85-
{
86-
AttributeValues("QosCategory", "Description", "Quality of Service Category", "en");
87-
}
88-
89-
[TestMethod, Timeout(TestHelper.UnitTestTimeout)]
90-
public void TestArrayDimensions()
91-
{
92-
AttributeType structured = GetStructured(TestHelper.Uris.Test,
93-
PublisherQosDataType, "DatagramQos");
94-
AttributeType attribute = GetAttribute(structured, "ArrayDimensions");
95-
AttributeType first = GetAttribute(attribute, "0");
96-
Assert.AreEqual("2", first.Value, "Unexpected value for ArrayDimensions[0].");
97-
AttributeType second = GetAttribute(attribute, "1");
98-
Assert.AreEqual("3", second.Value, "Unexpected value for ArrayDimensions[1].");
99-
}
100-
101-
public void AttributeValues(string variableName,
102-
string attributeName,
10375
string expectedValue,
104-
string localeId = "")
76+
string arrayIndex,
77+
string localeId)
10578
{
10679
AttributeFamilyType objectToTest = GetTestAttribute(TestHelper.Uris.Test,
10780
PublisherQosDataType);
10881

10982
AttributeType variableAttribute = GetAttribute(objectToTest.Attribute, variableName);
11083
AttributeType structured = GetAttribute(variableAttribute, "StructureFieldDefinition");
111-
AttributeType attribute = GetAttribute(structured.Attribute, attributeName);
112-
Assert.AreEqual(expectedValue, attribute.Value,
113-
$"Unexpected value for {variableName}.{attributeName} in {structured.Name}.");
11484

115-
if (!string.IsNullOrEmpty(localeId))
85+
if (string.IsNullOrEmpty(expectedValue))
86+
{
87+
// attributeName should not exist
88+
Assert.IsNull(structured.Attribute[attributeName],
89+
$"Attribute {attributeName} exists in {variableName} when it should not.");
90+
}
91+
else
11692
{
117-
AttributeType locale = GetAttribute(attribute.Attribute, localeId);
118-
Assert.AreEqual(expectedValue, locale.Value,
119-
$"Unexpected locale value for {variableName}.{attributeName} in {structured.Name}.");
93+
AttributeType attribute = GetAttribute(structured.Attribute, attributeName);
94+
95+
if (!string.IsNullOrEmpty(arrayIndex))
96+
{
97+
attribute = GetAttribute(attribute, arrayIndex);
98+
}
99+
100+
Assert.AreEqual(expectedValue, attribute.Value,
101+
$"Unexpected value for {variableName}.{attributeName} in {structured.Name}.");
102+
103+
if (!string.IsNullOrEmpty(localeId))
104+
{
105+
AttributeType locale = GetAttribute(attribute.Attribute, localeId);
106+
Assert.AreEqual(expectedValue, locale.Value,
107+
$"Unexpected locale value for {variableName}.{attributeName} in {structured.Name}.");
108+
}
120109
}
121110
}
122111

123112

113+
[TestMethod, Timeout(TestHelper.UnitTestTimeout)]
114+
public void TestUnwantedStructureAttribute()
115+
{
116+
AttributeFamilyType objectToTest = GetTestAttribute(TestHelper.Uris.Test, PublisherQosDataType);
117+
AttributeType emptyAttribute = GetAttribute(objectToTest.Attribute, "PracticallyEmpty");
118+
Assert.IsNull(emptyAttribute.Attribute["StructureFieldDefinition"],
119+
"Unexpected StructureFieldDefinition found in PracticallyEmpty");
120+
}
121+
124122

125123
#endregion
126124

127-
#region Helpers
125+
#region Helpers
128126

129127
private CAEXDocument GetDocument()
130128
{

0 commit comments

Comments
 (0)