Skip to content

Enable Access To OPC-UA

Introduction

The OPC-UA Adapter is an application used to connect the SINUMERIK Edge to OPC-UA servers.

Overview

The OPC-UA Adapter allows access of data from multiple OPC-UA servers. The OPC-UA Adapter can then be used in the following ways:

  • Accessing data via Parameter Service
  • Subscribing for data on change
  • Configuring the event processor in the OPC-UA Adapter to calculate new values based on the OPC-UA Data and access them through Parameter Service or subscription

In order to use data from the OPC-UA Adapter an application has to address the correct data points. In case the application uses the event processing feature, it additionally has to configure the OPC-UA Adapter to pre-process the data (See Event Processor).

Compatibility with OPC-UA Versions

The OPC-UA Adapter supports the following versions of the OPC-UA standard:

  • 1.00
  • 1.02
  • 1.03
  • 1.04

Data subscription

To subscribe data on change, the application has to configure the data requirements against the OPC-UA Adapter.

Here is an example configuration of an applicaiton using the OPC-UA Adapter (Subscription and Parameter Service):

{
  "specificConfig": {},
  "loggingConfig": {
    "appender": "JOURNAL_APPENDER",
    "severity": "INFO"
  },
  "datasourceConfig": {
    "requiredDatasource": [
      {
        "datasourceId": "OPCUAADAPTER1",
        "type": "OPCUA",
        "services": {
          "parameter-service/v1": {
            "access": [
              {
                "accessType": "r",
                "datapoints": [
                  {
                    "address": "ext::opcua::sinumerik::NS0|Numeric|2278"
                  }
                ]
              }
            ]
          },
          "subscription-service/v1": {
            "subscriptions": [
              {
                "messageId": "opcua_adapter_data",
                "messageName": "opcua_adapter_data",
                "quality": "quality_all",
                "datapoints": [
                  {
                    "address": "ext::opcua::sinumerik::NS0|Numeric|2278"
                  }
                ]
              }
            ]
          }
        }
      }
    ]
  }
}

Addressing Scheme

In order to access values from a connected OPC-UA server, there is an address required to identify the data item. The OPC-UA Adapter has two different address spaces:

  • OPC-UA value addresses
  • Internal (calculated) value addresses

OPC-UA Addresses

OPC-UA addresses have the following format:

ext::opcua::<Connection Name>::<OPC-UA Namespace>|<Node ID Type>|<Node ID>

The Connection Name is the name of the connection as provided in the configuration of the OPC-UA Adapter.

The OPC-UA Namespace is the OPC-UA namespace. Example NS2 for namespace 2.

The Node ID Type is the type of the node ID according to the OPC-UA standard. The following types are supported:

  • String
  • Numeric
  • Opaque

Internal (Calculated) Addresses

Calculated variable addresses have the following format:

ext::var_t::<Node ID>

The Node ID is a string which uniquely represents the calculated value. The value has to be calculated via the event processor.

See the Event Processor chapter for more detail.

Connecting to OPC-UA servers

In order to access OPC-UA data, the OPC-UA Adapter has to connect to at least one OPC-UA server.

Preconditions

To connect to an OPC-UA server, the server needs to be accessible in the machine network. If the OPC-UA server to connect to is the SINUMERIK OPC-UA server, then this server must be activated and configured in the SINUMERIK HMI.

Configuration

The OPC-UA connection configuration is part of the specific configuration of the OPC-UA Adapter. Here is an example:

"specificConfig": {
    "mca": [
        {
            "name": "mca.adapterconfig::opcua",
            "config": {
                "connections": [
                {
                    "name": "sinumerik",
                    "message_security_mode": "SignAndEncrypt",
                    "security_policy": "Basic256Sha256",
                    "url": "opc.tcp://192.168.100.200:4840", 
                    "login": {
                        "pw": "password",
                        "user": "username"
                    },
                    "pki": {
                       "client_cert": "Client certificate in PEM format",
                       "client_private_key": "Private key for the certificate in PEM format"
                    }
                  }
                ]
            }
        }
      ]
    }

MCA in this configuration stands for machine controller adapter. The section contains the configuration for all connections to the controller in the OPC-UA Adapter. On the SINUMERIK Edge 'mca' can contain only one item. The name property of this one item must be "mca.adapterconfig::opcua"

