C++ Based OPC UA Client/Server SDK  1.6.0.389
OPC UA NodeId Concepts

Difference to “Classic” OPC DA

In the old days the classic DA Server have used simple “string”-Identifiers. The so called “ItemID” was a fully qualified name that was unique throughout the whole server (there was only one “namespace”). Furthermore, the classic DA Servers had only capabilities for a simple hierarchy, i.e. a tree-like structure with branches and leaves. Hence, many vendors have used the full folder hierarchy to create unique ItemIDs (e.g. “Folder1.Folder2.Folder3.MyTemperature”). This lead to massive redundant strings, wasting memory and slowing down performance when looking up or searching for individual Items. With OPC UA, this concept has been abandoned.

OPC UA NodeIds

In OPC UA, every entity in the address space is a node. To uniquely identify a Node, each node has a NodeId that consists of a namespace and an identifier. NodeIds are used to address Nodes in OPC UA Services like Read, Write, Call or CreateMonitoredItems.

The namespace is used to ensure unique NodeIds even if different naming authorities use the same identifiers. This happens if naming authorities work independent of each other like different information model working groups.

The identifier is defined by the naming authority for the namespace. The naming authority can be a standard working group like the OPC UA working group or it could be the local OPC UA Server providing the Nodes. Most standard namespaces define only the identifier for type Nodes like ObjectTypes or DataTypes. Identifiers of instance Nodes like Objects, Variables and Methods are typically defined by the local Server in a dedicated namespace for the Server specific instances. Even if the Server has product or Server specific type Nodes, it is strongly recommended to use own namespaces for type Nodes and instance Nodes.

Identifiers for standard namespaces are known to Clients and Servers. Identifiers for Server defined Nodes are opaque to a Client and a Client cannot make any assumptions about the identifier.

Servers must ensure that NodeIds do not change for Nodes, as long as the Node represents the same information source. Therefore the Server must use logic for identifier creation that ensures that the identifier does not change after a restart of the Server or reconfiguration of the Server.

Clients can get NodeIds through the Services Browse or TranslateBrowsePathsToNodeIds. Browse is typically used by Clients to navigate through the Server address space and to select Nodes and the resulting NodeId of interest. TranslateBrowsePathsToNodeIds is used to get NodeIds of Nodes by following a known path of BrowseNames from a known starting Node. Clients can persist NodeIds of Nodes if necessary, this includes Nodes received through TranslateBrowsePathsToNodeIds.

The NodeId is always composed of three elements:

NamespaceIndex
The index an OPC UA server uses for a namespace URI. The namespace URI identifies the naming authority defining the identifiers of NodeIds, e.g. the OPC Foundation, other standard bodies and consortia, the underlying system, the local server. They are stored in the so-called namespace array (also referred to as namespace table). Namespace indexes are numeric values used to identify namespaces to optimize transfer and processing. The namespace index is the index of the namespace URI in the namespace array.
IdentifierType
The format and data type of the identifier. It can be a numeric value, a string, a globally unique identifier (GUID), or an opaque value (a namespace specific format in a ByteString). Which type is preferred depends on the use case. If it is important to save memory or bandwidth, it makes sense to use numeric NodeIds which are smaller and faster to resolve. The OPC UA namespace as defined by the OPC Foundation uses numeric NodeIds. System-wide and globally unique identifiers allow clients to track Nodes, e.g. work orders, moving between OPC UA servers as they progress through the system.
Identifier
The identifier for a node in the address space of an OPC UA server.

Numeric identifiers are most efficient and easy to handle in Client and Server implementations. Numeric identifiers are used if the Nodes are static like Nodes defined by standard working groups. Static Nodes have identical and static Attribute values in all Servers providing the corresponding namespace. Numeric identifiers are also used by Servers with a well known address space like for an embedded OPC UA Server that has a known number of Nodes for data and configuration options. Numeric identifiers cannot be used for namespaces where Nodes are configured dynamically since a Server will not be able to assign unique identifiers due to a limited number of identifiers.

