Skip to content

Commit f4d8344

Browse files
Option sets and enumerations (#136)
* Minimize Enumerations complete * Save Work In Progress * Add Field Definition for OptionSets * Add Test for TypeOnly
1 parent 2c910d4 commit f4d8344

4 files changed

Lines changed: 310 additions & 58 deletions

File tree

NodeSetToAML.cs

Lines changed: 125 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3113,6 +3113,8 @@ private void AddAttributeData( AttributeFamilyType attribute, UANode uaNode )
31133113

31143114
AddEnumerationFieldDefinition( attribute, uaNode );
31153115

3116+
AddOptionSetFieldDefintion( attribute, uaNode );
3117+
31163118
AttributeType nodeIdAttribute = AddModifyAttribute( attribute.Attribute,"NodeId", "NodeId",
31173119
new Variant( uaNode.DecodedNodeId ) );
31183120

@@ -3211,32 +3213,16 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode
32113213
RemoveUnwantedAttribute( structureFieldAttribute, "MaxStringLength" );
32123214
}
32133215

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);
3216+
AttributeType descriptionCreated = AddLocalizedTextArrayAttribute(
3217+
structureFieldAttribute,
3218+
"Description",
3219+
field.Description);
32243220

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)
3221+
if (descriptionCreated != null)
32343222
{
3235-
RemoveUnwantedAttribute(structureFieldAttribute, "Description");
3223+
RemoveUnwantedAttribute(descriptionCreated,"StructureFieldDefinition");
32363224
}
32373225

3238-
3239-
32403226
// Remove the NodeId from the structure Field
32413227
AttributeType nodeIdAttribute = structureFieldAttribute.Attribute[ "DataType" ];
32423228
if( nodeIdAttribute != null )
@@ -3297,8 +3283,27 @@ private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode
32973283
enumValues.DecodedValue, bListOf: true);
32983284
if (added != null)
32993285
{
3286+
// Remove empty values
33003287
RemoveNodeIdsFromDefinition(added);
33013288

3289+
foreach( AttributeType arrayElement in added.Attribute )
3290+
{
3291+
List<string> toBeRemoved = new List<string>();
3292+
3293+
foreach ( AttributeType parameter in arrayElement.Attribute )
3294+
{
3295+
if ( string.IsNullOrEmpty( parameter.Value ) )
3296+
{
3297+
toBeRemoved.Add(parameter.Name);
3298+
}
3299+
}
3300+
3301+
foreach (string removeAttribute in toBeRemoved)
3302+
{
3303+
RemoveUnwantedAttribute(arrayElement, removeAttribute);
3304+
}
3305+
}
3306+
33023307
AttributeType nodeIdAttribute = AddModifyAttribute(added.Attribute,
33033308
"NodeId", "NodeId", new Variant(EnumValuesPropertyId));
33043309

@@ -3327,30 +3332,14 @@ private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode
33273332
fieldAttribute.RecreateAttributeInstance(enumFieldSource);
33283333
fieldAttribute.Name = fieldDefinition.Name;
33293334

3330-
AddModifyAttribute(fieldAttribute.Attribute,
3331-
"Name", "String", new Variant(fieldDefinition.Name));
3335+
// Remove the Name
3336+
RemoveUnwantedAttribute(fieldAttribute, "Name");
33323337

3333-
LocalizedText descriptionLocalizedText = new LocalizedText("");
3338+
// Description is an array of LocalizedText
33343339

3335-
if (fieldDefinition.Description != null && fieldDefinition.Description.Length > 0)
3336-
{
3337-
descriptionLocalizedText = new LocalizedText(
3338-
fieldDefinition.Description[0].Locale, fieldDefinition.Description[0].Value);
3339-
}
3340+
AddLocalizedTextArrayAttribute(fieldAttribute, "Description", fieldDefinition.Description);
33403341

3341-
AddModifyAttribute(fieldAttribute.Attribute, "Description", "LocalizedText",
3342-
new Variant(descriptionLocalizedText));
3343-
3344-
LocalizedText displayNameLocalizedText = new LocalizedText("");
3345-
3346-
if (fieldDefinition.DisplayName != null && fieldDefinition.DisplayName.Length > 0)
3347-
{
3348-
displayNameLocalizedText = new LocalizedText(
3349-
fieldDefinition.DisplayName[0].Locale, fieldDefinition.DisplayName[0].Value);
3350-
}
3351-
3352-
AddModifyAttribute(fieldAttribute.Attribute, "DisplayName", "LocalizedText",
3353-
new Variant(displayNameLocalizedText));
3342+
AddLocalizedTextArrayAttribute( fieldAttribute, "DisplayName", fieldDefinition.DisplayName);
33543343

33553344
AddModifyAttribute(fieldAttribute.Attribute,
33563345
"Value", "Int32", new Variant(fieldDefinition.Value));
@@ -3366,6 +3355,56 @@ private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode
33663355
}
33673356
}
33683357

