Skip to content

Using the SiOME API

In the hello-world component of the siome plugin base project, the SiOME Plugin API is already correctly imported and instantiated. To make use of the different API functions, use the object siomeApi provided by the getter.

Logging

The SiOME API gives you access to the logging interface of SiOME. The log area does not display all log messages line by line, but instead displays them depending on their origin in a tree like structure. The root is called a log node, the leaves are the single log messages. In order to use the log interface, there has to be a log node for your plugin. You can create a log node with the following line of code:

await this.siomeApi.createLogNode("<<name>>");

With the placeholder <<name>>, you define the displayed name of the node in the log output area.

You can output a log message with the following code:

await this.siomeApi.newLogEntry("<<logEntry>>", "<<type>>");

For the placeholder <<log entry>>, you enter a log message and for <<type>> you enter one of three log types:

  • info
  • warning
  • error

Example 1

Goal

In this example we will add a new button to the plugin that creates a new log node and different log message leaf nodes.

Solution

hello-world.component.html
//...
<button style="width: 120px; margin: 8px;" mat-raised-button color="primary" (click)="addLogs()">
Add logs
</button>
//...
hello-world.component.ts
//...
async addLogs(){
await this.siomeApi.createLogNode("example log node");
await this.siomeApi.newLogEntry("This log message has the type info", "info")
await this.siomeApi.newLogEntry("This log message has the type warning", "warning")
await this.siomeApi.newLogEntry("This log message has the type error", "error")
}
//...

Result

After compiling and reloading the plugin, a new button got added to the GUI:

log output gui

Clicking the button produces the following log output:

log output example

Information modeling

By using the SiOME API methods, you are able to add, change and remove nodes from the information model displayed in the editor view. This allows you to automate lots of different tasks and steps of manual modeling through programming.

Example 1

Goal

In this example we will add a new button to the plugin that adds an Object inside the Objects folder of our information model. This folder is already defined by the OPC UA standard and has the node id ns=0;i=85. Folders group nodes by referencing them with an Organizes reference. This reference is also defined by the OPC UA standard and has the node id ns=0;i=35.

Solution

We use the addChild() method to add a node to the information model. The node to be added can be parameterized by passing a Parameter object. The type of this parameter object varies depending on the NodeClass. For example, to create a node of the Object NodeClass, you pass addChild() an object of the type IAddObjectParameter. For this object, the following properties must be specified:

First we create a new function and an IAddObjectParameter object after importing the corresponding object interface. To be able to refer to the node class by name, we also import the NodeClass enumeration:

hello-world.component.ts
//...
import { NodeClass } from 'src/app/shared/public-api/enums/node-classes';
import { IAddObjectParameter } from 'src/app/shared/public-api/interfaces/add-object-parameter';
//...
async addNode(){
   const params: IAddObjectParameter = {
   name: 'My Custom Node',
   nodeClass: NodeClass.Object,
   referenceType: 'ns=0;i=35',
   namespaceIndex: 1,
   typeDefinition: "ns=0;i=58"
   }
}
//...

In addition to the OPC UA base namespace, which always has namespace index 0, we will now use createNamespaceImplicit() to create our own namespace, into which we will add our node instances for the rest of this guide. For a new empty nodeset this namespace will have the namespace index 1 accordingly. To avoid creating the same namespace multiple times, we first check whether it already exists in the namespace array of the nodeset by using the method getNamespaceArray().

Then we call the addChild() method. If the node is added correctly, an IOpcReference is returned, and if an error occurs, an ISiomeApiError is thrown instead. The error can be handled with a try-catch statement or with promise methods.

hello-world.component.ts
//...
import { NodeClass } from 'src/app/shared/public-api/enums/node-classes';
import { IAddObjectParameter } from 'src/app/shared/public-api/interfaces/add-object-parameter';
//...
async addNode(){
    const params: IAddObjectParameter = {
      name: 'My Custom Node',
      nodeClass: NodeClass.Object,
      referenceType: 'ns=0;i=35',
      namespaceIndex: 1,
      typeDefinition: "ns=0;i=58"
    }

    const namespaceArray = await this.siomeApi.getNamespaceArray()
    if(namespaceArray.includes("my-new-namespace") === false ) {
      await this.siomeApi.createNamespaceImplicit("my-new-namespace", "0.1")
    }
    const returnValue = await this.siomeApi.addChild("ns=0;i=85", params)
  }
//...

Lastly we add a button that calls the new function:

hello-world.component.html
//...
<button
  style="width: 120px; margin: 8px;"
  mat-raised-button
  color="primary"
  (click)="addNode()"
>
  Add new Node
</button>
//...

Result

After compiling and reloading the plugin, a new button got added to the GUI:

addChild Button

Clicking the button results in a newly added node called MyCustomNode and can be found under the browse path Root/Objects/MyCustomNode.

addChild Result

Example 2

Goal

In this example we create a node of the Method node class with input arguments by using the addMethod() function, which can create OPC UA methods with input and output arguments.

Solution

For our example, we add a new function to our component. There we create an array of IMethodArguments, which contains the input arguments we want to define for the OPC UA method to be created.