Within the connections section it is possible to configure multiple individual connections. Each of them has a name property. The names have to be unique. In this example the only one connection is named sinumerik. When data points of the OPC-UA Adapter are referenced, this name is used in the address string to identifiy the OPC-UA connection.

List of supported message securities:

  • None
  • Sign
  • SignAndEncrypt

List of supported security policies:

  • None
  • Basic128Rsa15
  • Basic256
  • Basic256Sha256

Basic128Rsa15 and Basic256 marked DEPRECATED in the standard therefor it is suggested to use Basic256Sha256 policy when encryption is necessary.

See OPC-UA specification for more detail about OPC-UA message security and security policies.

The "login" and "pki" properties are not mandatory. They are only required if the server needs them to be able to establish connection.

Certificate creation

When creating a client certificate for the adapter to be able to connect to the OPC-UA server two important properties has to be set: * subjectAltName=URI:urn:MachineAgent * Case sensitive so the "urn:MachineAgent" has to be the same always otherwise the agent fails to verify the given certificate. * authorityKeyIdentifier has to be included in the certificate file otherwise the agent fails to verify the certificate.

After creating the certificate and private key PEM files the content of the files has to be pasted into the configuration.

See OPC-UA specification for more detail about OPC-UA certificates.

Event processor documentation

The event processor allows us to configure and execute event processing programs to monitor the controller's state. These programs can contain multiple processor flows. Every flow executes different kind of operations and always started with a trigger.


Overview of processor types

Three main category is known by the Machine Agent. - Triggers - Triggers are the starting point of each processor program - They are responsible to start an execution flow within the Agent - Output port: - triggered - Filters - Filters can separate the flow sequence based on different conditions - They are capable of defining multiple outputs for the same processor program - Two output ports: - filtered - If the condition defined by the filter was not met - passed - If the condition defined by the filter was met
- Functions - These processors can execute different types of operations on specified data - They can execute mathematical operations on the data - They can gather or send data - One output port: - exec - DTS connectors - These processors are used, to transfer different types of data to the DTS - The DTS will then accummulate and then upload data to a server side application. - The target application will be determined based on the name of the Event Processor job.


How to create a processor

All processors must be encapsulated, in order to make each of them uniquely identifiable for linking.