3358+
private void AddOptionSetFieldDefintion( AttributeFamilyType attribute, UANode uaNode )
3359+
{
3360+
UADataType optionSetNode = uaNode as UADataType;
3361+
if (optionSetNode != null &&
3362+
optionSetNode.Definition != null &&
3363+
optionSetNode.Definition.IsOptionSet == true)
3364+
{
3365+
if ( optionSetNode.Definition.Field != null &&
3366+
optionSetNode.Definition.Field.Length > 0 )
3367+
{
3368+
string path = BuildLibraryReference(ATLPrefix, Opc.Ua.Namespaces.OpcUa, "ListOfOptionSet");
3369+
AttributeFamilyType optionSetFieldDefinition = m_cAEXDocument.FindByPath(path) as AttributeFamilyType;
3370+
3371+
AttributeType optionSetFields = new AttributeType(
3372+
new System.Xml.Linq.XElement(defaultNS + "Attribute"));
3373+
3374+
optionSetFields.RecreateAttributeInstance(optionSetFieldDefinition as AttributeFamilyType);
3375+
optionSetFields.Name = "OptionSetFieldDefinition";
3376+
optionSetFields.AdditionalInformation.Append(OpcUaTypeOnly);
3377+
3378+
string optionSetPath = BuildLibraryReference(ATLPrefix, Opc.Ua.Namespaces.OpcUa, "OptionSet");
3379+
AttributeFamilyType optionSetSource = m_cAEXDocument.FindByPath(optionSetPath) as AttributeFamilyType;
3380+
3381+
foreach (DataTypeField fieldDefinition in optionSetNode.Definition.Field)
3382+
{
3383+
AttributeType fieldAttribute = new AttributeType(new System.Xml.Linq.XElement(defaultNS + "Attribute"));
3384+
3385+
fieldAttribute.RecreateAttributeInstance(optionSetSource);
3386+
fieldAttribute.Name = fieldDefinition.Name;
3387+
3388+
AttributeType valueAttribute = AddModifyAttribute(fieldAttribute.Attribute,
3389+
"Value", "Int32", new Variant(fieldDefinition.Value));
3390+
3391+
RemoveUnwantedAttribute(valueAttribute, "NodeId");
3392+
RemoveUnwantedAttribute(fieldAttribute, "ValidBits");
3393+
RemoveUnwantedAttribute(fieldAttribute, "NodeId");
3394+
3395+
optionSetFields.Attribute.Insert(fieldAttribute, false, true);
3396+
}
3397+
3398+
attribute.Attribute.Insert(optionSetFields, false, true);
3399+
}
3400+
else
3401+
{
3402+
bool unexpected = true;
3403+
}
3404+
}
3405+
}
3406+
3407+
33693408
private AttributeFamilyType ProcessDataType(NodeSet.UANode node)
33703409
{
33713410
var typeNode = node as MarkdownProcessor.NodeSet.UADataType;
@@ -3400,6 +3439,49 @@ private AttributeFamilyType ProcessDataType(NodeSet.UANode node)
34003439

34013440
return added;
34023441
}
3442+
3443+
private AttributeType AddLocalizedTextArrayAttribute( AttributeType attribute,
3444+
string name, NodeSet.LocalizedText[] texts)
3445+
{
3446+
AttributeType created = null;
3447+
3448+
// Currently for Field Definitions
3449+
Variant localizedTextArray = LocalizedTextArrayAsVariant(texts);
3450+
if (localizedTextArray.TypeInfo != null &&
3451+
localizedTextArray.TypeInfo.BuiltInType != null &&
3452+
localizedTextArray.TypeInfo.BuiltInType == BuiltInType.Variant)
3453+
{
3454+
created = AddModifyAttribute(attribute.Attribute, name, "LocalizedText",
3455+
localizedTextArray, bListOf: true);
3456+
}
3457+
else
3458+
{
3459+
RemoveUnwantedAttribute(attribute, name);
3460+
}
3461+
3462+
return created;
3463+
}
3464+
3465+
private Variant LocalizedTextArrayAsVariant(NodeSet.LocalizedText[] array )
3466+
{
3467+
Variant localizedTextArray = new Variant();
3468+
3469+
if (array != null && array.Length >= 0)
3470+
{
3471+
List<Variant> localizedTextList = new List<Variant>(array.Length);
3472+
foreach (NodeSet.LocalizedText text in array)
3473+
{
3474+
localizedTextList.Add(
3475+
new Variant(
3476+
new LocalizedText(text.Locale, text.Value)));
3477+
}
3478+
3479+
localizedTextArray = new Variant(localizedTextList);
3480+
}
3481+
3482+
return localizedTextArray;
3483+
}
3484+
34033485
#endregion
34043486