hello-world.component.ts
//...
import { IMethodArguments } from 'src/app/shared/public-api/interfaces/method-arguments.interface';
//...
async addMethodNode(){
    const inputArgs: IMethodArguments[] = [
      {
      name: 'test',
      valueRank: 0,
      dataType: 'ns=0;i=24'
    }]
  }
//...

Then we call addMethod() and fill the parameters for the parentNodeId, the name of the method, the namespace index as well as the method input and output arguments.

hello-world.component.ts
//...
import { IMethodArguments } from 'src/app/shared/public-api/interfaces/method-arguments.interface';
//...
async addMethodNode(){
    const inputArgs: IMethodArguments[] = [
      {
      name: 'NumberArg1',
      valueRank: 0,
      dataType: 'ns=0;i=24'
    }]

    const namespaceArray = await this.siomeApi.getNamespaceArray()
    if(namespaceArray.includes("my-new-namespace") === false ) {
      await this.siomeApi.createNamespaceImplicit("my-new-namespace", "0.1")
    }
    await this.siomeApi.addMethod("ns=0;i=85", "Method1", 1,  inputArgs, [])
  }
//...

Lastly we add a button that calls the new function:

hello-world.component.html
//...
<button
  style="width: 120px; margin: 8px;"
  mat-raised-button
  color="primary"
  (click)="addMethodNode()"
>
  Add new MethodNode
</button>
//...

Result

After compiling and reloading the plugin, a new button got added to the GUI:

addMethod Button

Clicking the button results in a newly added method called Method1 and can be found under the browse path Root/Objects/Method1.

addMethod Result

Example 3

Goal

In this example we create a custom structured datatype.

Solution

In a new function, we use addChild() like previously in Example 1, to add a new datatype to the information model. In comparison to the first example, we use the HasSubtype (NodeId ns=0;i=45) reference to inherit from the datatype Structure (NodeId ns=0;i=22). Both the reference and the datatype are defined by the OPC UA standard.

hello-world.component.ts
//...
import { IAddDataTypeParameter } from 'src/app/shared/public-api/interfaces/add-data-type-parameter';
import { IOpcReference } from 'src/app/shared/public-api/interfaces/opc-reference.interface';
//...
async addStruct(){
    const params: IAddDataTypeParameter = {
      isAbstract: false,
      name: 'MyStruct',
      namespaceIndex: 1,
      nodeClass: NodeClass.DataType,
      referenceType: 'ns=0;i=45'
    }

    const namespaceArray = await this.siomeApi.getNamespaceArray()
   if(namespaceArray.includes("my-new-namespace") === false ) {
     await this.siomeApi.createNamespaceImplicit("my-new-namespace", "0.1")
   }
    const returnValue = await this.siomeApi.addChild("ns=0;i=22", params) as IOpcReference
    const returnedChildNodeId = JSON.parse(JSON.stringify(returnValue.target))
  }
//...

After creating the MyStruct datatype, we can use the addStructureItem() method, to populate the datatype with items. The item to be added gets parametrized by passing an IAddStructurItemParameter object:

hello-world.component.ts
//...
import { IAddDataTypeParameter } from 'src/app/shared/public-api/interfaces/add-data-type-parameter';
import { IOpcReference } from 'src/app/shared/public-api/interfaces/opc-reference.interface';
import { IAddStructurItemParameter } from 'src/app/shared/public-api/interfaces/add-structure-item-parameter.interface';
//...
async addStruct(){

    const params: IAddDataTypeParameter = {
     isAbstract: false,
     name: 'MyStruct',
     namespaceIndex: 1,
     nodeClass: NodeClass.DataType,
     referenceType: 'ns=0;i=45'
   }
   const namespaceArray = await this.siomeApi.getNamespaceArray()
   if(namespaceArray.includes("my-new-namespace") === false ) {
     await this.siomeApi.createNamespaceImplicit("my-new-namespace", "0.1")
   }
   const returnValue = await this.siomeApi.addChild("ns=0;i=22", params) as IOpcReference
   const returnedChildNodeId = JSON.parse(JSON.stringify(returnValue.target))

   const itemParam1: IAddStructurItemParameter = {
     name: 'item 1',
     dataType: 'ns=0;i=26',
     isOptional: false,
     valueRank: -1,
     allowSubTypes: false
   }
   const itemParam2: IAddStructurItemParameter = {
     name: 'item 2',
     dataType: 'ns=0;i=26',
     isOptional: false,
     valueRank: -1,
     allowSubTypes: false
   }
   await this.siomeApi.addStructureItem(returnedChildNodeId, itemParam1)
   await this.siomeApi.addStructureItem(returnedChildNodeId, itemParam2)
 }
//...

Lastly we add a button that calls the new function:

hello-world.component.html
//...
<button
  style="width: 120px; margin: 8px;"
  mat-raised-button
  color="primary"
  (click)="addStruct()"
>
  Add new Structure
</button>
//...

Result

After compiling and reloading the plugin, a new button got added to the GUI:

addStruct Button

Clicking the Button results in a newly added Structure subtype called MyStruct and can be found under the browse path Root/Types/DataTypes/BaseDataType/Structure/MyStruct.

addStruct Result

Get SiOME