diff --git a/NodeSetToAML.cs b/NodeSetToAML.cs index a41a839..6a3da50 100644 --- a/NodeSetToAML.cs +++ b/NodeSetToAML.cs @@ -53,7 +53,6 @@ using System.Reflection; using System.Xml; using Opc2Aml; -using Newtonsoft.Json.Linq; namespace MarkdownProcessor @@ -107,6 +106,7 @@ public class NodeSetToAML private readonly NodeId IntegerNodeId = Opc.Ua.DataTypeIds.Integer; private readonly System.Xml.Linq.XNamespace defaultNS = "http://www.dke.de/CAEX"; + private const string OpcUaTypeOnly = "OpcUa:TypeOnly"; private const string uaNamespaceURI = "http://opcfoundation.org/UA/"; private const string OpcLibInfoNamespace = "http://opcfoundation.org/UA/FX/2021/08/OpcUaLibInfo.xsd"; private UANode structureNode; @@ -1678,7 +1678,7 @@ private AttributeType OverrideBooleanAttribute(AttributeSequence seq, var at = AddModifyAttribute(seq, AttributeName, "Boolean", value); if (at != null && typeOnly) { - at.AdditionalInformation.Append("OpcUa:TypeOnly"); + at.AdditionalInformation.Append(OpcUaTypeOnly); } return at; } @@ -2707,7 +2707,7 @@ private void OverrideAttribute(IClassWithBaseClassReference owner, a.AttributeDataType = AttType; a.AttributeValue = value; - a.AdditionalInformation.Append("OpcUa:TypeOnly"); + a.AdditionalInformation.Append(OpcUaTypeOnly); } private void ProcessReferenceType(ref InterfaceClassLibType icl, NodeId nodeId) @@ -2772,7 +2772,7 @@ private void ProcessReferenceType(ref InterfaceClassLibType icl, NodeId nodeId) AttributeType inverseNameAttribute = AddModifyAttribute(added.Attribute, "InverseName", "LocalizedText", refnode.InverseName[0].Value); RemoveUnwantedNodeIdAttribute(inverseNameAttribute); - inverseNameAttribute.AdditionalInformation.Append("OpcUa:TypeOnly"); + inverseNameAttribute.AdditionalInformation.Append(OpcUaTypeOnly); } // Need typeonly here @@ -2798,7 +2798,7 @@ private void ProcessReferenceType(ref InterfaceClassLibType icl, NodeId nodeId) AttributeType inverseNameAttribute = AddModifyAttribute( inverseAdded.Attribute, "InverseName", "LocalizedText", refnode.DecodedBrowseName.Name); RemoveUnwantedNodeIdAttribute(inverseNameAttribute); - inverseNameAttribute.AdditionalInformation.Append("OpcUa:TypeOnly"); + inverseNameAttribute.AdditionalInformation.Append(OpcUaTypeOnly); OverrideAttribute(inverseAdded, IsSource, "xs:boolean", false); OverrideAttribute(inverseAdded, RefClassConnectsToPath, "xs:string", added.CAEXPath()); @@ -2806,7 +2806,7 @@ private void ProcessReferenceType(ref InterfaceClassLibType icl, NodeId nodeId) } AttributeType nodeIdAttribute = AddModifyAttribute(added.Attribute, "NodeId", "NodeId", new Variant( nodeId ) ); - nodeIdAttribute.AdditionalInformation.Append( "OpcUa:TypeOnly" ); + nodeIdAttribute.AdditionalInformation.Append( OpcUaTypeOnly ); MinimizeNodeId( nodeIdAttribute ); } @@ -2822,6 +2822,21 @@ private void RemoveUnwantedNodeIdAttribute( AttributeType attribute ) } } + private void RemoveNodeIdsFromDefinition(AttributeType attribute) + { + if (attribute != null) + { + foreach (AttributeType indexAttribute in attribute.Attribute) + { + RemoveUnwantedNodeIdAttribute(indexAttribute); + foreach (AttributeType fieldAttribute in indexAttribute.Attribute) + { + RemoveUnwantedNodeIdAttribute(fieldAttribute); + } + } + } + } + #endregion @@ -2847,48 +2862,32 @@ end Mantis #7383 */ private void ProcessEnumerations(ref AttributeTypeType att, NodeId nodeId) { - NodeId EnumStringsPropertyId = m_modelManager.FindFirstTarget(nodeId, HasPropertyNodeId, true, "EnumStrings"); - if (EnumStringsPropertyId != null) + NodeId enumTypeNodeId = m_modelManager.FindFirstTarget(nodeId, HasPropertyNodeId, true, "EnumStrings"); + if (enumTypeNodeId == null) { - UADataType enumNode = FindNode( nodeId ); + enumTypeNodeId = m_modelManager.FindFirstTarget(nodeId, HasPropertyNodeId, true, "EnumValues"); + } + if (enumTypeNodeId != null) + { att.AttributeDataType = "xs:string"; - UAVariable EnumStrings = FindNode(EnumStringsPropertyId); - AttributeValueRequirementType stringValueRequirement = new AttributeValueRequirementType( new System.Xml.Linq.XElement( defaultNS + "Constraint" ) ); UADataType MyNode = FindNode(nodeId); - stringValueRequirement.Name = MyNode.DecodedBrowseName.Name + " Constraint"; - NominalScaledTypeType stringValueNominalType = stringValueRequirement.New_NominalType(); - Opc.Ua.LocalizedText[] EnumValues = EnumStrings.DecodedValue.Value as Opc.Ua.LocalizedText[]; - foreach ( Opc.Ua.LocalizedText EnumValue in EnumValues ) + if (MyNode.Definition != null && MyNode.Definition.Field != null && MyNode.Definition.Field.Length > 0) { - stringValueNominalType.RequiredValue.Append( EnumValue.Text ); - } + AttributeValueRequirementType stringValueRequirement = new AttributeValueRequirementType(new System.Xml.Linq.XElement(defaultNS + "Constraint")); - att.Constraint.Insert( stringValueRequirement ); - return; - } + stringValueRequirement.Name = MyNode.DecodedBrowseName.Name + " Constraint"; + NominalScaledTypeType stringValueNominalType = stringValueRequirement.New_NominalType(); - NodeId EnumValuesPropertyId = m_modelManager.FindFirstTarget(nodeId, HasPropertyNodeId, true, "EnumValues"); - if (EnumValuesPropertyId != null) - { - att.AttributeDataType = "xs:string"; - UAVariable EnumValues = FindNode(EnumValuesPropertyId); - AttributeValueRequirementType stringValueRequirement = new AttributeValueRequirementType( new System.Xml.Linq.XElement( defaultNS + "Constraint" ) ); - UADataType MyNode = FindNode(nodeId); - - stringValueRequirement.Name = MyNode.DecodedBrowseName.Name + " Constraint"; - NominalScaledTypeType stringValueNominalType = stringValueRequirement.New_NominalType(); + foreach (DataTypeField field in MyNode.Definition.Field) + { + stringValueNominalType.RequiredValue.Append(field.Name); + } - ExtensionObject[] EnumVals = EnumValues.DecodedValue.Value as ExtensionObject[]; - foreach ( ExtensionObject EnumValue in EnumVals ) - { - EnumValueType ev = EnumValue.Body as EnumValueType; - stringValueNominalType.RequiredValue.Append( ev.DisplayName.Text ); + att.Constraint.Insert(stringValueRequirement); } - - att.Constraint.Insert( stringValueRequirement ); } } @@ -3105,7 +3104,7 @@ private void AddAttributeData( AttributeFamilyType attribute, UANode uaNode ) MinimizeNodeId( nodeIdAttribute ); - nodeIdAttribute.AdditionalInformation.Append( "OpcUa:TypeOnly" ); + nodeIdAttribute.AdditionalInformation.Append( OpcUaTypeOnly ); } @@ -3143,7 +3142,7 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode structureFieldAttribute.RecreateAttributeInstance( structureFieldDefinition as AttributeFamilyType ); structureFieldAttribute.Name = "StructureFieldDefinition"; - structureFieldAttribute.AdditionalInformation.Append( "OpcUa:TypeOnly" ); + structureFieldAttribute.AdditionalInformation.Append( OpcUaTypeOnly ); // Now fill the data @@ -3188,15 +3187,51 @@ private void AddStructureFieldDefinition( AttributeFamilyType attribute, UANode private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode uaNode) { UADataType enumNode = uaNode as UADataType; - if (enumNode != null && - enumNode.Definition != null && + if (enumNode != null && + enumNode.Definition != null && enumNode.Definition.Field != null && - enumNode.Definition.Field.Length > 0 ) + enumNode.Definition.Field.Length > 0) { string enumPath = BuildLibraryReference(ATLPrefix, Opc.Ua.Namespaces.OpcUa, Enumeration); if (attribute.RefBaseClassPath.Equals(enumPath)) { + NodeId enumStringsNodeId = m_modelManager.FindFirstTarget(uaNode.DecodedNodeId, + HasPropertyNodeId, true, "EnumStrings"); + if (enumStringsNodeId != null) + { + UAVariable enumStrings = FindNode(enumStringsNodeId); + AttributeType added = AddModifyAttribute(attribute.Attribute, "EnumStrings", "LocalizedText", + enumStrings.DecodedValue, bListOf: true); + if (added != null) + { + added.AdditionalInformation.Append(OpcUaTypeOnly); + RemoveNodeIdsFromDefinition(added); + + AttributeType nodeIdAttribute = AddModifyAttribute(added.Attribute, + "NodeId", "NodeId", new Variant(enumStringsNodeId)); + } + } + else + { + NodeId EnumValuesPropertyId = m_modelManager.FindFirstTarget(uaNode.DecodedNodeId, HasPropertyNodeId, true, "EnumValues"); + if (EnumValuesPropertyId != null) + { + UAVariable enumValues = FindNode(EnumValuesPropertyId); + AttributeType added = AddModifyAttribute(attribute.Attribute, "EnumValues", "EnumValueType", + enumValues.DecodedValue, bListOf: true); + if (added != null) + { + RemoveNodeIdsFromDefinition(added); + + AttributeType nodeIdAttribute = AddModifyAttribute(added.Attribute, + "NodeId", "NodeId", new Variant(EnumValuesPropertyId)); + + added.AdditionalInformation.Append(OpcUaTypeOnly); + } + } + } + string path = BuildLibraryReference(ATLPrefix, Opc.Ua.Namespaces.OpcUa, "ListOfEnumField"); AttributeFamilyType enumFieldDefinition = m_cAEXDocument.FindByPath(path) as AttributeFamilyType; @@ -3205,14 +3240,14 @@ private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode enumFields.RecreateAttributeInstance(enumFieldDefinition as AttributeFamilyType); enumFields.Name = "EnumFieldDefinition"; - enumFields.AdditionalInformation.Append("OpcUa:TypeOnly"); + enumFields.AdditionalInformation.Append(OpcUaTypeOnly); string enumFieldPath = BuildLibraryReference(ATLPrefix, Opc.Ua.Namespaces.OpcUa, "EnumField"); AttributeFamilyType enumFieldSource = m_cAEXDocument.FindByPath(enumFieldPath) as AttributeFamilyType; foreach (DataTypeField fieldDefinition in enumNode.Definition.Field) { - AttributeType fieldAttribute = new AttributeType( new System.Xml.Linq.XElement(defaultNS + "Attribute")); + AttributeType fieldAttribute = new AttributeType(new System.Xml.Linq.XElement(defaultNS + "Attribute")); fieldAttribute.RecreateAttributeInstance(enumFieldSource); fieldAttribute.Name = fieldDefinition.Name; @@ -3220,27 +3255,37 @@ private void AddEnumerationFieldDefinition(AttributeFamilyType attribute, UANode AddModifyAttribute(fieldAttribute.Attribute, "Name", "String", new Variant(fieldDefinition.Name)); + LocalizedText descriptionLocalizedText = new LocalizedText(""); + if (fieldDefinition.Description != null && fieldDefinition.Description.Length > 0) { - LocalizedText localizedText = new LocalizedText( + descriptionLocalizedText = new LocalizedText( fieldDefinition.Description[0].Locale, fieldDefinition.Description[0].Value); - AddModifyAttribute(fieldAttribute.Attribute, - "Description", "LocalizedText", new Variant(localizedText)); } + AddModifyAttribute(fieldAttribute.Attribute, "Description", "LocalizedText", + new Variant(descriptionLocalizedText)); + + LocalizedText displayNameLocalizedText = new LocalizedText(""); + if (fieldDefinition.DisplayName != null && fieldDefinition.DisplayName.Length > 0) { - LocalizedText localizedText = new LocalizedText( + displayNameLocalizedText = new LocalizedText( fieldDefinition.DisplayName[0].Locale, fieldDefinition.DisplayName[0].Value); - AddModifyAttribute(fieldAttribute.Attribute, - "DisplayName", "LocalizedText", new Variant(localizedText)); } + AddModifyAttribute(fieldAttribute.Attribute, "DisplayName", "LocalizedText", + new Variant(displayNameLocalizedText)); + AddModifyAttribute(fieldAttribute.Attribute, "Value", "Int32", new Variant(fieldDefinition.Value)); + RemoveNodeIdsFromDefinition(fieldAttribute); + enumFields.Attribute.Insert(fieldAttribute, false, true); } + RemoveNodeIdsFromDefinition(enumFields); + attribute.Attribute.Insert(enumFields, false, true); } } @@ -3963,7 +4008,7 @@ private void RemoveTypeOnlyAttributes( AttributeSequence attributes, string path { string isTypeOnly = additionalInformation as string; if ( !string.IsNullOrEmpty( isTypeOnly ) && - isTypeOnly == "OpcUa:TypeOnly" ) + isTypeOnly == OpcUaTypeOnly ) { attributesToRemove.Add( attribute ); break; @@ -3975,7 +4020,6 @@ private void RemoveTypeOnlyAttributes( AttributeSequence attributes, string path foreach( AttributeType attribute in attributesToRemove ) { - Utils.LogDebug( "{0} Removing TypeOnly Attribute {1}", path, attribute.Name ); attributes.RemoveElement( attribute ); } @@ -3986,7 +4030,6 @@ private void RemoveTypeOnlyAttributes( AttributeSequence attributes, string path } } - #endregion } } \ No newline at end of file diff --git a/SystemTest/NodeSetFiles/TestAml.xml b/SystemTest/NodeSetFiles/TestAml.xml index 6495e39..eba26fe 100644 --- a/SystemTest/NodeSetFiles/TestAml.xml +++ b/SystemTest/NodeSetFiles/TestAml.xml @@ -45,7 +45,259 @@ - + + Modified NodeClass + Base Info Base Types + https://reference.opcfoundation.org/v105/Core/docs/Part5/12.2.5/#12.2.5.2 + + ns=1;i=2004 + i=29 + + + + No value is specified. + + + The Node is an Object. + + + The Node is a Variable. + + + The Node is a Method. + + + The Node is an ObjectType. + + + The Node is a VariableType. + + + The Node is a ReferenceType. + + + The Node is a DataType. + + + The Node is a View. + + + + + EnumValues + + i=68 + ns=1;i=3004 + + + + + + i=7616 + + + + 0 + + de + Nicht spezifiziert + + + de + Es ist kein Wert angegeben. + + + + + + + i=7616 + + + + 1 + + fr + Objet + + + fr + Le nœud est un objet. + + + + + + + i=7616 + + + + 2 + + en + Variable + + + en + The Node is a Variable. + + + + + + + i=7616 + + + + 4 + + de + Verfahren + + + de + Der Knoten ist eine Methode. + + + + + + + i=7616 + + + + 8 + + fr + Type d'objet + + + fr + Le nœud est un type d'objet. + + + + + + + i=7616 + + + + 16 + + en + VariableType + + + en + The Node is a VariableType. + + + + + + + i=7616 + + + + 32 + + de + Referenztyp + + + de + Der Knoten ist ein Referenztyp. + + + + + + + i=7616 + + + + 64 + + DataType + + + The Node is a DataType. + + + + + + + i=7616 + + + + 128 + + View + + + The Node is a View. + + + + + + + + + + Modified MessageSecurityMode + Base Info ServerType + https://reference.opcfoundation.org/v105/Core/docs/Part5/12.3.10 + + ns=1;i=2005 + i=29 + + + + + + + + + + EnumStrings + + i=68 + ns=1;i=3005 + + + + + de + Ungültig + + + fr + Aucune + + + Sign + + + en + Sign And Encrypt + + + + + + + + LowLevelStructure i=22 @@ -3250,10 +3502,13 @@ + + + diff --git a/SystemTest/TestEnums.cs b/SystemTest/TestEnums.cs index 4640cb6..f0d0463 100644 --- a/SystemTest/TestEnums.cs +++ b/SystemTest/TestEnums.cs @@ -1,18 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using Aml.Engine.AmlObjects; using Aml.Engine.CAEX; using Aml.Engine.CAEX.Extensions; -using Aml.Engine.Services; using System.Linq; -using Microsoft.VisualBasic.FileIO; -using System.Reflection.Metadata; -using Aml.Engine.Adapter; -using System.ComponentModel; -using Newtonsoft.Json.Linq; using System; using Opc.Ua; @@ -132,24 +122,23 @@ public void MultiStateClasses(string nodeId, string[] values, bool hasValues) [TestMethod, Timeout( TestHelper.UnitTestTimeout )] - [DataRow( Opc.Ua.DataTypes.NodeClass, new NodeClass(), DisplayName = "EnumValue Constraints - NodeClass" )] - [DataRow( Opc.Ua.DataTypes.MessageSecurityMode, new MessageSecurityMode(), DisplayName = "EnumString Constraints - MessageSecurityMode" )] + [DataRow( Opc.Ua.DataTypes.NodeClass, 3004u, new NodeClass(), DisplayName = "EnumValue Constraints - NodeClass" ) ] + [DataRow( Opc.Ua.DataTypes.MessageSecurityMode, 3005u, new MessageSecurityMode(), DisplayName = "EnumString Constraints - MessageSecurityMode" ) ] - public void TestConstraints( uint nodeId, object enumObject ) + public void TestConstraints( uint baseNodeId, uint testNodeId, object enumObject ) { - int[] enumValuesArray = (int[])Enum.GetValues( enumObject.GetType() ); - string[] enumStringsArray = (string[])Enum.GetNames( enumObject.GetType() ); + int[] enumValuesArray = ( int[] )Enum.GetValues( enumObject.GetType() ); + string[] enumStringsArray = ( string[] )Enum.GetNames( enumObject.GetType() ); List enumValues = enumValuesArray.ToList(); List values = enumStringsArray.ToList(); - Assert.AreEqual( enumValues.Count, values.Count ); + Assert.AreEqual(enumValues.Count, values.Count); - AttributeFamilyType objectToTest = GetTestAttribute( nodeId.ToString() ); + AttributeFamilyType objectToTest = GetTestAttribute( testNodeId.ToString(), foundation: false ); Assert.IsNotNull( objectToTest ); Assert.AreEqual( "xs:string", objectToTest.AttributeDataType ); - AttributeValueRequirementType enumStringsConstraint = null; GetConstraints( objectToTest, out enumStringsConstraint ); @@ -303,6 +292,171 @@ public void TestEnumObject() } } + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + public void TestAttributeStringType() + { + // TestMessageSecurityMode + CAEXDocument doc = GetDocument(); + string enumerationObjectId = TestHelper.BuildAmlId("", TestHelper.Uris.Test, "3005"); + CAEXObject initialObject = doc.FindByID(enumerationObjectId); + AttributeFamilyType mainAttribute = initialObject as AttributeFamilyType; + Assert.IsNotNull(mainAttribute, "Unable to find Initial Object"); + + AttributeType enumStrings = GetAttribute(mainAttribute, "EnumStrings"); + Assert.AreEqual(5, enumStrings.Attribute.Count); + Assert.IsNotNull(enumStrings.AdditionalInformation); + Assert.AreEqual(1, enumStrings.AdditionalInformation.Count); + Assert.AreEqual(TestHelper.TypeOnly, enumStrings.AdditionalInformation[0] as string); + + { + AttributeType index = GetAttribute(enumStrings, "0"); + Assert.AreEqual("Ungültig", index.Value); + AttributeType de = GetAttribute(index, "de"); + Assert.AreEqual("Ungültig", de.Value); + } + + { + AttributeType index = GetAttribute(enumStrings, "1"); + Assert.AreEqual("Aucune", index.Value); + AttributeType fr = GetAttribute(index, "fr"); + Assert.AreEqual("Aucune", fr.Value); + } + + { + AttributeType index = GetAttribute(enumStrings, "2"); + Assert.AreEqual("Sign", index.Value); + Assert.AreEqual(0, index.Attribute.Count); + } + + { + AttributeType index = GetAttribute(enumStrings, "3"); + Assert.AreEqual("Sign And Encrypt", index.Value); + AttributeType en = GetAttribute(index, "en"); + Assert.AreEqual("Sign And Encrypt", en.Value); + } + } + + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + public void TestAttributeValueType() + { + // TestNodeClass + CAEXDocument doc = GetDocument(); + string enumerationObjectId = TestHelper.BuildAmlId("", TestHelper.Uris.Test, "3004"); + CAEXObject initialObject = doc.FindByID(enumerationObjectId); + AttributeFamilyType mainAttribute = initialObject as AttributeFamilyType; + Assert.IsNotNull(mainAttribute, "Unable to find Initial Object"); + + AttributeType enumStrings = GetAttribute(mainAttribute, "EnumValues"); + Assert.AreEqual(10, enumStrings.Attribute.Count); + Assert.IsNotNull(enumStrings.AdditionalInformation); + Assert.AreEqual(1, enumStrings.AdditionalInformation.Count); + Assert.AreEqual(TestHelper.TypeOnly, enumStrings.AdditionalInformation[0] as string); + + TestEnumValue(enumStrings, "0", "0", "de", "Nicht spezifiziert", "Es ist kein Wert angegeben."); + TestEnumValue(enumStrings, "1", "1", "fr", "Objet", "Le nœud est un objet."); + TestEnumValue(enumStrings, "2", "2", "en", "Variable", "The Node is a Variable."); + TestEnumValue(enumStrings, "3", "4", "de", "Verfahren", "Der Knoten ist eine Methode."); + TestEnumValue(enumStrings, "4", "8", "fr", "Type d'objet", "Le nœud est un type d'objet."); + TestEnumValue(enumStrings, "5", "16", "en", "VariableType", "The Node is a VariableType."); + TestEnumValue(enumStrings, "6", "32", "de", "Referenztyp", "Der Knoten ist ein Referenztyp."); + TestEnumValue(enumStrings, "7", "64", string.Empty, "DataType", "The Node is a DataType."); + TestEnumValue(enumStrings, "8", "128", string.Empty, "View", "The Node is a View."); + } + + public void TestEnumValue( AttributeType enumValues, + string index, + string value, + string locale, + string displayName, + string description ) + { + AttributeType indexed = GetAttribute(enumValues, index); + AttributeType valueAttribute = GetAttribute(indexed, "Value"); + Assert.IsNotNull(valueAttribute.Value); + Assert.AreEqual(value, valueAttribute.Value); + AttributeType nameAttribute = GetAttribute(indexed, "DisplayName"); + Assert.IsNotNull(nameAttribute.Value); + Assert.AreEqual(displayName, nameAttribute.Value); + if ( !string.IsNullOrEmpty(locale)) + { + AttributeType localeAttribute = GetAttribute(nameAttribute, locale); + Assert.IsNotNull(localeAttribute.Value); + Assert.AreEqual(displayName, localeAttribute.Value); + } + AttributeType descriptionAttribute = GetAttribute(indexed, "Description"); + Assert.IsNotNull(descriptionAttribute.Value); + Assert.AreEqual(description, descriptionAttribute.Value); + if (!string.IsNullOrEmpty(locale)) + { + AttributeType localeAttribute = GetAttribute(descriptionAttribute, locale); + Assert.IsNotNull(localeAttribute.Value); + Assert.AreEqual(description, localeAttribute.Value); + } + } + + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + [DataRow(Opc.Ua.DataTypes.NodeClass, DisplayName = "NodeClass NodeId")] + [DataRow(Opc.Ua.DataTypes.NegotiationStatus, DisplayName = "NegotiationStatus NodeId")] + [DataRow(Opc.Ua.DataTypes.MessageSecurityMode, DisplayName = "MessageSecurityMode NodeId")] + + public void TestEnumNodeId(uint nodeId) + { + AttributeFamilyType objectToTest = GetTestAttribute( nodeId.ToString() ); + + Assert.IsNotNull(objectToTest.Attribute["NodeId"]); + AttributeType enumFieldDefinition = objectToTest.Attribute["EnumFieldDefinition"]; + Assert.IsNotNull(enumFieldDefinition, "Unable to retrieve EnumFieldDefinition"); + // EnumFieldDefinition should not have nodeId, as the objectToTest should already have the same info + TestUnwantedAttribute(enumFieldDefinition, "NodeId", ""); + + AttributeType enums = objectToTest.Attribute["EnumStrings"]; + if (enums == null) + { + enums = objectToTest.Attribute["EnumValues"]; + } + Assert.IsNotNull(enums, "Unable to retrieve EnumStrings or EnumValues"); + Assert.IsNotNull(enums.Attribute["NodeId"]); + } + + + + [TestMethod, Timeout(TestHelper.UnitTestTimeout)] + [DataRow(Opc.Ua.DataTypes.NodeClass, "StructureFieldDefinition", DisplayName = "NodeClass StructureFieldDefinition")] + [DataRow(Opc.Ua.DataTypes.NegotiationStatus, "StructureFieldDefinition", DisplayName = "NegotiationStatus StructureFieldDefinition")] + [DataRow(Opc.Ua.DataTypes.MessageSecurityMode, "StructureFieldDefinition", DisplayName = "MessageSecurityMode StructureFieldDefinition")] + + public void TestUnwantedAttributes(uint nodeId, string unwantedAttribute) + { + AttributeFamilyType objectToTest = GetTestAttribute(nodeId.ToString()); + + AttributeType enumFieldDefinition = objectToTest.Attribute["EnumFieldDefinition"]; + Assert.IsNotNull(enumFieldDefinition, "Unable to retrieve EnumFieldDefinition"); + + TestUnwantedAttribute(enumFieldDefinition, unwantedAttribute, ""); + + AttributeType enums = objectToTest.Attribute["EnumStrings"]; + if ( enums == null ) + { + enums = objectToTest.Attribute["EnumValues"]; + } + Assert.IsNotNull(enums, "Unable to retrieve EnumStrings or EnumValues"); + + TestUnwantedAttribute(enums, unwantedAttribute, ""); + } + + // Recursively looks for unwanted attributes in the attribute tree + public void TestUnwantedAttribute(AttributeType attribute, string unwantedAttribute, string name) + { + string path = name + " " + attribute.Name; + AttributeType unwanted = attribute.Attribute[unwantedAttribute]; + Assert.IsNull(unwanted, "Unexpected Attribute found:" + path); + + foreach( AttributeType sub in attribute.Attribute) + { + TestUnwantedAttribute( sub, unwantedAttribute, path); + } + } + #endregion #region Helpers @@ -471,10 +625,14 @@ public void EnumClassTest(string nodeId, string[] values, bool hasValues, Assert.IsNotNull(valueAsText, "Unable to Find ValueAsText Property"); } - public AttributeFamilyType GetTestAttribute( string nodeId ) + public AttributeFamilyType GetTestAttribute( string nodeId, bool foundation = true ) { CAEXDocument document = GetDocument(); string rootName = TestHelper.GetOpcRootName(); + if (!foundation) + { + rootName = TestHelper.GetRootName(); + } string desiredName = rootName + nodeId; Console.WriteLine( "Looking for " + rootName + nodeId ); CAEXObject initialObject = document.FindByID( rootName + nodeId ); @@ -482,11 +640,11 @@ public AttributeFamilyType GetTestAttribute( string nodeId ) { Console.WriteLine( "Cant find, use back for " + desiredName ); - AttributeTypeLibType archie = document.CAEXFile.AttributeTypeLib[ "ATL_http://opcfoundation.org/UA/" ]; + AttributeTypeLibType attributeType = document.CAEXFile.AttributeTypeLib[ "ATL_http://opcfoundation.org/UA/" ]; - if( archie != null ) + if( attributeType != null ) { - foreach( AttributeFamilyType familyType in archie ) + foreach( AttributeFamilyType familyType in attributeType ) { if( familyType.ID.Equals( desiredName, StringComparison.OrdinalIgnoreCase ) ) { @@ -523,6 +681,24 @@ public void GetConstraints( AttributeTypeType attributeType, stringValues = enumStringsConstraint; } + public AttributeType GetAttribute( AttributeFamilyType attributeFamilyType, string attributeName) + { + Assert.IsNotNull(attributeFamilyType, "AttributeFamilyType is null"); + return GetAttribute(attributeFamilyType.Attribute, attributeName); + } + 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; + } #endregion } diff --git a/SystemTest/TestHelper.cs b/SystemTest/TestHelper.cs index fae92dc..419358c 100644 --- a/SystemTest/TestHelper.cs +++ b/SystemTest/TestHelper.cs @@ -17,6 +17,7 @@ public class TestHelper public const string ExtractPrefix = "Extract_"; public const string Opc2AmlName = "Opc2AmlConsole"; public const string Opc2Aml = Opc2AmlName + ".exe"; + public const string TypeOnly = "OpcUa:TypeOnly"; public const string TestAmlUri = "http://opcfoundation.org/UA/FX/AML/TESTING";