34053487
#region INSTANCE

SystemTest/NodeSetFiles/TestAml.xml

Lines changed: 116 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,120 @@
367367
</Definition>
368368
</UADataType>
369369

370+
<UADataType NodeId="ns=1;i=3008" BrowseName="Test_Duplex">
371+
<DisplayName>Duplex</DisplayName>
372+
<Category>BNM Ethernet Base Info</Category>
373+
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part22/5.3.1/#5.3.1.1</Documentation>
374+
<References>
375+
<Reference ReferenceType="HasProperty" BrowseName="EnumValues">ns=1;i=6238</Reference>
376+
<Reference ReferenceType="HasSubtype" IsForward="false" BrowseName="Enumeration">i=29</Reference>
377+
</References>
378+
<Definition Name="Duplex">
379+
<Field Name="Full" Value="0">
380+
<Description Locale="en">Full duplex.</Description>
381+
</Field>
382+
<Field Name="Half" Value="1">
383+
<Description>Half duplex.</Description>
384+
</Field>
385+
<Field Name="Unknown" Value="2">
386+
</Field>
387+
</Definition>
388+
</UADataType>
389+
<UAVariable NodeId="ns=1;i=6238" BrowseName="EnumValues" ParentNodeId="i=24210" DataType="i=7594" ValueRank="1" ArrayDimensions="3">
390+
<DisplayName>EnumValues</DisplayName>
391+
<References>
392+
<Reference ReferenceType="HasTypeDefinition" BrowseName="PropertyType">i=68</Reference>
393+
<Reference ReferenceType="HasProperty" IsForward="false" BrowseName="Duplex">i=24210</Reference>
394+
</References>
395+
<Value>
396+
<ListOfExtensionObject xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
397+
<ExtensionObject>
398+
<TypeId>
399+
<Identifier>i=7616</Identifier>
400+
</TypeId>
401+
<Body>
402+
<EnumValueType>
403+
<Value>0</Value>
404+
<DisplayName>
405+
<Locale>de</Locale>
406+
<Text>Voll</Text>
407+
</DisplayName>
408+
<Description>
409+
<Locale>de</Locale>
410+
<Text>Vollduplex</Text>
411+
</Description>
412+
</EnumValueType>
413+
</Body>
414+
</ExtensionObject>
415+
<ExtensionObject>
416+
<TypeId>
417+
<Identifier>i=7616</Identifier>
418+
</TypeId>
419+
<Body>
420+
<EnumValueType>
421+
<Value>1</Value>
422+
<DisplayName>
423+
<Text>Half</Text>
424+
</DisplayName>
425+
<Description>
426+
<Text>Half duplex.</Text>
427+
</Description>
428+
</EnumValueType>
429+
</Body>
430+
</ExtensionObject>
431+
<ExtensionObject>
432+
<TypeId>
433+
<Identifier>i=7616</Identifier>
434+
</TypeId>
435+
<Body>
436+
<EnumValueType>
437+
<Value>2</Value>
438+
<!-- It's not valid that an enumvaluetype doesn't have a name, this is just for testing purposes-->
439+
</EnumValueType>
440+
</Body>
441+
</ExtensionObject>
442+
</ListOfExtensionObject>
443+
</Value>
444+
</UAVariable>
445+
446+
447+
448+
<UADataType NodeId="ns=1;i=3009" BrowseName="TestAlarmMask">
449+
<DisplayName>AlarmMask</DisplayName>
450+
<Category>A &amp; C Summary</Category>
451+
<Documentation>https://reference.opcfoundation.org/v105/Core/docs/Part9/8.3</Documentation>
452+
<References>
453+
<Reference ReferenceType="HasProperty" BrowseName="OptionSetValues">ns=1;i=6239</Reference>
454+
<Reference ReferenceType="HasSubtype" IsForward="false" BrowseName="UInt16">i=5</Reference>
455+
</References>
456+
<Definition Name="AlarmMask" IsOptionSet="true">
457+
<Field Name="Active" Value="0" />
458+
<Field Name="Unacknowledged" Value="1" />
459+
<Field Name="Unconfirmed" Value="2" />
460+
</Definition>
461+
</UADataType>
462+
<UAVariable NodeId="ns=1;i=6239" BrowseName="OptionSetValues" ParentNodeId="i=32251" DataType="LocalizedText" ValueRank="1" ArrayDimensions="3">
463+
<DisplayName>OptionSetValues</DisplayName>
464+
<References>
465+
<Reference ReferenceType="HasTypeDefinition" BrowseName="PropertyType">i=68</Reference>
466+
<Reference ReferenceType="HasProperty" IsForward="false" BrowseName="AlarmMask">i=32251</Reference>
467+
</References>
468+
<Value>
469+
<ListOfLocalizedText xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
470+
<LocalizedText>
471+
<Text>Active</Text>
472+
</LocalizedText>
473+
<LocalizedText>
474+
<Text>Unacknowledged</Text>
475+
</LocalizedText>
476+
<LocalizedText>
477+
<Text>Unconfirmed</Text>
478+
</LocalizedText>
479+
</ListOfLocalizedText>
480+
</Value>
481+
</UAVariable>
482+
483+
370484
<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">
371485
<DisplayName>http://opcfoundation.org/UA/FX/AML/TESTING</DisplayName>
372486
<References>
@@ -3586,8 +3700,8 @@
35863700
<!-- Next Numbers
35873701
ObjectType 1009
35883702
VariableType 2006
3589-
DataType 3008
3703+
DataType 3010
35903704
Object 5025
3591-
Variable 6238
3705+
Variable 6240
35923706
-->
35933707
</UANodeSet>

