# Connecting & Integrating a SICK RFID Sensor via SOPAS

This guide teaches you how to integrate a SICK sensor device. In more detail, the following topics are covered:

* Identifying SOPAS commands
* Creating the [service commissioning file](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files)
* Specifying MQTT mappings
* Establishing a connection via the Connectware [Admin UI](https://docs.cybus.io/2-0-6/getting-started/admin-ui)
* Verifying data in the [Data Explorer](https://docs.cybus.io/2-0-6/documentation/monitoring/data-explorer)
* Utilizing the [Rule Engine](https://docs.cybus.io/2-0-6/documentation/services/rule-engine)

The service commissioning file used in this guide is available on [Cybus GitHub](https://github.com/cybusio/example-how-to-connect-and-use-sopas-device).

## Prerequisites

To follow this guide, you will need the following:

* A running instance of Cybus Connectware.
* Access to the [Admin UI](https://docs.cybus.io/2-0-6/getting-started/admin-ui) with sufficient [user permissions](https://docs.cybus.io/2-0-6/documentation/user-management).
* Basic knowledge of MQTT and the Connectware [services](https://docs.cybus.io/2-0-6/documentation/services) concept (e.g. [service commissioning files](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files), [connections](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files/resources/cybus-connection), and [endpoints](https://docs.cybus.io/2-0-6/documentation/services/service-commissioning-files/resources/cybus-endpoint)).
* For using the [Rule Engine](https://docs.cybus.io/2-0-6/documentation/services/rule-engine), a basic knowledge of [JSON](https://www.json.org/) and [JSONata](https://jsonata.org/) is useful.

## Example Setup for This Guide

Our setup for this guide consists of a SICK RFU620 RFID reading device. RFID stands for "Radio Frequency Identification" and enables reading from (and writing to) small RFID tags using radio frequency. Our SICK RFU620 is connected to an Ethernet network on a static IP address, in this guide referred to as "myIPaddress". We also have Connectware running in the same network.

## About the SOPAS Protocol

The protocol used to communicate with SICK sensors is the SOPAS command language, which utilizes command strings (telegrams) and comes in two protocol formats: CoLa A (Command Language A) with ASCII telegram format, and CoLa B with binary telegram format (not covered here). Often, the terms SOPAS and CoLa are used interchangeably, although strictly speaking, we will send the SOPAS commands over the CoLa A protocol format. In this guide, we use the CoLa A format only, as this is supported by our example sensor RFU620. Some SICK sensors only support CoLa A, others only CoLa B, and yet others support both.

The SICK configuration software [SOPAS ET](https://www.sick.com/us/en/p/p367244) also utilizes the SOPAS protocol to change settings of a device and retrieve data. The telegrams used for the communication can be monitored with SOPAS ET's integrated terminal emulator. Additional documentation with telegram listings and descriptions of your device can be obtained from SICK, either on their website or on request.

## Identifying SOPAS Commands

For the integration, we will need three pieces of information about the SOPAS commands we want to utilize:

1. **Interface type**: This part can be a bit tricky because the terminology used in device documentation may vary. The telegram listing of your device will probably group telegrams into events, methods, and variables, but sometimes you won't find the term "variable" but only the descriptions "Read"/"Write".
2. **Command name**: Every event, method, or variable is addressed using a unique string.
3. **Parameters**: In case of variable writing or method calling, some parameters may be required.

The three interface types mainly have the following purposes:

* **Events** can be subscribed to and will provide asynchronous messages.
* **Methods** can be called and will be executed by the device.
* **Variables** can be read or written, for example, to adjust the configuration of the device.

The telegram listing from your device's documentation is the most important source of this information. But to get a hint of the structure of telegrams, we will take a short look at it.

For example, a command string for the RFU620 that we monitored with SOPAS ET's integrated terminal emulator could look like this:

{% code lineNumbers="true" %}

```
sMN TAreadTagData +0 0 0 0 0 1
```

{% endcode %}

The first parameter in this string is the command type, which in case of a request can be one of the following relevant values:

| Value | Command type       | Interface type |
| ----- | ------------------ | -------------- |
| sRN   | Read               | variable       |
| sWN   | Write              | variable       |
| sMN   | Method call        | method         |
| sEN   | Event subscription | event          |

The command type is `sMN` (where M stands for "method call", and N for the naming scheme "by name" as opposed to "by index"). This command name `TAreadTagData` enables us to read data from an RFID tag. Following the command name, there are several space-separated parameters for the method call, for example, the ID of the tag to read from. In this case, we could extract the name `TAreadTagData` and the type method from the command string for our service commissioning file, but we still don't know the meaning of each parameter, so we have to consult the device's telegram listing.

For this guide, we have identified the following commands of our RFID sensor:

| Name          | Type     | Parameters  | Description           |
| ------------- | -------- | ----------- | --------------------- |
| QSinv         | event    | -           | Inventory             |
| MIStartIn     | method   | -           | Start inventory       |
| MIStopIn      | method   | -           | Stop inventory        |
| QSIsRn        | variable | -           | Inventory running     |
| HMISetFbLight | method   | color, mode | Switch feedback light |

## Writing the Service Commissioning File

The service commissioning file contains all connection and mapping details and is read by Connectware. To get started, open a text editor and create a new file, e.g., `sopas-example-commissioning-file.yml`. The service commissioning file is in YAML format. We will now go through the process of defining the required sections for this example:

* Description
* Metadata
* Parameters
* Resources

### Description and Metadata

These sections contain more general information about the service commissioning file. You can give a short description and add a stack of metadata. Regarding the metadata, only the name is required while the rest is optional. We will just use the following set of information for this guide:

{% code lineNumbers="true" %}

```yaml
description: >
  SICK SOPAS Example Commissioning File for RFU620
  How to connect and use a SICK RFID sensor via SOPAS

metadata:
  name: SICK SOPAS Example
  version: 1.0.0
  icon: https://www.cybus.io/wp-content/uploads/2019/03/Cybus-logo-Claim-lang.svg
  provider: cybus
  homepage: https://www.cybus.io
```

{% endcode %}

### Parameters

Parameters allow the user to customize service commissioning files for multiple use cases by referring to them from within the service commissioning file. Each time a service commissioning file is applied or reconfigured in Connectware, the user is asked to enter custom values for the parameters or to confirm the default values.

{% code lineNumbers="true" %}

```yaml
parameters:
  IP_Address:
    description: IP address of the SICK device
    type: string
    default: mySICKdevice

  Port_Number:
    description: Port on the SICK device. Usually 2111 or 2112.
    type: number
    default: 2112
```

{% endcode %}

We define the host address details of the SICK RFU620 device as parameters, so they are used as default, but can be customized in case we want to connect to a different device.

### Resources

In the resources section, we declare every resource that is needed for our application. The first resource we need is a connection to the SICK RFID sensor.

#### Cybus::Connection

{% code lineNumbers="true" %}

```yaml
resources:
  sopasConnection:
    type: Cybus::Connection
    properties:
      protocol: Sopas
      connection:
        host: !ref IP_Address
        port: !ref Port_Number
```

{% endcode %}

After giving our resource a name – for the connection it is `sopasConnection` – we define the type of the resource and its type-specific properties. In the case of `Cybus::Connection`, we declare which protocol and connection parameters we want to use. For the definition of our connection, we refer to the earlier declared parameters `IP_Address` and `Port_Number` by using `!ref`.

#### Cybus::Endpoint

The next resources needed are the endpoints which we supply data to or request data from. These are identified by the command names that we have selected earlier. Let's add each SOPAS command by extending our list of resources with some endpoints.

{% code lineNumbers="true" %}

```yaml
inventory:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    subscribe:
      name: QSinv
      type: event

inventoryStart:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    write:
      name: MIStartIn
      type: method

inventoryStop:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    write:
      name: MIStopIn
      type: method

inventoryCheck:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    read:
      name: QSIsRn
      type: variable

inventoryRunning:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    subscribe:
      name: QSIsRn
      type: variable

feedbackLight:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    write:
      name: HMISetFbLight
      type: method
```

{% endcode %}

Each resource of the type `Cybus::Endpoint` needs a definition of the used protocol and on which connection it is rooted. Here you can easily refer to the previously declared connection by using `!ref` and its name. To define a SOPAS command, we need to specify the desired operation as a property, which can be read, write, or subscribe, and among this, the command name and its interface type. The available operations depend on the interface type:

| Type     | Operation | Result                                     |
| -------- | --------- | ------------------------------------------ |
| event    | read      | n/a                                        |
|          | write     | n/a                                        |
|          | subscribe | Subscribes to asynchronous messages        |
| method   | read      | n/a                                        |
|          | write     | Calls a method                             |
|          | subscribe | Subscribes to method's answers             |
| variable | read      | Requests the actual value of the variable  |
|          | write     | Writes a value to the variable             |
|          | subscribe | Subscribes to the results of read-requests |

This means our endpoints are now defined as follows:

* `inventory` subscribes to asynchronous messages of `QSinv`
* `inventoryStart` calls the method `MIStartIn`
* `inventoryStop` calls the method `MIStopIn`
* `inventoryCheck` triggers the request of the variable `QSIsRn`
* `inventoryRunning` receives the data from `QSIsRn` requested by `inventoryCheck`
* `feedbackLight` calls the method `HMISetFbLight`

The `accessMode` is not a required property for SOPAS endpoints and is by default set to 0. But if you want to access specific SOPAS variables for write access which require a higher `accessMode` than the default 0 (zero), look up the suitable `accessMode` and its password in the SICK device documentation. Regarding the `accessMode`, Connectware supports the following values:

| Value | Name              |
| ----- | ----------------- |
| 0     | Always (Run)      |
| 1     | Operator          |
| 2     | Maintenance       |
| 3     | Authorized Client |
| 4     | Service           |

#### Cybus::Mapping

Up to this point, we would already be able to read values from the SICK RFID sensor and monitor them in the Data Explorer or on the default MQTT topics related to our service. To achieve a data flow that would satisfy the requirements of our integration purpose, we may need to add a mapping resource to publish the data on topics corresponding to our MQTT topic structure.

{% code lineNumbers="true" %}

```yaml
mapping:
  type: Cybus::Mapping
  properties:
    mappings:
      - subscribe:
          endpoint: !ref inventory
        publish:
          topic: 'sick/rfid/inventory'
      - subscribe:
          topic: 'sick/rfid/inventory/start'
        publish:
          endpoint: !ref inventoryStart
      - subscribe:
          topic: 'sick/rfid/inventory/stop'
        publish:
          endpoint: !ref inventoryStop
      - subscribe:
          topic: 'sick/rfid/inventory/check'
        publish:
          endpoint: !ref inventoryCheck
      - subscribe:
          endpoint: !ref inventoryRunning
        publish:
          topic: 'sick/rfid/inventory/running'
      - subscribe:
          topic: 'sick/rfid/light'
        publish:
          endpoint: !ref feedbackLight
```

{% endcode %}

The mapping defines which endpoint's value is published on which MQTT topic, or the other way around, which MQTT topic will forward commands to which endpoint. In this example, we could publish an empty message on topic `sick/rfid/inventory/start` to start RFID reading and publish an empty message on topic `sick/rfid/inventory/stop` to stop the reading process. While the reading (also referred to as inventory) is running, we continuously receive messages on topic `sick/rfid/inventory` containing the results of the inventory. Similarly, when publishing an empty message on `sick/rfid/inventory/check` while having subscribed to `sick/rfid/inventory/running`, we will receive a message indicating if the inventory is running or not.

To provide parameters for variable writing or method calling, you have to send them as a space-separated string. For instance, to invoke the method for controlling the integrated feedback light of the device, just publish the following MQTT message containing a color and a mode parameter on topic `sick/rfid/light`: `"1 2"`

## Installing the Service Commissioning File

1. Install the service commissioning file. See [Installing Services](https://docs.cybus.io/2-0-6/documentation/services/setting-up-and-configuring-services/installing-services).
2. Enable the service. See [Enabling Services](https://docs.cybus.io/2-0-6/documentation/services/setting-up-and-configuring-services/enabling-services).

**Result**: The service is enabled. This results in the installation of a service that manages all the resources we have just defined. This includes the SOPAS connection, the endpoints that collect data from the device and the mapping that controls where this data can be accessed.

After enabling this service, you can verify that everything works correctly.

## Verifying the Data

Although it is not represented by the [Data Explorer](https://docs.cybus.io/2-0-6/documentation/monitoring/data-explorer), on MQTT topics the data is provided in JSON format, and applications consuming the data must take care of JSON parsing to pick the desired property. The data is provided in JSON format, and messages published on MQTT topics contain the keys `timestamp` and `value`.

{% code lineNumbers="true" %}

```json
{
  "timestamp": 1581949802832,
  "value": "sSN QSinv 1 0 8 *noread* 0 0 0 0 0 AC"
}
```

{% endcode %}

This particular example is an inventory message published on topic `sick/rfid/inventory`. You recognize its `value` is a string in the form of the SOPAS protocol containing some values and parameters. This is the original message received from the SICK device, which means you still have to parse it according to SOPAS specifications. Connectware offers a feature that can easily help you along with this task, and we will take a look at it in the next step.

## Utilizing the Rule Engine

For this guide, we will demonstrate the concept of the Rule Engine with a simpler example than the above. We will look at the answer to a variable read request, which is more intuitive to read since those messages usually only contain the variable's value. For instance, this is the answer to `inventoryCheck` on endpoint `inventoryRunning`:

{% code lineNumbers="true" %}

```json
{
  "timestamp": 1581950874186,
  "value": "sRA QSIsRn 1"
}
```

{% endcode %}

The SOPAS string contains just the command type (`sRA` = read answer), the variable name (`QSIsRn`), and the value `1` indicating that the inventory is running. But essentially, we care about the value, because the command type is no further interesting for us and the information about the variable name/meaning is implied in the topic (`sick/rfid/inventory/running`). If this message is so easy to interpret for us, it cannot be too complicated for Connectware. We just need the right tools! And that is where the Rule Engine comes into play.

A rule is used to perform powerful transformations of data messages right inside the Connectware data processing. It enables us to parse, transform, or filter messages on the basis of simple-to-define rules, which we can append to endpoint or mapping resource definitions. We will therefore extend the `inventoryRunning` resource in our service commissioning file as follows:

{% code lineNumbers="true" %}

```yaml
inventoryRunning:
  type: Cybus::Endpoint
  properties:
    protocol: Sopas
    connection: !ref sopasConnection
    subscribe:
      name: QSIsRn
      type: variable
    rules:
      - transform:
          expression: |
            {
              "timestamp": timestamp,
              "value": $number($substringAfter(value, "sRA QSIsRn "))
            }
```

{% endcode %}

We add the property `rules` and define a rule of the type `transform` by giving the `expression`, which is a string in the form of JSONata language. Rules of the type `transform` are direct transformations, generating for every input message exactly one output message. The principle of the expression is that you construct an output JSON object in which you can refer to keys of the input object (the JSON message of this endpoint) and apply a set of functions to modify the content. Note that the pipe `|` is not part of the expression but a YAML-specific indicator for multiline strings, denoting to keep newlines as newlines instead of replacing them with spaces.

We want to keep the form of the input JSON object, so we again define the key "timestamp" and reference its value to `timestamp` of the input object to maintain it. We also define the key "value", but this time we do some magic utilizing JSONata functions:

* `$number(arg)` casts the arg parameter to a number if possible
* `$substringAfter(str, chars)` returns the substring after the first occurrence of the character sequence chars in str

The second function will reduce the SOPAS string we refer to with `value` by returning just the characters after the string "sRA QSIsRn", in our case a single digit, which is then cast to a number by the first function. We can apply these functions in this way because we know that the string before our value will always look the same. For more information about JSONata and details about the functions, see <https://docs.jsonata.org/overview.html>.

After installing the new Service with the modified service commissioning file, we now receive the following answer to an `inventoryCheck` request (while inventory is running):

{% code lineNumbers="true" %}

```json
{
  "timestamp": 1581956395025,
  "value": "1"
}
```

{% endcode %}

This value is now ready-to-use, and applications working with this data do not have to care about any SOPAS parsing.

Connectware supports some more types of rules. Here is a quick overview:

* **parse** – parse any non-JSON data to JSON
* **transform** – transform payloads into a new structure
* **filter** – break the message flow if the expression evaluates to a false value
* **setContextVars** – modify context variables
* **cov** – Change-on-Value filter that only forwards data when it has changed
* **burst** – burst-mode that allows aggregation of many messages
* **stash** – stash intermediate states of messages for later reference

## Summary

We started this guide with a few SOPAS basics and learned which information about the SOPAS interface is required to define the service commissioning file for the integration of our SICK RFU620. Then we created the service commissioning file and specified the SOPAS connection, the endpoints, and the MQTT mapping.

Utilizing the service commissioning file, we installed a service managing those resources in Connectware and monitored the data of our device in the Data Explorer of the Admin UI. In the end, we had a quick look at possibilities of preprocessing data using the Rule Engine to get ready-to-use values for our application.
