ObjectTypes are a very common modelling concept in OPC UA. Typically, each Companion Specification defines one or more ObjectTypes. Defining an ObjectType makes sense when it is expected that the ObjectType is used several times, either in one Server or across various Servers to provide interoperability on the model.

An ObjectType can be simple without defining any substructure, like the FolderType of the base specification. Typically, an ObjectType defines a substructure with Objects, Variables and / or Methods.

When defining an ObjectType, it needs to be considered if there is already an appropriate ObjectType it can derive from. That includes the defined semantic of the ObjectType as well as the defined substructure. The ObjectType may contain an optional substructure that is not needed but should not have any mandatory substructure that is not wanted / needed. Not only ObjectTypes of the base specification should be considered, but also ObjectTypes defined in existing Companion Specifications. The OPC Foundation provides an online search on all specifications (https://reference.opcfoundation.org/). When there is no more appropriate ObjectType defined, the BaseObjectType shall be used as base (as defined in the OPC UA Specification).

When designing a type hierarchy, the base type(s) may either be abstract or concrete.

Design decision in favour of abstract types: The main type is a template that contains all the common elements of the child types but is not “whole” itself, thus shall never be instantiated. Example: a biological parent is either a mother or a father and in the application context it makes no sense to instantiate “parent” without this information. However, both mother and father are parents.

Design decision in favour of concrete types: The super type might directly be used if no more detailed information exists. Example: To know how long a tool has been used, only the time and identifier are needed. A base type for the tool can contain only those two. For other use cases, more specific tools like drills, saws or punches may be defined, adding specific information.

Potentially, not only one ObjectType needs to be defined, but many. In this case, consider creating base ObjectTypes for common semantics and substructures.

On the other hand, it is not practical to have a very deep type hierarchy with many levels. In addition OPC UA only supports single inheritance (multiple inheritance is not supported), thus an ObjectType can be derived from only one supertype so the type hierarchy can only be organized by one aspect.

If additional aspects need to be added, composition must be used. Composition can be either done by:

  • an AddIn (see OPC 10000-3), having an Object as substructure defined by an ObjectType providing a standardized BrowseName (DefaultInstanceBrowseName) and adding the aspect as defined by the AddIn-ObjectType or by
  • an Interface (see OPC 10000-3), defining a substructure that is directly deployed (without grouping Object) on the ObjectType.

Composition allows definition of an aspect that can be deployed on various places in the type hierarchy, or even only on specific instances.

Therefore, subtyping should be used to refine the main characteristic of Objects of the ObjectType, and additional aspects should be defined by composition.

For example, the type hierarchy may define different types of devices (SensorType as common base type and derived a TemperatureSensorType, FlowSensorType, etc.) whereas additional aspects like the capability to lock an Object are defined by AddIns or Interfaces and added to the types.

However, even the above-mentioned example can lead to problems without multiple inheritance, as a device may support measuring temperature and flow. In this case, also the characteristic to measure a temperature and a flow should be modelled by composition (see PA-DIM for an example).

When defining a substructure of an ObjectType addressing different aspects (e.g. Identification information, measurement values, configuration parameters) everything can be referenced directly from the ObjectType, or grouping Objects can be used to address the different aspects.

image005.png

Figure 2 – Example of grouping Objects

From a pure programmatical access perspective no grouping information is required, as all individual subcomponents have their standardized names. However, from a human-readability perspective, having grouping objects increases the understandability of the provided substructure.

Grouping Objects make sense:

As a general rule of thumb, when the ObjectType has more than 30 subcomponents, having grouping Objects may be helpful. This also applies inside such a grouping Object, i.e., if the grouping Object would have more than 30 subcomponents, grouping the subcomponents with Objects as subcomponents of the grouping Object make sense. When applying the rule, potential subtypes should be considered. For example, if a base ObjectType only addresses identification but it is expected that subtypes with additional substructures are defined, it makes sense to already group the identification.

When using grouping Objects, it must be considered what ObjectType the grouping Object should have. If it groups an aspect that is expected to be used only in one place (the ObjectType the grouping Object is used), it is not necessary to create a specific ObjectType for the grouping Object, and instead it is recommended to use the FolderType. If the grouping may be used in various places, it is recommended to define an ObjectType (as AddIn), allowing to use the same grouping easily in other places.

The question whether to define an Interface or AddIn has two aspects.

  1. The interface is intentionally a simple and therefore limited construct (see OPC 10000-3). Therefore, depending on what needs to be defined an interface may not be an option.
  2. An AddIn implies a grouping Object with a standardized default name, whereas an interface can be deployed without grouping Object. Of course, even with an interface an additional grouping Object can be created and the interface gets deployed on the grouping Object.

Therefore, it is recommended to use an interface if something rather small and simple is defined (with maybe a handful of Variables and Methods) that potentially should be deployed directly on an ObjectType (without grouping Object).

In all other cases, it is recommended to define an AddIn. Potentially, an Interface can be defined and in addition an AddIn using the Interface.

As a general consideration:

  • From a consumer (client) perspective, making a functionality mandatory, like a Method or Variable, is desirable because then the consumer can rely on its availability.
  • From a provider (server) side making functionality mandatory can be challenging, if it cannot be provided in all cases. Therefore, as a compromise functionally is often defined as optional.

There are two ways to deal with optional functionality. One obvious way is to define the InstanceDeclaration as optional, and then the server may or may not provide the functionality.

A second option is, at least for variables, to define a specific default value implying that the information is not provided. Depending on the DataType, this might be the NULL value (if the DataType provides a NULL value (see OPC 10000-6)). Or it might be a specific value like “-1” or an empty string.

It is recommended to use the Optional ModellingRule if the provider does not support the functionality at all. If it is a Variable, where the value can also be written and thus the information can be provided by the Client, it is recommended to use the Mandatory ModellingRule and define a default behaviour, if the functionality is not available. If the Variable is not there at all, the Client does not have an option to write a value into the Variable.

Note that as an additional option, it would also be possible to have mandatory Variables and Methods, that just provide a BAD status code if not supported. It is not recommended to use this approach.

Note that optional functionality can be made mandatory by conformance units and profiles (see 3.3). In this case, the Server implementation can decide whether the conformance units or profiles are supported. Clients can discover the supported conformance units and profiles of a Server.

When an ObjectType needs to expose several subcomponents of a specific type without defining the exact number or the BrowseName, the MandatoryPlaceholder (at least one) or the OptionalPlaceholder ModellingRules are used. Often, this might be combined with a grouping Object in order to easily browse all subcomponents of that type.

When an ObjectType requires at least one subcomponent of the specific type, the grouping Object must be Mandatory and the subcomponent of the specific type must use MandatoryPlaceholder.

When the ObjectType is not required to have at least one subcomponent, options are:

  1. The grouping Object is Mandatory and the subcomponent uses OptionalPlaceholder
  2. The grouping Object is Optional and the subcomponent uses MandatoryPlaceholder
  3. The grouping Object is Optional and the subcomponent uses OptionalPlaceholder

When the grouping Object has additional functionality like a Method to add to its subcomponents it is recommended to make the grouping Object Mandatory and therefore the subcomponent has to use OptionalPlaceholder (Option 1).

When the grouping Object does not have any additional functionality like a Method to add to subcomponents, it is recommended to make the grouping Object optional so a client does not have to browse the empty grouping Object to figure out that it is empty. In this case, it makes sense to define the subcomponent as MandatoryPlaceholder so the grouping Object is only available when a subcomponent is included (Option 2).

However, when the model is built to be extended and a subtype may add additional functionality to the grouping Object, defining the subcomponent as OptionalPlaceholder allows subtypes to change the ModellingRule of the grouping Object without requiring a subcomponent of the specific type (Option 3).