SystemTest/TestEnums.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,6 @@ public void TestFieldDefinition(uint nodeId, object enumObject)
178178

179179
foreach (AttributeType fieldDefinition in enumFieldDefinition.Attribute)
180180
{
181-
AttributeType nameAttribute = fieldDefinition.Attribute["Name"];
182-
Assert.IsNotNull(nameAttribute, "Field Definition Name is null");
183-
Assert.IsNotNull(nameAttribute.Value, "Field Definition Name Value is null");
184-
Assert.AreEqual(fieldDefinition.Name, nameAttribute.Value, "Field Definition Name does not match Attribute Name");
185181
AttributeType valueAttribute = fieldDefinition.Attribute["Value"];
186182
Assert.IsNotNull(valueAttribute, "Field Definition Value is null");
187183
Assert.IsNotNull(valueAttribute.Value, "Field Definition Value-Value is null");
@@ -457,6 +453,24 @@ public void TestUnwantedAttribute(AttributeType attribute, string unwantedAttrib
457453
}
458454
}
459455

456+
[TestMethod, Timeout(TestHelper.UnitTestTimeout)]
457+
public void TestForRemovedAttributes()
458+
{
459+
AttributeFamilyType objectToTest = GetTestAttribute("3008", foundation: false);
460+
461+
AttributeType enumFieldDefinition = GetAttribute(objectToTest, "EnumFieldDefinition");
462+
AttributeType unknownElement = GetAttribute(enumFieldDefinition, "Unknown");
463+
Assert.IsNull(unknownElement.Attribute["DisplayName"]);
464+
Assert.IsNull(unknownElement.Attribute["Description"]);
465+
466+
AttributeType enumValues = GetAttribute(objectToTest, "EnumValues");
467+
AttributeType lastArrayElement = GetAttribute(enumValues, "2");
468+
Assert.IsNull(lastArrayElement.Attribute["DisplayName"]);
469+
Assert.IsNull(lastArrayElement.Attribute["Description"]);
470+
}
471+
472+
473+
460474
#endregion
461475

462476
#region Helpers

0 commit comments

Comments
 (0)