UA Bundle SDK .NET
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Events Groups Pages


The ModelManager allows the Application Developer to create classes that represent some portion of the Server address space and to Read from, Write to and Subscribe to the entire object in a single operation. The following steps illustrate what needs to be done to read the few variables which belong to the standard Server object which every Server supports.

A ModelManager object is thread-safe.

Step 1) Define a Model class

[UaTypeDefinition(NodeId=ObjectTypes.ServerType, NamespaceUri=Namespaces.OpcUa)]
internal partial class ServerModel
[UaInstanceDeclaration(BrowseName="NamespaceArray", NamespaceUri=Namespaces.OpcUa)]
public string[] NamespaceArray { get; set; }
public string[] ServerArray { get; set; }
public bool Auditing { get; set; }
public byte ServiceLevel { get; set; }
public ServerStatusModel ServerStatus { get; set; }

The attributes used in the example above are defined in the SDK.

The UaTypeDefinition attribute indicates that a class represents an ObjectType or VariableType in an OPC UA information model. The NodeId is the identifier portion of the NodeId which is combined with the NamespaceUri to construct an absolute NodeId for the type definition. If the NamespaceUri is not specified then the default OPC UA namespace is assumed.

The UaInstanceDeclaration indicates that a property corresponds to a child of the ObjectType or VariableType represented by the containing class. The BrowseName of the child can be explicitly specified. If it is omitted the name of the property is used as the BrowseName. If the NamespaceUri is not specified then the default OPC UA namespace is assumed.

Properties with the UaInstanceDeclaration attribute can be other classes with UaTypeDefinition attribute defined. When this is done, browse paths with multiple elements can be constructed to access values in the Server address space.

Step 2) Register the Model class with the ModelManager

m_session.Model.RegisterMappedObject(ObjectTypeIds.ServerType, typeof(ServerModel));

This step allows the application to use the TypeDefinitionId to determine if a suitable Model class exists. For example:

if (m_session.Model.IsMappedObject((NodeId)reference.TypeDefinition))

Step 3) Create a ModelHandle

ServerModel instance = new ServerModel();
ModelHandle handle = ModelMapper.GetModelHandle(m_session.NamespaceUris, instance);

ModelHandle is used to optimize access to the object so the GetModelHandle method should only be called once per instance. The ModelHandle can then be passed multiple times to the Read, Write and Subscribe methods. The instance associated with a ModelHandle is called the Model object.

The default behavior of the ModelMapper uses reflection; however, a Model class can implement the IModelMapper interface and provide optimized assessors for some or all of the class properties.

The ModelHandle.Mappings property provides information about how a Model class is mapped to a Server address space, including the BrowsePath, which is used to find the node in the address space that a class property is mapped to and the NodeId once that BrowsePath has been resolved.

Step 4) Perform opterations with the ModelHandle

List<StatusCode> errors = m_session.Model.Read((NodeId)reference.NodeId, handle, null);
ServerModel instance = handle.Instance as ServerModel;
ServerState state = instance.ServerStatus.State;

The code above reads the values for all of the mapped nodes and updates the Model object. When the operation completes, the values can be accessed by accessing the mapped properties on the Model object as shown.

If errors occurr, they are reported in the array returned from the call. Each element corresponds to an element in the ModelHandle.Mappings property. If an error occurs during a Read, then the corresponding property in the Model object is set to its default value.

If the ModelHandle is subscribed to, then all or part of the Model object is updated whenever an update from the Server arrives. Thread synchronization is taken care by locking the synchronization object provided in the Subscribe request before applying any updates to the Model object.