String identifiers provide a simple way to ensure unique identifiers that can be persisted even if nodes are configured dynamically. One option is to use the path as identifier. Another option is to use a Server defined syntax to construct the identifier from Server internal addressing information.

GUID identifiers provide a well defined option to generate unique identifiers, especially if the path to a Node does not change but the Node is logically different. One example is the Object that represents the Session diagnostic where the Client does not change at a reconnect but the Object is logically a different Session after a reconnect.

The following image shows examples for NodeIds having different identifier types.

nodeid_concept_1.png
Examples for different types of NodeIds

String Notation

There is an string notation for NodeIds defined as part of the OPC UA XML Schema which represents a fully qualified NodeId. The format of the string is:

ns=<namespaceIndex>;<identifiertype>=<identifier>

with the fields

<namespace index>
The namespace index formatted as a base 10 number. If the index is 0, then the entire “ns=0;” clause is omitted.
<identifier type>

A flag that specifies the identifier type. The flag has the following values:

Flag Identifier Type
i NUMERIC (UInteger)
s STRING (String)
g GUID (Guid)
b OPAQUE (ByteString)
<identifier>
The identifier encoded as string. The identifier is formatted using the XML data type mapping for the identifier type. Note that the identifier may contain any non-null UTF8 character including whitespace.

Examples:

ns=2;s=MyTemperature
namespace index 2, string identifier
i=2045
namespace index 0, numeric identifier
ns=1;g=09087e75-8e5e-499b-954f-f2a9603db28a
namespace index 1, GUID identifier
ns=1;b=M/RbKBsRVkePCePcx24oRA=='
namespace index 1, Opaque/ByteString identifier

Namespace part of a NodeId

The identifier part of a NodeId uniquely identifies a node within a namespace, but it is possible that the same identifier is used in different namespaces for different nodes. Hence only the namespace plus the identifier forms a fully qualified identifier (see figure below). This means if a client requests a node, e.g. in a read service, it not only needs the identifier, but also the namespace the node belongs to.

nodeid_concept_2.png
Nodes with the same identifier in different namespaces

Client Namespace Mapping

In OPC UA service calls the namespace index is used instead of the longer namespace URI in NodeIds. The Client needs to take care of the correct mapping from namespace URI to namespace index. Servers are not allowed to change the namespace index for a specific namespace URI or delete entries from the namespace table as long as an active session exist, so that clients can cache the namespace table for a specific session. But a Server may change namespace indexes and remove entries from the namespace table if no Client is connected or if the server is restarted. For this reason, a Client should not persist the namespace index without storing the namespace URI as well, because a namespace URI represented by index “2” during one session could be represented by index “5” during the next session. Thus, when having established a session with a Server, a Client should always read the Server’s namespace table and update namespace indexes before calling services in which NodeIds are involved.

The following figure shows a typical procedure a Client follows when reading atrributes of a node. In this example, the client wants to read the Node represented by the identifier “MyTemperature” which belongs to the namespace identified by the URI “urn:MyCompany:UaServer:Model2”. The client stores an own namespace table containing the URIs it is interested in to build up fully qualified NodeIds, but doesn’t yet know the corresponding namespace index in the Server namespace table. For being able to access the correct Node, the client has to read the Server namespace table first. The namespace URI “urn:MyCompany:UaServer:Model2” is represented by namespace index “3” on the Server. Now the client is able to update the namespace indexes in its own namespace table and the NodeId it wants to access (in our example this means replacing index “2” with index “3”) and has all information that is needed to access the correct node, in our example “ns=3;s=MyTemperature” in XML notation. As it is allowed that the namespace identified by the URI “urn:MyCompany:UaServer:Model1” also contains a Node having the identifier “MyTemperature”, the client may not even notice that it accessed the wrong node when reading “ns=2;s=MyTemperature”.

namespace_table.png
Reading and storing the namespace table

The next chapter describes the OPC UA Subscription Concept.