From 800094989b83e03c79a98d6623d413f3fea4340b Mon Sep 17 00:00:00 2001 From: Archie Date: Tue, 16 Dec 2025 14:45:18 -0700 Subject: [PATCH] Fix for Issue 145 Option Set Values is missing NodeId --- NodeSetToAML.cs | 52 +++++++++++++------ SystemTest/NodeSetFiles/TestAml.xml | 7 ++- ...tionSetEmptyValues.cs => TestOptionSet.cs} | 44 ++++++++++++---- 3 files changed, 75 insertions(+), 28 deletions(-) rename SystemTest/{TestOptionSetEmptyValues.cs => TestOptionSet.cs} (75%) diff --git a/NodeSetToAML.cs b/NodeSetToAML.cs index e9e33c1..7410904 100644 --- a/NodeSetToAML.cs +++ b/NodeSetToAML.cs @@ -3115,36 +3115,37 @@ private void ProcessEnumerations(ref AttributeTypeType att, NodeId nodeId) private void ProcessOptionSets(ref AttributeTypeType att, NodeId nodeId) { - NodeId OptionSetsPropertyId = m_modelManager.FindFirstTarget(nodeId, HasPropertyNodeId, true, "OptionSetValues"); - - if (OptionSetsPropertyId != null && m_modelManager.IsTypeOf(nodeId, NumberNodeId)) + // This should not be done via the Localized text. Should be from Field Definitions. + UADataType dataType = m_modelManager.FindNode(nodeId); + if ( dataType != null ) { - att.AttributeDataType = ""; - var OptionSetsPropertyNode = FindNode(OptionSetsPropertyId); - var OptionSets = OptionSetsPropertyNode as UAVariable; - Opc.Ua.LocalizedText[] OptionSetValues = OptionSets.DecodedValue.Value as Opc.Ua.LocalizedText[]; - foreach (var OptionSetValue in OptionSetValues) + if (dataType != null && + dataType.Definition != null && + dataType.Definition.IsOptionSet == true) { - if( OptionSetValue.Text != null && OptionSetValue.Text.Length > 0 ) + if (dataType.Definition.Field != null) { - AttributeType a = new AttributeType( new System.Xml.Linq.XElement( defaultNS + "Attribute" ) ); - a.Name = OptionSetValue.Text; - a.AttributeDataType = "xs:boolean"; - att.Attribute.Insert( a, false ); + foreach(DataTypeField field in dataType.Definition.Field) + { + if ( !string.IsNullOrEmpty( field.Name )) + { + AttributeType a = new AttributeType(new System.Xml.Linq.XElement(defaultNS + "Attribute")); + a.Name = field.Name; + a.AttributeDataType = "xs:boolean"; + att.Attribute.Insert(a, false); + } + } } } } - if (nodeId == NodeIdNodeId || nodeId == ExpandedNodeIdNodeId) { - // add NodeId - var added2 = new AttributeType(new System.Xml.Linq.XElement(defaultNS + "Attribute")); added2.Name = "ServerInstanceUri"; added2.AttributeDataType = "xs:anyURI"; - att.Attribute.Insert( added2, false); + att.Attribute.Insert(added2, false); added2 = new AttributeType(new System.Xml.Linq.XElement(defaultNS + "Attribute")); added2.Name = "Alias"; @@ -3614,6 +3615,23 @@ private void AddOptionSetFieldDefinition( AttributeFamilyType attribute, UANode } attribute.Attribute.Insert(optionSetFields, false, true); + + NodeId optionSetsPropertyId = m_modelManager.FindFirstTarget( + optionSetNode.DecodedNodeId, HasPropertyNodeId, true, "OptionSetValues"); + + if (optionSetsPropertyId != null && m_modelManager.IsTypeOf(optionSetNode.DecodedNodeId, NumberNodeId)) + { + UANode optionSetsPropertyNode = FindNode(optionSetsPropertyId); + UAVariable optionSets = optionSetsPropertyNode as UAVariable; + + Opc.Ua.LocalizedText[] optionSetValues = optionSets.DecodedValue.Value as Opc.Ua.LocalizedText[]; + AttributeType optionSetValuesAttribute = AddModifyAttribute(attribute.Attribute, "OptionSetValues", + Opc.Ua.DataTypes.LocalizedText, new Variant(optionSetValues), bListOf: true); + optionSetValuesAttribute.AdditionalInformation.Append(OpcUaTypeOnly); + RemoveNodeIdsFromDefinition(optionSetValuesAttribute); + AddModifyAttribute(optionSetValuesAttribute.Attribute, "NodeId", "NodeId", + new Variant(optionSetsPropertyId)); + } } else { diff --git a/SystemTest/NodeSetFiles/TestAml.xml b/SystemTest/NodeSetFiles/TestAml.xml index f5c9346..07e2d6a 100644 --- a/SystemTest/NodeSetFiles/TestAml.xml +++ b/SystemTest/NodeSetFiles/TestAml.xml @@ -468,12 +468,15 @@ - Active + fr + Activer - Unacknowledged + de + Nicht bestätigt + en Unconfirmed diff --git a/SystemTest/TestOptionSetEmptyValues.cs b/SystemTest/TestOptionSet.cs similarity index 75% rename from SystemTest/TestOptionSetEmptyValues.cs rename to SystemTest/TestOptionSet.cs index 68870a3..55f1366 100644 --- a/SystemTest/TestOptionSetEmptyValues.cs +++ b/SystemTest/TestOptionSet.cs @@ -1,16 +1,12 @@ using Aml.Engine.CAEX; using Aml.Engine.CAEX.Extensions; -using Aml.Engine.Services; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Opc.Ua; -using System; using System.Collections.Generic; -using System.Linq; namespace SystemTest { [TestClass] - public class TestOptionSetEmptyValues + public class TestOptionSet { #region Tests @@ -19,7 +15,7 @@ public void TestOperationalHealthOptionSet() { AttributeTypeLibType attributeLibrary = GetFxAcAttributes(); AttributeFamilyType attributeFamilyType = attributeLibrary[ "OperationalHealthOptionSet" ]; - TestOptionSet( attributeFamilyType ); + TestOptionSetCount( attributeFamilyType ); } [TestMethod, Timeout( TestHelper.UnitTestTimeout )] @@ -29,7 +25,7 @@ public void TestAggregatedHealthOptionSet() AttributeFamilyType attributeFamilyType = attributeLibrary[ "AggregatedHealthDataType" ]; Assert.IsNotNull( attributeFamilyType ); AttributeType attributeType = attributeFamilyType.Attribute[ "AggregatedOperationalHealth" ]; - TestOptionSet( attributeType ); + TestOptionSetCount( attributeType ); } [TestMethod, Timeout( TestHelper.UnitTestTimeout )] @@ -42,7 +38,7 @@ public void TestInstance() Assert.IsNotNull( internalElementType ); AttributeType value = internalElementType.Attribute[ "Value" ]; Assert.IsNotNull( value ); - TestOptionSet( value ); + TestOptionSetCount( value ); } [TestMethod, Timeout(TestHelper.UnitTestTimeout)] @@ -75,6 +71,36 @@ public void TestFieldDefinitions(string attributeName, string attributeValue) Assert.IsNull(valueAttribute.Attribute[ "ValidBits" ]); } + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + [DataRow("0", "fr", "Activer")] + [DataRow("1", "de", "Nicht bestätigt")] + [DataRow("2", "en", "Unconfirmed")] + public void TestFieldValues(string index, string localeId, string value) + { + CAEXDocument document = GetDocument("TestAml.xml.amlx"); + string amlId = TestHelper.BuildAmlId("", TestHelper.Uris.Test, "3009"); + 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"); + + + AttributeType values = GetAttribute(theObject.Attribute, "OptionSetValues"); + Assert.IsNotNull(values.AdditionalInformation); + Assert.AreEqual(1, values.AdditionalInformation.Count); + Assert.AreEqual("OpcUa:TypeOnly", values.AdditionalInformation[0]); + + AttributeType nodeIdAttribute = GetAttribute(values, "NodeId"); + AttributeType rootNodeIdAttribute = GetAttribute(nodeIdAttribute, "RootNodeId"); + AttributeType numericId = GetAttribute(rootNodeIdAttribute, "NumericId"); + Assert.AreEqual(numericId.Value, "6239"); + + + AttributeType indexAttribute = GetAttribute(values, index); + AttributeType localeAttribute = GetAttribute(indexAttribute, localeId); + Assert.AreEqual(localeAttribute.Value, value); + } + #endregion @@ -88,7 +114,7 @@ private CAEXDocument GetDocument(string fileName = "AmlFxTest.xml.amlx") return document; } - public void TestOptionSet( AttributeTypeType attributeFamilyType ) + public void TestOptionSetCount( AttributeTypeType attributeFamilyType ) { Assert.IsNotNull( attributeFamilyType ); Assert.IsTrue( attributeFamilyType.Attribute.Count >= 4 );