From 808969e731d491a1f6f8a4778a9360e3f9d11feb Mon Sep 17 00:00:00 2001 From: mpostol Date: Sun, 21 Feb 2021 23:50:28 +0100 Subject: [PATCH 1/6] Add a warning that the AS contains nodes orphaned and inaccessible for browsing starting from the Root node #529 - prototyping with XMLModelsProblemsToReportUnitTest.cs - Added errors to signal that nodes have been duplicated (have same NodeId) - working copy --- .../ModelFactoryTestingFixture/NodeFactoryBase.cs | 2 +- .../XMLModelsProblemsToReportUnitTest.cs | 6 ++++-- .../UANodeSetValidation/AddressSpaceContext.cs | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/SemanticData/Tests/USNodeSetValidationUnitTestProject/ModelFactoryTestingFixture/NodeFactoryBase.cs b/SemanticData/Tests/USNodeSetValidationUnitTestProject/ModelFactoryTestingFixture/NodeFactoryBase.cs index d97ca316..dd8271b3 100644 --- a/SemanticData/Tests/USNodeSetValidationUnitTestProject/ModelFactoryTestingFixture/NodeFactoryBase.cs +++ b/SemanticData/Tests/USNodeSetValidationUnitTestProject/ModelFactoryTestingFixture/NodeFactoryBase.cs @@ -27,7 +27,7 @@ internal class NodeFactoryBase : NodesContainer, INodeFactory /// The BrowseName of the node. public string BrowseName { - set { } + set; get; } /// diff --git a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs index c2ed3398..51f54fc1 100644 --- a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs +++ b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs @@ -78,13 +78,15 @@ public void eoursel510Test() List orphanedNodes = new List(); foreach (IUANodeContext item in allNodes) { - if (!nodesDictionary.ContainsKey(item.BrowseName.Name)) + if (!nodesDictionary.ContainsKey(item.BrowseName.ToString())) { orphanedNodes.Add(item); Debug.WriteLine($"The following node has been removed from the model: {item.ToString()}"); } } - Debug.WriteLine($"After removing inherited and instance declaration nodes the recovered information model contains {nodes.Count()}"); + Debug.WriteLine($"The recovered information model contains {nodes.Count()} nodes"); + Debug.WriteLine($"The source information model contains {allNodes.Count()} nodes"); + Debug.WriteLine($"Number of nodes not considered for export {orphanedNodes.Count()}"); } } diff --git a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs index 44bb9bb6..958817cd 100644 --- a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs +++ b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs @@ -282,8 +282,14 @@ private Uri ImportNodeSet(UANodeSet model) IUAModelContext _modelContext = model.ParseUAModelContext(m_NamespaceTable, m_TraceEvent.TraceEvent); m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage($"Entering AddressSpaceContext.ImportNodeSet - starting import {_modelContext}.")); m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage("AddressSpaceContext.ImportNodeSet - the context for the imported model is created and starting import nodes.")); - foreach (UANode _nd in model.Items) - ImportUANode(_nd); + Dictionary itemsDictionary = new Dictionary(); + foreach (UANode node in model.Items) + { + if (itemsDictionary.ContainsKey(node.NodeId.ToString())) + m_TraceEvent.TraceEvent(TraceMessage.BuildErrorTraceMessage(BuildError.NotSupportedFeature, $"{node.ToString()} has been duplicated in the imported model and is removed from further processing")); + else + ImportUANode(node); + } m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage($"Finishing AddressSpaceContext.ImportNodeSet - imported {model.Items.Length} nodes.")); return _modelContext.ModelUri; } From 3faf0d53614e1da4797f28f78d5bb2b5580b31e1 Mon Sep 17 00:00:00 2001 From: mpostol Date: Mon, 22 Feb 2021 18:07:31 +0100 Subject: [PATCH 2/6] Enhance/Improve node selection algorithm for ValidateAndExportModel working on documentation ObjectModelSpecyficatio.md --- .../docs/ObjectModelSpecyficatio.md | 105 +++++++++++++----- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md b/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md index dd1fff98..ae43fcfe 100644 --- a/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md +++ b/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md @@ -8,62 +8,103 @@ The primary objective of the OPC UA application is to expose information that ca - **meaningful** – there must exist rules (unambiguous for all interoperating parties) on how to map the meaning and bitstreams (data) - **addressable** – there must exist services to selectively access the data -To promote interoperability in the multi-vendor environment the services fulfilling appropriate functionality must be standardized. +To promote interoperability in the multi-vendor environment the services fulfilling these functionality must be standardized. -The discussion related to the data transfer is outside the scope of this document and will be skipped. +The discussion related to the data transfer is outside the scope of this document. Based on the role humans take while using OPC UA applications they can be grouped as follows: - **human-centric** - information origin or ultimate information destination is an operator, - **machine-centric** - information creation, consumption, networking, and processing are achieved entirely without human interaction. -A typical **human-centric** approach is a web-service supporting, for example, a web user interface (UI) to monitor conditions and manage millions of devices and their data in a typical cloud-based IoT approach. In this case, it is characteristic that any uncertainty and necessity to make a decision can be relaxed by human interaction. Coordination of robot behaviors in a work-cell (automation islands) is a **machine-centric** example. In this case, any human interaction must be recognized as impractical or even impossible. This interconnection scenario requires machine to machine communication (M2M) demanding the integration of multi-vendor devices. +A typical **human-centric** approach is a web-service supporting, for example, a web user interface (UI) to monitor conditions and manage millions of devices in a typical cloud-based IoT approach. It is essential in this case that any uncertainty and necessity to make a decision can be relaxed by human interaction. Coordination of robot behaviors in a work-cell (automation islands) is a **machine-centric** example. In this case, any human interaction must be recognized as impractical or even impossible. This interconnection scenario requires the machine to machine communication (M2M) demanding the integration of multi-vendor devices. -To leverage the **meaningful** data distribution, the OPC UA engages rules derived from the object-oriented programming concept. Following this approach types are commonly used to describe the data semantics (to assign meaning to the bitstreams). For example, using Int32 we are dealing with a set of numbers that can be represented as bitstreams 32 bits long. Unfortunately, sometimes it is not enough. Let assume that we are going to use these numbers to express the age in a personal record. In the **human-centric** environment, we can use the appropriate names derived from the native language of the data placeholders called variables. For the **machine-centric** case, the multi-vendor environment must be considered. A typical approach to deal with this environment is the usage of names defined by a commonly acceptable standardization body. To make the name unambiguous for all vendors it must be globally unique. +To leverage the **meaningful** data distribution, the OPC UA engages rules derived from the object-oriented programming concept. Following this approach types are commonly used to describe the data semantics (to assign meaning to the bitstreams). For example, using Int32 we are dealing with a set of numbers that can be represented as bitstreams 32 bits long. Unfortunately, sometimes it is not enough. Let assume that we are going to use these numbers to express the age in a personal record. In the **human-centric** environment, we can use the appropriate names derived from the native language of the data holders called variables. For the **machine-centric** case, the multi-vendor environment must be considered. A typical approach to deal with this environment is the usage of names defined by a commonly acceptable standardization body. To make the name unambiguous 9to avoid name collision) for all vendors it must be globally unique. -Generally speaking, to select a particular target piece of complex data we have two options: **random access** or **browsing**. **Random-access** requires that the target item must have been assigned a unique address that must be known in advance by a selection operation. The browsing approach means that the data consumer walks down available paths from an entity to an entity that builds up the structure of compound data - a data graph - using references interconnecting entities. It is necessary if we need to represent a relationship between data components. As an example, consider a family tree containing a graph of personal records. The browsing process is costly because instead of jumping to a target, we need to traverse the graph step by step using references. The main advantage of this approach is that the data consumer do not need any prior knowledge of the data structure. To minimize the cost, after having found the target as the result of browsing the graph, every operation targeting it can use direct access. Random access is possible only if the browsing path is convertible to a unique direct address or selected targets have well know addresses assigned by a standardization body. +Generally speaking, to select a particular target piece of complex data we have two options: **random access** or **browsing**. **Random-access** requires that the target item must have been assigned a unique address known in advance by a selection operation. The browsing approach means that the data consumer walks down available paths from an entity to an entity that builds up the structure of compound data - a data graph - using references interconnecting entities. It is necessary if we need to represent a relationship between data components. As an example, consider a family tree containing a graph of personal records. The browsing process is costly because instead of jumping to a target, we need to traverse the graph step by step using references. The main advantage of this approach is that the data consumer do not need any prior knowledge of the data structure. To minimize the cost, after having found the target as the result of browsing the graph, every operation targeting it can use direct access. Random access is possible only if the browsing path is convertible to a unique direct address or selected targets have well know addresses assigned by a standardization body. -It seems that, despite the access method, we have to assign an address to all of the accessible entities in the representation of the process data structure. In this concept, this atomic addressable entity is called a node. Each node is a collection of attributes (value-holders) that have values accessible locally in the context of the node. To enable browsing the internal structure of the nodes graph (relationship information), nodes are interconnected by references (address-holders of coupled nodes). Taking into consideration that the browse mechanism is based on the incremental and relative passage along the path of a node, we can easily find out that each path must have a defined entry point, so we must address the question of where to start. +It seems that, despite the access method, we have to assign an identification to all of the accessible entities in the representation of the process data structure. In this concept, this atomic identifiable entity is called a node. Each node is a collection of attributes (value-holders) that have values accessible locally in the context of the node. To enable browsing the internal structure of the nodes graph (relationship information), nodes are interconnected by references (address-holders of coupled nodes). Taking into consideration that the browse mechanism is based on the incremental and relative passage along the path of interconnected nodes, we can easily find out that each path must have a defined entry point, so we must address the question of where to start. -The collection of these nodes is called the Address Space. OPC UA Address Space concept is all about exposing the process data in a standard way. The main goal of exposing a graph of nodes as one whole is to create a meaningful context for the underlying process data. To create the Address Space, we need to instantiate nodes and interconnect them by references. +The collection of these nodes is called the **address space**. The OPC UA Address Space concept is all about exposing the process data in a standard way. The main goal of exposing a graph of nodes as one whole is to create a meaningful context for the underlying process data. To create the Address Space, we need to instantiate nodes and interconnect them by references. -## Naming Conventions for Nodes - -### Introduction - -To instantiate the Address Space we need to deal with naming, addressing, and meaning of the nodes. Appropriate naming is helpful in the **human-centric** environment, especially at the design-time. Proper addressing is essential for **machine-centric** environment, especially at the run-time. Designing appropriate rules applied to make the Address Space meaningful is necessary for both and must be addressed by the information model design process. All mentioned above aspects are tightly coupled and contribute to the design process. The design process can be backed by: +To instantiate the **address space** we need to deal with naming, addressing, and meaning of the nodes. Appropriate naming is helpful in the **human-centric** environment, especially at the design-time. Proper addressing is essential for **machine-centric** environment, especially at the run-time. Designing appropriate rules applied to make the **address space** meaningful is necessary for both and must be addressed by the information model design process. All mentioned above aspects are tightly coupled and contribute to the design process. The design process can be backed by: - design conventions - contributing to design best practice rules - OPC UA concepts - as a foundation of AS deployment addressing a selected process requirements -- design tool - to author reusable in the multi-vendor market comprehensive information model +- design tool - to author reusable in the multi-vendor market comprehensive information model The following section covers a detailed description of the design conventions to improve reusability, comprehensiveness and minimize the deployment costs in the production environment. OPC UA engages the following concepts supporting the mentioned above topics, namely naming, addressing, and meaning associations: -- BrowseName attribute - to support browsing and meaning association -- DisplayName attribute - to enable comprehensive description using native languages -- NodeId attribute - to implement the nodes direct addressing -- Reference - to apply nodes relationship information -- Type concept - to provide metadata used as a meaningful context for the process data. +- `BrowseName` attribute - to support browsing and meaning association +- `DisplayName` attribute - to enable comprehensive description using native languages +- `NodeId` attribute - to implement the nodes direct addressing +- `Reference` - to apply nodes relationship information +- `Type` concept - to provide metadata used as a meaningful context for the process data. + +To create the address space exposed by an OPC UA Application it must instantiate all nodes and interconnect them through references at the bootstrap process. Before the address space can be instantiated by an OPC UA application it must be designed first. To promote reusability of the address space design process a Domain Specific Language (DSL) is required. A detailed description of this process is covered by the document [Address Space Model Life-cycle](https://commsvr.gitbook.io/ooi/semantic-data-processing/informationmodelsdevelopment/informationmodellifecycle). A mandatory option - coined as `NodeSet` model - of the DSL is described in OPC UA Specification [Part 6: Mappings][Opc.UA.Part6]. By design, it minimizes the required effort spent by the OPC UA applications to instantiate the address space because it requires a detailed description of all implementation details enabling to avoid the necessity to resolve inheritance chains, type definitions, encodings, direct addressing, defaults, etc. A detailed description of this DSL is covered by the document [OPC UA Address Space Interchange XML][InterchangeXML]. As a result, it is expected that all OPC UA applications and design tools must be compliant with this language somehow. + +This standard additionaly introduces the term + +- `SymbolicName` - an identifier that uniquely identifies a specific entity in a program or procedure. + +## Naming Conventions for Nodes + +### General Rules for BrowseName Attribute + +OPC UA defines two attributes containing naming information about an OPC UA Node, the `BrowseName` and the `DisplayName`. The `NodeSet` DSL additionally introduces `SymbolicName`. + +The `BrowseName` is of the `QualifiedName` type. Each complex value of this type contains `namespaceIndex` and `name` fields. The `namespaceIndex` field is an index that identifies the namespace (a set of unique names) that defines the context of the name. This index is a selector of that namespace in an array of namespace entries in concern. This array may be used to access the actual value of the namespace selected by the `namespaceIndex`. + +Namespace concept is used by OPC UA to create unique identifiers across different naming authorities. The attributes NodeId and BrowseName are identifiers. A Node in the UA AddressSpace is unambiguously identified using a NodeId. Unlike the NodeId identifier, the BrowseName cannot be used to unambiguously identify a Node. Different Nodes may have the same `BrowseName`. -OPC UA defines two attributes containing naming information about an OPC UA Node, the BrowseName and the DisplayName. +The `BrowseName` are used: -Recommendations for DisplayName will be given in a later version of this document. +- to build a browse path between Nodes +- define a globaly unique meaning of an entity, e.g. properties -The BrowseName is of DataType QualifiedName, containing a NamespaceIndex and a String. Unless the BrowseName is defined in some other Namespace or there is some specific handling for the BrowseName, the Namespace for the BrowseName should be the one the Node is defined in (i.e. the same Namespace as the NodeId). Nodes defined in a Companion Specification should use the Namespace of the Companion Specification for their NodeIds and BrowseNames. -For the string-part the following naming conventions apply: +In case the `BrowseName` is applied to build a browse path its uniqueness is resolved in the context of a parent node. Unless the `BrowseName` is assigned a globally specific meaning defined independently the `namespaceIndex` shall be the same as assigned to `NodeId` attribute of the hosting Node. It is recommended that Nodes defined in any custom model (including but not limited to Companion Specification) should use a Namespace of the model for their NodeId and BrowseName attributes. -### General Rules for BrowseNames +If `BrowseName` is associated with a globally unique meaning it shall be defined in the context of a dedicated namespace. For this purpose, standardization organizations (naming authorities) shall define independent globally unique identifiers (e.g. URI) that must be added to the local namespaces array and to be indexed by the `BrowseName` value. -All BrowseNames should be upper camel case (also known as PascalCase), that is, all words written without spaces, and the first character of each word is upper case, the other characters are lower case. Examples: ReferenceType, BaseObjectType, Int32. +For the `name` part of the `BrowseName` attribute, the following naming conventions apply. + +The `name` field value should be the upper camel case (also known as PascalCase), that is, all words are written without spaces (concatenated), and the first character of each word is the upper case letter, the other characters are lower case or digits. Examples: ReferenceType, BaseObjectType, Int32. If an acronym or abbreviation is used, upper camel case should also be used. Examples: PortMacAddress (where MAC is an acronym for Media Access Control), NodeId (where ID is an -abbreviation for identification), UInt32 (where U is an abbreviation for unsigned). In general, it is recommended to only use letters, digits or the underscore (‘_’) as characters for the BrowseName for TypeDefinitions (ObjectTypes, VariableTypes, DataTypes, ReferenceTypes and InstanceDeclarations), unless it is explicitly defined like “<” and “>” for optional placeholders. +abbreviation for identyfier), UInt32 (where U is an abbreviation for unsigned). In general, it is recommended to only use letters, digits or the underscore (‘_’) as characters for the `BrowseName`. unless it is explicitly defined like “<” and “>” for optional placeholders. + +> Remark: If special chars like “&”, “<”, etc. are used, the `NodeSet` document should define the `SymbolicName` attribute for that Node as well. This can then be used for code generation. + +There is no recommendation on the use of prefixes. Companion Specifications may use a prefix because it suits their model. For example, if the Vision companion specification were to define types based on generic concepts (say a state machine), then using the prefix “Vision” may make sense (as in “VisionStateMachineType”). + +### General Rules for DisplayName Attribute + + +### General Rules for NodeId Attribute -> Remark: If special chars like “&”, “<”, etc. are used, the NodeSet-File should define the optional SymbolicName for that Node. This can then be used for code generation. + + + -There is no recommendation on the use of prefixes. Companion specifications may use a prefix because it suits their model. For example, if the Vision companion specification were to define types based on generic concepts (say a state machine), then using the prefix “Vision” may make sense (as in “VisionStateMachineType”). +### General Rules for SymbolicName Attribute + +Accordin to the specyfication it can be used as a class/field name in auto generated code. It should only be specified if the `BrowseName` cannot be used for this purpose. + +This xml attribute does not appear in the AddressSpace and is intended for use by design tools. Only letters, digits or the underscore (‘_’) are permitted. The detailed syntax definition is as follows by the type `SymbolicName` + +```XML + + + + + + + + + +``` ## UANodeSet validation @@ -83,7 +124,7 @@ The version information is also provided as part of the ModelTableEntry in the U The NamespaceUri for all NodeIds defined in this document is defined in Annex A. The NamespaceIndex for this NamespaceUri is vendor-specific and depends on the position of the NamespaceUri in the server namespace table. -Namespaces are used by OPC UA to create unique identifiers across different naming authorities. The Attributes NodeId and BrowseName are identifiers. A Node in the UA AddressSpace is unambiguously identified using a NodeId. Unlike NodeIds, the BrowseName cannot be used to unambiguously identify a Node. Different Nodes may have the same BrowseName. They are used to build a browse path between two Nodes or to define a standard Property. + ## AS graph @@ -120,4 +161,10 @@ As long as those InstanceDeclarations are not overridden they are not referenced As long as those InstanceDeclarations are not overridden they are not referenced by the subtype. InstanceDeclarations can be overridden by adding References, changing References to reference different Nodes, changing References to be subtypes of the original ReferenceType, changing values of the Attributes or adding optional Attributes. In order to get the full information about a subtype, the inherited InstanceDeclarations have to be collected from all types that can be found by recursively following the inverse HasSubtype References from the subtype. This collection of InstanceDeclarations is called the fully-inherited InstanceDeclarationHierarchy of a subtype. -### [P4 5.8.4 TranslateBrowsePathsToNodeIds](https://reference.opcfoundation.org/v104/Core/docs/Part4/5.8.4/) \ No newline at end of file +- [P4 5.8.4 TranslateBrowsePathsToNodeIds](https://reference.opcfoundation.org/v104/Core/docs/Part4/5.8.4/) +- [Postół M. (2016) OPC UA Address Space Interchange XML; Technical Report; DOI: 10.13140/RG.2.2.12228.37768][InterchangeXML] +- [OPC Unified Architecture Specification Part 6: Mappings, OPC Foundation, Rel. 1.04, 2017-11-22][OPC.UA.Part6] + +[InterchangeXML]: https://www.researchgate.net/publication/334259707_OPC_UA_Address_Space_Interchange_XML +[Opc.UA.Part6]:https://opcfoundation.org/developer-tools/specifications-unified-architecture/part-6-mappings/ +[InterchangeXML]: https://www.researchgate.net/publication/334259707_OPC_UA_Address_Space_Interchange_XML \ No newline at end of file From e5e428b24f6c820638dce2ad2d483404b90ff8ea Mon Sep 17 00:00:00 2001 From: mpostol Date: Tue, 23 Feb 2021 10:41:57 +0100 Subject: [PATCH 3/6] Add a warning that the AS contains nodes orphaned and inaccessible for browsing starting from the Root node #529 -Prototyping with that - working on an algorithm to compare nodes in UANodesSet and ModelDesign. - UT :+1: --- .../XMLModelsProblemsToReportUnitTest.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs index 51f54fc1..a3d98645 100644 --- a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs +++ b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs @@ -68,7 +68,7 @@ public void eoursel510Test() addressSpace.ValidateAndExportModel(model); Assert.AreEqual(5, traceContext.TraceList.Count); IEnumerable nodes = testingModelFixture.Export(); - Assert.AreEqual(21, nodes.Count< NodeFactoryBase>()); + Assert.AreEqual(21, nodes.Count()); Dictionary nodesDictionary = nodes.ToDictionary(x => x.SymbolicName.Name); AddressSpaceContext asContext = addressSpace as AddressSpaceContext; //TODO Add a warning that the AS contains nodes orphaned and inaccessible for browsing starting from the Root node #529 @@ -76,17 +76,21 @@ public void eoursel510Test() asContext.UTValidateAndExportModel(1, x => allNodes = x); Assert.IsNotNull(allNodes); List orphanedNodes = new List(); + List processedNodes = new List(); foreach (IUANodeContext item in allNodes) { - if (!nodesDictionary.ContainsKey(item.BrowseName.ToString())) + if (!nodesDictionary.ContainsKey(item.BrowseName.Name)) { orphanedNodes.Add(item); Debug.WriteLine($"The following node has been removed from the model: {item.ToString()}"); } + else + processedNodes.Add(item); } - Debug.WriteLine($"The recovered information model contains {nodes.Count()} nodes"); + Debug.WriteLine($"The recovered information model contains {nodesDictionary.Count} nodes"); Debug.WriteLine($"The source information model contains {allNodes.Count()} nodes"); - Debug.WriteLine($"Number of nodes not considered for export {orphanedNodes.Count()}"); + Debug.WriteLine($"Number of nodes not considered for export {orphanedNodes.Count}"); + Debug.WriteLine($"Number of processed nodes {processedNodes.Count}"); } } From 0742e54088fc5730f35b6ddf5e0a82539385d289 Mon Sep 17 00:00:00 2001 From: mpostol Date: Tue, 23 Feb 2021 11:32:57 +0100 Subject: [PATCH 4/6] Enhance/Improve NodeId must be unique in context of a model #539 - added anchor to start with --- SemanticData/UANodeSetValidation/AddressSpaceContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs index 958817cd..e05fb867 100644 --- a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs +++ b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs @@ -283,6 +283,7 @@ private Uri ImportNodeSet(UANodeSet model) m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage($"Entering AddressSpaceContext.ImportNodeSet - starting import {_modelContext}.")); m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage("AddressSpaceContext.ImportNodeSet - the context for the imported model is created and starting import nodes.")); Dictionary itemsDictionary = new Dictionary(); + //TODO Enhance/Improve NodeId must be unique in context of a model #539 foreach (UANode node in model.Items) { if (itemsDictionary.ContainsKey(node.NodeId.ToString())) From 85566283dc05a8b38eeb47ec778a841b364ae363 Mon Sep 17 00:00:00 2001 From: mpostol Date: Tue, 23 Feb 2021 16:26:44 +0100 Subject: [PATCH 5/6] Enhance/Improve BrowseName parser #538 - added anchors to the code - unimportant modifications --- .../QualifiedNameUnitTest.cs | 1 + .../XMLModelsProblemsToReportUnitTest.cs | 2 +- .../UANodeSetValidation/DataSerialization/QualifiedName.cs | 1 + SemanticData/UANodeSetValidation/UANodeContext.cs | 7 ++++--- SemanticData/UANodeSetValidation/XML/UANode.cs | 1 + .../UANodeSetValidation/docs/ObjectModelSpecyficatio.md | 4 ++-- 6 files changed, 10 insertions(+), 6 deletions(-) diff --git a/SemanticData/Tests/DataSerializationUnitTestProject/QualifiedNameUnitTest.cs b/SemanticData/Tests/DataSerializationUnitTestProject/QualifiedNameUnitTest.cs index 0d4773f9..3232e84c 100644 --- a/SemanticData/Tests/DataSerializationUnitTestProject/QualifiedNameUnitTest.cs +++ b/SemanticData/Tests/DataSerializationUnitTestProject/QualifiedNameUnitTest.cs @@ -26,6 +26,7 @@ public void QualifiedNameTestMethod1() [TestMethod] public void QualifiedNameParseTestMethod3() { + //TODO Enhance/Improve BrowseName parser #538 QualifiedName _qn = QualifiedName.Parse("Name"); //Cannot find information that the NamespaceIndex is optional Assert.IsNotNull(_qn); Assert.AreEqual(_qn.NamespaceIndex, 0); diff --git a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs index a3d98645..65a2f546 100644 --- a/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs +++ b/SemanticData/Tests/USNodeSetValidationUnitTestProject/XMLModelsProblemsToReportUnitTest.cs @@ -34,7 +34,7 @@ public void ADITest() Assert.AreEqual(BuildError.ModelsCannotBeNull.Identifier, traceContext.TraceList[0].BuildError.Identifier); traceContext.Clear(); addressSpace.ValidateAndExportModel(model); - //TODO ADI model from Embedded example import fails #509 + //TODO Enhance/Improve BrowseName parser #538 Assert.AreEqual(0, traceContext.TraceList.Where(x => x.BuildError.Focus == Focus.DataEncoding).Count()); Assert.AreEqual(0, traceContext.TraceList.Where(x => x.BuildError.Focus == Focus.DataType).Count()); Assert.AreEqual(0, traceContext.TraceList.Where(x => x.BuildError.Focus == Focus.Naming).Count()); diff --git a/SemanticData/UANodeSetValidation/DataSerialization/QualifiedName.cs b/SemanticData/UANodeSetValidation/DataSerialization/QualifiedName.cs index f7ae5027..1d34672a 100644 --- a/SemanticData/UANodeSetValidation/DataSerialization/QualifiedName.cs +++ b/SemanticData/UANodeSetValidation/DataSerialization/QualifiedName.cs @@ -34,6 +34,7 @@ namespace UAOOI.SemanticData.UANodeSetValidation.DataSerialization /// 1:MyName ///
/// + //TODO Enhance/Improve BrowseName parser #538 public partial class QualifiedName : IFormattable, ICloneable, IComparable { #region Constructors diff --git a/SemanticData/UANodeSetValidation/UANodeContext.cs b/SemanticData/UANodeSetValidation/UANodeContext.cs index 28acb024..1385550c 100644 --- a/SemanticData/UANodeSetValidation/UANodeContext.cs +++ b/SemanticData/UANodeSetValidation/UANodeContext.cs @@ -83,6 +83,7 @@ public void Update(UANode node, Action addReference) return; } UANode = node; + //TODO Enhance/Improve BrowseName parser #538 this.BrowseName = node.BrowseName.Parse(_TraceEvent); if (QualifiedName.IsNull(this.BrowseName)) { @@ -328,9 +329,9 @@ public bool Equals(IUANodeBase other) { if (Object.ReferenceEquals(other, null)) return false; - //TODO ADI model from Embedded example import fails #509 - if (this.BrowseName != other.BrowseName) //1:TransitionNumber vs TransitionNumber; 1:StateNumber vs StateNumber - return false; // throw new ArgumentOutOfRangeException("The browse name of compared nodes musty be equal."); + //TODO Enhance/Improve BrowseName parser #538 + if (this.BrowseName != other.BrowseName) + return false; return this.UANode.Equals(other.UANode); } diff --git a/SemanticData/UANodeSetValidation/XML/UANode.cs b/SemanticData/UANodeSetValidation/XML/UANode.cs index 3e361286..2733db49 100644 --- a/SemanticData/UANodeSetValidation/XML/UANode.cs +++ b/SemanticData/UANodeSetValidation/XML/UANode.cs @@ -33,6 +33,7 @@ public virtual bool Equals(UANode other) ParentEquals(other) && this.AccessRestrictions == other.AccessRestrictions && this.BrowseName.AreEqual(other.BrowseName) && + //TODO Enhance/Improve BrowseName parser #538 this.Description.LocalizedTextArraysEqual(other.Description) && this.DisplayName.LocalizedTextArraysEqual(other.DisplayName) && this.Documentation.AreEqual(other.Documentation) && diff --git a/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md b/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md index ae43fcfe..b30d0bd2 100644 --- a/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md +++ b/SemanticData/UANodeSetValidation/docs/ObjectModelSpecyficatio.md @@ -62,7 +62,7 @@ Namespace concept is used by OPC UA to create unique identifiers across differen The `BrowseName` are used: - to build a browse path between Nodes -- define a globaly unique meaning of an entity, e.g. properties +- define a globaly unique meaning of an entity, e.g. properties, types, etc In case the `BrowseName` is applied to build a browse path its uniqueness is resolved in the context of a parent node. Unless the `BrowseName` is assigned a globally specific meaning defined independently the `namespaceIndex` shall be the same as assigned to `NodeId` attribute of the hosting Node. It is recommended that Nodes defined in any custom model (including but not limited to Companion Specification) should use a Namespace of the model for their NodeId and BrowseName attributes. @@ -151,7 +151,7 @@ The semantic of this ReferenceType is to express a subtype relationship of types [5.8.3 DataType NodeClass](https://reference.opcfoundation.org/v104/Core/docs/Part3/5.8.3/) HasSubtype References may be used to expose a data type hierarchy in the AddressSpace. The semantic of subtyping is only defined to the point, that a Server may provide instances of the subtype instead of the DataType. Clients should not make any assumptions about any other semantic with that information. For example, it might not be possible to cast a value of one data type to its base data type. Servers need not provide HasSubtype References, even if their DataTypes span a type hierarchy. Some restrictions apply for subtyping enumeration DataTypes as defined in 8.14. -[6.3 Subtyping of ObjectTypes and VariableTypes](https://reference.opcfoundation.org/v104/Core/docs/Part3/6.3.1/) +[6.3 Subtyping of ObjectTypes and VariableTypes](https://reference.opcfoundation.org/v104/Core/docs/Part3/6.3.1/) The HasSubtype ReferenceType defines subtypes of types. Subtyping can only occur between Nodes of the same NodeClass. Rules for subtyping ReferenceTypes are described in 5.3.3.3. There is no common definition for subtyping DataTypes, as described in 5.8.3. The remainder of 6.3 specify subtyping rules for single inheritance on ObjectTypes and VariableTypes. From 8f64a315922010269c9bf65ecdb2eb73c3e8a640 Mon Sep 17 00:00:00 2001 From: mpostol Date: Tue, 23 Feb 2021 16:42:44 +0100 Subject: [PATCH 6/6] Enhance/Improve NodeId must be unique in context of a model #539 - fixes #539 --- SemanticData/UANodeSetValidation/AddressSpaceContext.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs index e05fb867..bb99ed0c 100644 --- a/SemanticData/UANodeSetValidation/AddressSpaceContext.cs +++ b/SemanticData/UANodeSetValidation/AddressSpaceContext.cs @@ -283,11 +283,10 @@ private Uri ImportNodeSet(UANodeSet model) m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage($"Entering AddressSpaceContext.ImportNodeSet - starting import {_modelContext}.")); m_TraceEvent.TraceEvent(TraceMessage.DiagnosticTraceMessage("AddressSpaceContext.ImportNodeSet - the context for the imported model is created and starting import nodes.")); Dictionary itemsDictionary = new Dictionary(); - //TODO Enhance/Improve NodeId must be unique in context of a model #539 foreach (UANode node in model.Items) { if (itemsDictionary.ContainsKey(node.NodeId.ToString())) - m_TraceEvent.TraceEvent(TraceMessage.BuildErrorTraceMessage(BuildError.NotSupportedFeature, $"{node.ToString()} has been duplicated in the imported model and is removed from further processing")); + m_TraceEvent.TraceEvent(TraceMessage.BuildErrorTraceMessage(BuildError.NodeIdDuplicated, $"The {node.NodeId.ToString()} is already defined in the imported model and is removed from further processing.")); else ImportUANode(node); }