readme and fix in structured mode
This commit is contained in:
81
readme.md
Normal file
81
readme.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# OPC-UA to MQTT Bridge
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
* Take the tarball from the Release area and unpack it in the desired location on the edge device
|
||||||
|
* The systemctl service script assumes to find the application at `/home/alarm/KROHNE/opcua2mqtt-bridge`, so it is a good idea to put it there
|
||||||
|
* Create a configuration directory `/etc/opcua2mqtt-bridge` and place the adjusted `config.json` into it
|
||||||
|
* Enable the service using `sudo systemctl enable /home/alarm/KROHNE/opcua2mqtt-bridge/opcua2mqtt-bridge.service`
|
||||||
|
* Start the service using `sudo systemctl start opcua2mqtt-bridge`
|
||||||
|
* Check the log messages using `journalctl -f`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
``
|
||||||
|
{
|
||||||
|
"mqtt": {
|
||||||
|
"broker": "172.16.2.16",
|
||||||
|
"port": 1883,
|
||||||
|
"publishTopicPrefix": "opcua"
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"topic": "statistics",
|
||||||
|
"period": 60
|
||||||
|
},
|
||||||
|
"opcua": [
|
||||||
|
{
|
||||||
|
"enabled": "true",
|
||||||
|
"type": "flat",
|
||||||
|
"url": "opc.tcp://172.16.3.60:4840",
|
||||||
|
"name": "apl",
|
||||||
|
"period": 1.0,
|
||||||
|
"timeout": 1.0,
|
||||||
|
"nodes": [
|
||||||
|
{ "ns": 0, "n": "i=345", "d": "pv" },
|
||||||
|
{ "ns": 0, "n": "i=348", "d": "sv" },
|
||||||
|
{ "ns": 0, "n": "i=351", "d": "tv" },
|
||||||
|
{ "ns": 0, "n": "i=354", "d": "qv" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
``
|
||||||
|
|
||||||
|
The configuration object consists of three parts: `mqtt`, `stats` and `opcua`.
|
||||||
|
|
||||||
|
In `mqtt` the access to the broker is configuration and the prefix for the topics used by the statistics module and the actual bridge are defined.
|
||||||
|
Besides the above shown attributes the `login` and `password` for authentication at the broker and `ca`, `cert` and `key` for TLS connections to the broker are available. `ca`, `cert` and `key` contain the filenames including complete path of the particular files.
|
||||||
|
|
||||||
|
In `stats` the topic suffix and the period of statistics messages are defined. The complete suffix will be `${mqtt.publicTopicPrefix}/${stats.topic}`.
|
||||||
|
|
||||||
|
The section `opcua` contains a list of OPC-UA servers to be queried. Each entry can be enabled/disabled using the attribute `enabled`. The attribute `url` obviously has the URL to connect the server, `period` is the period to repeat queries and `timeout` is the timeout when talking to a server.
|
||||||
|
`name` identifies the particular server, it becomes part of the topic of the published MQTT messages. The attribute `type` defines whether the individual variables of an OPC-UA server shall be communicated in individual MQTT messages (`flat`) or all variables of a server in a single message (`structured`).
|
||||||
|
|
||||||
|
The attribute `nodes` finally contains the list of variables to be queried. It contains the namespace index (`ns`), the node-id (`n`) and an optional descriptive name (`d`). Namespace index and node-id can be determined using for instance UAExpert when browsing the server and navigating to the relevant variables. The descriptive name - if given, otherwise the display name of the variable is used - becomes in `flat` mode part of the topic, in `structured` mode it becomes an attribute name within the message.
|
||||||
|
|
||||||
|
In `flat` mode the final topic will be `${mqtt.publicTopicPrefix}/${opcua.name}/${opcua.node.ns}/${opcua.node.d}`
|
||||||
|
|
||||||
|
An example for the MQTT messages according to the above configuration in `flat` mode is:
|
||||||
|
|
||||||
|
``
|
||||||
|
opcua/apl/0/pv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "pv", "value": 19.849281311035156}
|
||||||
|
opcua/apl/0/sv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "sv", "value": 1688.5152587890625}
|
||||||
|
opcua/apl/0/tv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "tv", "value": 22.574615478515625}
|
||||||
|
opcua/apl/0/qv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "qv", "value": NaN}
|
||||||
|
opcua/apl/0/pv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "pv", "value": 19.849281311035156}
|
||||||
|
opcua/apl/0/sv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "sv", "value": 1688.5152587890625}
|
||||||
|
opcua/apl/0/tv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "tv", "value": 22.574615478515625}
|
||||||
|
opcua/apl/0/qv {"serverName": "apl", "nameSpaceIndex": 0, "variableName": "qv", "value": NaN}
|
||||||
|
``
|
||||||
|
|
||||||
|
In `structured` mode the final topic will be `${mqtt.publicTopicPrefix}/${opcua.name}`
|
||||||
|
|
||||||
|
An example for the MQTT messages according to the above configuration in `flat` mode is:
|
||||||
|
|
||||||
|
``
|
||||||
|
opcua/apl {"pv": 19.844480514526367, "sv": 1689.9193115234375, "tv": 22.68524169921875, "qv": NaN}
|
||||||
|
opcua/apl {"pv": 19.844480514526367, "sv": 1689.9193115234375, "tv": 22.68524169921875, "qv": NaN}
|
||||||
|
opcua/apl {"pv": 19.844480514526367, "sv": 1689.9193115234375, "tv": 22.68524169921875, "qv": NaN}
|
||||||
|
opcua/apl {"pv": 19.844480514526367, "sv": 1689.93701171875, "tv": 22.620391845703125, "qv": NaN}
|
||||||
|
``
|
||||||
|
|
@ -17,7 +17,7 @@ class OpcUaRequester(threading.Thread):
|
|||||||
self.name = self.config['name']
|
self.name = self.config['name']
|
||||||
self.url = self.config['url']
|
self.url = self.config['url']
|
||||||
self.nodes = self.config['nodes']
|
self.nodes = self.config['nodes']
|
||||||
self.delay = self.config['delay']
|
self.period = self.config['period']
|
||||||
self.timeout = self.config['timeout']
|
self.timeout = self.config['timeout']
|
||||||
self.dataObjectType = self.config['type']
|
self.dataObjectType = self.config['type']
|
||||||
self.flat = self.dataObjectType == 'flat'
|
self.flat = self.dataObjectType == 'flat'
|
||||||
@ -49,7 +49,7 @@ class OpcUaRequester(threading.Thread):
|
|||||||
logger.error(f"UaError in inner OPC-UA loop: {type(e)} {e}")
|
logger.error(f"UaError in inner OPC-UA loop: {type(e)} {e}")
|
||||||
if not self.flat:
|
if not self.flat:
|
||||||
self.queue.put(dataObject)
|
self.queue.put(dataObject)
|
||||||
await asyncio.sleep(self.delay)
|
await asyncio.sleep(self.period)
|
||||||
except asyncio.exceptions.TimeoutError as e:
|
except asyncio.exceptions.TimeoutError as e:
|
||||||
self.stats.incOpcUaTimeouts()
|
self.stats.incOpcUaTimeouts()
|
||||||
logger.error(f"Timeout in inner OPC-UA loop")
|
logger.error(f"Timeout in inner OPC-UA loop")
|
||||||
|
@ -6,10 +6,10 @@ from AbstractDataObject import AbstractDataObject
|
|||||||
class StructuredDataObject(AbstractDataObject):
|
class StructuredDataObject(AbstractDataObject):
|
||||||
def __init__(self, topicPart):
|
def __init__(self, topicPart):
|
||||||
super().__init__(topicPart)
|
super().__init__(topicPart)
|
||||||
self.keyValuePairs = []
|
self.keyValuePairs = {}
|
||||||
|
|
||||||
def add(self, key, value):
|
def add(self, key, value):
|
||||||
self.keyValuePairs.append({key: value})
|
self.keyValuePairs[key] = value
|
||||||
|
|
||||||
def getPayload(self):
|
def getPayload(self):
|
||||||
return json.dumps(self.keyValuePairs)
|
return json.dumps(self.keyValuePairs)
|
||||||
|
@ -11,10 +11,10 @@
|
|||||||
"opcua": [
|
"opcua": [
|
||||||
{
|
{
|
||||||
"enabled": "true",
|
"enabled": "true",
|
||||||
"type": "flat",
|
"type": "structured",
|
||||||
"url": "opc.tcp://172.16.3.60:4840",
|
"url": "opc.tcp://172.16.3.60:4840",
|
||||||
"name": "apl",
|
"name": "apl",
|
||||||
"delay": 1.0,
|
"period": 1.0,
|
||||||
"timeout": 1.0,
|
"timeout": 1.0,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{ "ns": 0, "n": "i=345", "d": "pv" },
|
{ "ns": 0, "n": "i=345", "d": "pv" },
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"type": "flat",
|
"type": "flat",
|
||||||
"url": "opc.tcp://192.168.254.5:4863",
|
"url": "opc.tcp://192.168.254.5:4863",
|
||||||
"name": "sh",
|
"name": "sh",
|
||||||
"delay": 1.0,
|
"period": 1.0,
|
||||||
"timeout": 10.0,
|
"timeout": 10.0,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{ "ns": 1, "n": "s=t|SERVER::A201CD124/MOT_01.AV_Out#Value", "d": "A201CD124" },
|
{ "ns": 1, "n": "s=t|SERVER::A201CD124/MOT_01.AV_Out#Value", "d": "A201CD124" },
|
||||||
|
Reference in New Issue
Block a user