{
  "name": "name",
  "processor": { ... },
  "links": [ ... ]
}
Parameters - **name**: Defines a unique identifier of the processor. - **processor**: Defines the exact processor. This field must be filled with a valid instance of a [Processor](#processors). - **links**: Defines which output(s) of the processor should trigger the execution of which subsequent processor(s).
For more information on how to link processors see paragraph [How to Link processors together](#how-to-link-processors-together).



Every processor type must produce an output value. In another way every processor must have one or more ports that can be used to create links between them. These links define a many-to-many relation between the processors which means that one processor can have multiple links to other ones and can be linked to more than one. Links always have to be placed in the configuration of the processor that is sending the output. We have to specify the port and the target processor's name that also exists in the program.


Event Processing

Event Processing is the mechanism that is used to gather and make basic manipulation on data.


Value Caching

Data that is being processed is always cached by the Event Processor to make it faster and to keep consistency during processing. There are 2 types of caches within the Event Processor. - Global Cache - Cycle Cache

Global Cache

Is used to constantly keep the latest values of variables quickly available for the Event Processor.
Values within the Global Cache are kept up-to-date by the MCA via its subscription mechanism. Changes made by a Processor to a variable's value will be detected by the Global Cache and the change will be propagated toward the MCA service, to be written to the machine.
Only those variables are stored in the Global Cache, which are referenced by at least one Processor. If a new processor is configured during runtime, which references a variable that was not yet referenced, will be automatically added to the cache.

Cycle Cache

Is mirrored from the Global Cache the moment a new Processing Cycle starts. This Cycle Cache will only store values that were, until the Cycle start, referenced.
Values of variables will NOT be changed in the Cycle Cache during the Cycle's execution time! Changes made to those variables will only take effect in the Global Cache, and only after the MCA service notifies it of the change! However changes to an internal variable's value will take effect in the Cycle Cache!
The Cycle Cache will be deleted only when the Cycle has completed.


Event Processor layers

Event processing is a complex operation, consisting of multiple layers. Each of these layers have a different set of responsibilities. The layers, from the bottom to the top, are: - Processors - Workflows - Programs - Cycles

Event Processor Processors

Processors are the basic building blocks within the Event Processor. Each processor instance is responsible for a single, well defined operation.
A Processor consists of a Processor and its Wrapper.
NOTE: Executing a processor more than once in a Processing Cycle will result in an error! To avoid this you can use the Once per cycle filter.

Event Processor Workflow

In most cases single processor cannot cover a whole functionality. Thus Processors can be linked together to create complex operations. Such an independent chain of processors is called a Workflow.

Event Processor Program

Each Application can freely define Workflows. To make handling these Workflows together easier, an Application can define multiple workflows within one Event Pocessor configuration. These Workflows then will be executed independently from one another.
Synchronising or data sharing between Workflows can be achieved using internal variables.
There can be 2 types of Event Processor Programs: - System Programs: Can only be defined by the server side entity called System. These programs are unique in a way, that they will be executed before the Application programs. - Application Programs: Can be freely defined by Applications.

Event Processor Cycle

The Event Processor executes workflows in cycles. When a new cycle starts a new Cycle Cache for values is created. This means, that value changes that are made by Workflows in the Cycle or changes that occur on the Machine will not be visible within the Cycle! This mechanism is there to ensure consistency of data throughout the whole Cycle.


Example program

The following program will create a susbcription on the specified variable change address (variable_change_trigger) and will gather data with the help of the value_set_builder on every value change. When the data is ready the data_sender will forward it through the configured dts service.

{
    "sys_programs": [],
    "app_programs": [{
        "app_name": "my_app",
        "config":{
            "processors": [
                {
                    "name": "on_change_simulated",
                    "processor": {                
                        "__t": "variable_change_trigger",            
                        "desired_update_cycle_ms": 300,
                        "address": "ext::tst_south::test/zerotohundred"
                    },
                    "links": [{
                        "port_name": "triggered",
                        "target_processor": "status_vs_builder"
                    }
                    ]
                },
                {
                    "name": "status_vs_builder",
                    "processor": {    
                        "__t": "value_set_builder",        
                        "value_set_name": "machine_status",
                        "values": [
                            {
                                "value_name": "status",
                                "address": "ext::tst_south::test/zerotohundred",
                                "desired_update_cycle_ms": 300
                            }
                        ]
                    },
                    "links": [
                        {
                            "port_name": "exec",
                            "target_processor": "data_sender_1"
                        }
                    ]
                },
                {
                    "name": "data_sender_1",
                    "processor": {                    
                        "__t": "data_sender",    
                        "channel_name": "data"
                    },
                    "links": []
                }
            ]
        }
    }]
}


Processors

Triggers

Triggers are used to start the execution of an Event Pocessor Workflow. Each trigger has one output port: - triggered: Is fired, when the trigger condition is met. E.g.: when the right time period has passed using a cyclic trigger.

Cyclic trigger

The cyclic trigger can be used to start a workflow periodically.

Configuration:

{
    "__t": "cyclic_trigger",
    "cycle_in_milliseconds": 2000
}
NOTE: The Machine Agent cannot guarantee normal operation when the cyclic trigger is set to be triggered in less 300ms!

Parameters - **cycle_in_milliseconds**: Defines the cycle time.


Variable change trigger

The variable change trigger executes when the value of a variable changes. The variable can be a calculated one, calculated by the Event Processor, or a data point provided by an external adapter.

Configuration:

{
    "__t": "variable_change_trigger",
    "desired_update_cycle_ms": 300,
    "address": "app::var_t::/oee_tmp"
}
Parameters - **desired_update_cycle_ms**: Defines the sample rate of the variables observed by the variable.
**NOTE**: This value may not be precisely followed by adapters, resulting in different update cycles than requested. - **address**: Defines the address of the variable, which value is going to be observed.


Startup trigger

The startup trigger executes once per startup of the Machine Agent, or the first time when an application's configuration takes effect. This type of trigger can be used to set default values or gather preliminary data from the controller.

Configuration:

{
    "__t": "startup_trigger"
}

Filters

Filters are used to introduce logical separation of the flow sequence based on different conditions. They can be used to produce different outputs by the same flow, based on certain input values. Each filter has 2 output ports: - filtered - Is fired when the condition, defined by the filter, was not met - passed - Is fired when the condition, defined by the filter, was met

Condition filter

The condition filter is used to evaluate an expression in a workflow.

Configuration

{
    "__t": "condition_filter",
    "desired_update_cycle_ms": 300,
    "condition": {...}
}
Parameters - **desired_update_cycle_ms**: Defines the sample rate of the variables observed by the expression.
**NOTE**: This value may not be precisely followed by adapters, resulting in different update cycles than requested. - **condition**: Defines the expression to be evaluated. See [Expressions](#expressions) chapter on how to define an expresion.


Once per cycle filter

The once per cycle filter can be used to execute the subsequent part of the flow only once in a processing cycle. For example, if you have multiple variable triggers triggering a processing cycle at once, then you can use this filter to ensure that the subsequent parts of the flow is only executed once.

Configuration

{
    "__t": "once_per_cycle_filter",
}

Functions

Functions are used for different auxilary operations on a set of data.
All functions have one output port, which is called: exec

Variable calculator

The variable calculator is used to set calculated variables. In system programs, the component can be used to store the result in glob::cache_value... global cache. All apps can write to ext::var_t::... or opc ua addresses, or their local caches. These are in turn readable by all application programs.

This is how a variable calculator is configured:

{
    "__t": "variable_calculator",
    "target_address": "ext::adapter::address",
    "desired_update_cycle_ms": 300,
    "expression": {...}
}
Parameters - **target_address**: Defines the address, where the calculated value is stored. - **desired_update_cycle_ms**: Defines the sample rate of the variables observed by the variable.
**NOTE**: This value may not be precisely followed by adapters, resulting in different update cycles than requested. - **expression**: Defines the expression to be evaluated. See chapter [Expressions](#expressions) chapter on how to define an expresion.


NOTE: Changing the value of a variable will not trigger a Variable change trigger within the same Processing Cycle! The only exceptions are variables stored in the Transient Storage.
For more information on addressing see Addressing.

Value set builder

  • The result port is always "exec".

Configuration:

{
    "__type": "value_set_builder",
    "value_set_name": "my_value_set",
    "values": [
        {
            "value_name": "my_value",
            "address": "ext::tst::address",
            "desired_update_cycle_ms": "100"
        }
    ]
}

Produced data structure:

Example
{
    "__type": "value_set",
    "name": "my_value_set",
    "time": {
        "server_time": {
          "value": "2021-03-12T12:18:09.961Z",
          "quality": "GLOBAL_TIME_INVALID"
        },
        "client_time": "2021-03-12T12:18:09.961Z",
        "steady_time": {
          "client_exec_serial_num": 0,
          "offset": 0
        }
      },   
    "values": [
        {
            "value_name": "my_value",
            "address": "ext::tst::address",
            "value": 10,
            "quality": "Good",                    
        }
    ]
}



DTS connectors

DTS Connectors are used to forward different types of data towards a server side application.
DTS is a robust data uploader, which is capable of buffering incoming data, in order to prevent data loss, in case of a slow network connection, or a short network outage.
DTS always uploads its content to the server using the name of the Event Processor job.

Data sender

Is used to upload variables, using different channels of the DTS.

Configuration

{
    "__t": "data_sender",
    "channel_name": "data"
}
Parameters - **channel_name**: Defines the channel name in the DTS through which the provided data will be uploaded to the server


The output port is exec.


Expressions

Expressions are used by certain processors, to define complex mathematical or logical functions. The Machine Agent supports the following types of expressions:

  • Unary expression
  • Binary expression
  • Const expression
  • Variable expression

Unary Expression

Unary expressions contain an operator and one operand.

! (not) is the only supported operator.

The operand can be any expression (unary, binary, const, variable).

Definition:

{
    "__t": "unary_expr",
    "operator": "!",
    "expr": {...}
}

Parameters - **operator**: Defines the operation to be executed using the two operands provided.
The **operator** accepts the following inputs: * not: '!' - **expr**: Defines the expression which result is going to be used by the operator.


Binary Expression

Binary expressions have one oparator and a left and right operands. Binary expressions are evaluated from the left to the right. Both operand can be any expression (unary, binary, const, variable).

Definition

{
    "__t": "binary_expr",
    "operator": "||",
    "left": {...},
    "right": {...}
}

Parameters - **operator**: Defines the operation to be executed using the two operands provided.
The **operator** accepts the following inputs: * plus: '+' * minus: '-' * multiply: '*' * divide: '/' * modulo: '%' * equals: '==' * greater or equal: '>=' * less or equal: '<=' * greater: '>' * less: '<' * not equal: '!=' * logical and: '&&' * logical or: '||' - **left**: The left side operand. It can be any expression (unary, binary, const, variable). This operand is evaluated first! - **right**: The right side operand. It can be any expression (unary, binary, const, variable). This operand is evaluated last!


NOTE: - Logical operators do not support short-circuiting! This means, that regardless of the result of the expression left, the expression right is going to be evaluated! - Operands are not type restricted! This means, that operators will try to cast operands into a common type, on which they could execute the operation.

Constant Expression

The constant expression is used to represent a constant value in an expression.

Definition:

{
    "__t": "const_expr",
    "value": 3
}

Parameters - **value**: The constant value we want to use in our expression.
The type of the constant can be integer, float or string.


Variable Expression

The variable expression is used to represent the current value of a variable (e.g. an SINUMERIK NC variable) in an expression.

Definition:

{
    "__t": "variable_expr",
    "address": "ext::tst::address"
}

Parameters - **address**: The address of the value which value we want to use in our expression.


Complete OPC-UA Adapter Config Example

Here is a complete example of an OPC-UA Adapter configuration:

{
  "specificConfig": {
    "machineAgentConfiguration": {
      "dts": {
        "configs": [
          {
            "app_name": "listenerapp",
            "config": {
              "channels": [
                {
                  "buffer_config": {
                    "files_per_folder": 100,
                    "size_in_mb": 500,
                    "size_per_file_in_mb": 2
                  },
                  "connection_name": "default",
                  "name": "info"
                },
                {
                  "buffer_config": {
                    "files_per_folder": 100,
                    "size_in_mb": 500,
                    "size_per_file_in_mb": 2
                  },
                  "connection_name": "default",
                  "name": "opcua_adapter_data"
                }
              ]
            }
          }
        ]
      },
      "eventproc": {
        "sys_programs": [],
        "app_programs": [
          {
            "app_name": "listenerapp",
            "config": {
              "processors": [
                {
                  "name": "on_change_opcuatest",
                  "processor": {
                    "__type": "variable_change_trigger",
                    "__t": "variable_change_trigger",
                    "desired_update_cycle_ms": 50,
                    "address": "ext::opcua::sinumerik::NS0|Numeric|2278"
                  },
                  "links": [
                    {
                      "port_name": "triggered",
                      "target_processor": "vsbuilder1"
                    }
                  ]
                },
                {
                  "name": "vsbuilder1",
                  "processor": {
                    "__type": "value_set_builder",
                    "__t": "value_set_builder",
                    "value_set_name": "example_vs",
                    "values": [
                        { 
                            "value_name": "example_value",
                            "address": "ext::opcua::sinumerik::NS0|Numeric|2278",
                            "desired_update_cycle_ms": 50
                        }
                    ]
                  },
                  "links": [
                    {
                      "port_name": "exec",
                      "target_processor": "data_sender_1"
                    }
                  ]
                },
                {
                  "name": "data_sender_1",
                  "processor": {
                    "__type": "data_sender",
                    "__t": "data_sender",
                    "channel_name": "opcua_adapter_data"
                  },
                  "links": []
                }
              ]
            }
          }
        ]
      },
      "mca": [
        {
          "name": "mca.adapterconfig::opcua",
          "config": {
            "connections": [
              {
                "login": {
                  "pw": "",
                  "user": ""
                },
                "message_security_mode": "None",
                "name": "sinumerik",
                "security_policy": "None",
                "url": "opc.tcp://192.168.100.200:4840"
              }
            ]
          }
        }
      ]
    }
  },
  "loggingConfig": {
    "appender": "JOURNAL_APPENDER",
    "severity": "INFO"
  },
  "datasourceConfig": {
    "providedDatasource": [
      {
        "datasourceId": "OPCUAADAPTER1",
        "type": "OPCUA",
        "services": {
          "parameter-service/v1": {
            "address": {
              "tcpPort": 23313,
              "containerId": "opcuaadapter"
            },
            "access": []
          },
          "subscription-service/v1": {
            "subscriptions": [
              {
                "messageId": "opcua_adapter_data",
                "quality": "quality_all",
                "merge": false,
                "datapoints": []
              }
            ]
          }
        }
      }
    ]
  }
}

Any questions left?

Ask the community


Except where otherwise noted, content on this site is licensed under the The Siemens Inner Source License - 1.1.