Legacy Protocol Converter is a framework designed to convert messages from one protocol to another.
3.5K
The Legacy Systems Protocol Converter, initially developed within the Horizon Europe Interstore project, acts as a middleware, allowing devices that use different communication protocols to exchange data with EMS systems that use the IEEE2030.5 standard. It supports:
Key features of Legacy Protocol Converter:
The Legacy Protocol Converter can be deployed and run using:
LPC can be executed on-premise or in the cloud. It supports a variety of environments, including Kubernetes, Docker and classical virtual machines, as well as bare-metal.
Overall, the Legacy Protocol Converter is a tool for enabling communication between devices and EMS systems that use different communication protocols. It supports the latest IEEE2030.5 standard and provides a flexible and efficient way to translate messages between different protocols.
In order for Legacy Protocol Converter to work, configuration files must be mounted to the /app/conf folder. Configuration files are in the YAML format.
OPTIONAL If using the custom configuration file for the logging, then this file must be mounted to the container
before running it.
It must be mounted to /app/log-config/log4j2.xml like this:
docker run -v /path/to/log4j2.xml:/app/log-config/log4j2.xml -v /path/to/config:/app/conf interstore/legacy-protocol-converter:latest
Run the Docker container and mount configuration folder: docker run -v /path/to/config:/app/conf interstore/legacy-protocol-converter:latest
General format of the configuration file is following:
connections:
-
-
registration:
topic:
outgoing-connection:
-
-
message:
transformations:
-
-
Each incoming/outgoing connection must be configured in the list of connections so the LPC knows how to connect.
Possible options for each connection are following:
connections:
- name: string
type: NATS/MQTT/RabbitMQ/Modbus
host: string
port: integer
ssl:
default: true/false
username: string
password: string
version: 3/5
virtual-host: string
exchange-name: string
routing-key: string
exchange-type: direct/fanout/topic
reconnect: true/false
device: string
baud-rate: integer
data-bits: integer
parity: none/even/odd/space/mark
stop-bits: integer
...
name and type are required keys.
Currently supported parameters for connection with NATS are host, port, username, password and reconnect.
Example of configuration for NATS:
connections:
- name: NATS-connection
type: NATS
host: nats://localhost
port: 4222
username: userTest
password: testUser
reconnect: true
...
Currently supported parameters for connection with MQTT are host, port, ssl, version, username, password and reconnect.
Example of configuration for MQTT:
connections:
- name: MQTT-connection
type: MQTT
host: localhost
port: 8883
ssl:
default: true
version: 3
username: username
password: password
reconnect: false
...
Currently supported parameters for connection with RabbitMQ are host, port, username, password, virtual-host, exchange-name, routing-key, exchange-type, reconnect.
virtual-host, exchange-name, routing-key, exchange-type are optional.
Default value for virtual-host is /.
Example of configuration for RabbitMQ:
connections:
- name: RabbitMQ-connection
type: RabbitMQ
host: localhost
virtual-host: /
exchange-name: exchange
routing-key: key
exchange-type: direct
...
Configuration for Modbus depends on connection type, LPC supports serial connection or TCP connection. For TCP connection parameters host and port are required. For serial connection parameters device is required, optional parameters are baud-rate, data-bits, parity and stop-bits.
Option for RTU over TCP is also supported by configuring both parameters for TCP and serial connection.
Example of configuration for serial Modbus connection:
connections:
- name: Modbus-connection
type: Modbus
device: /dev/ttymxc2
baud-rate: 115200
data-bits: 8
parity: none
...
Example of configuration for TCP Modbus connection:
connections:
- name: Modbus-connection
type: Modbus
host: localhost
port: 502
..
Registration is optional and provides support for registering the LPC by specified connections. It is designed to send a message once to the specified topic when the LPC is started.
Possible options are:
registration:
topic: string
outgoing-connection:
-
-
message: string
Example of registration:
registration:
topic: registration/device/{deviceId}
outgoing-connection:
- NATS-connection
message: '
{
"status": "online"
}
'
In the transformations section, each transformation is described, from which topics to listen on to mapping incoming/outgoing messages to specified format and structure.
Possible options for each transformation are following:
connections:
-
-
transformations:
- name: string
description: string
connections:
incoming-connection:
-
-
incoming-topic: string
incoming-format: XML/JSON
outgoing-connection:
-
-
outgoing-topic: string
outgoing-format: XML/JSON
to-outgoing:
retry-count: integer
topic: string
message: string
to-incoming:
retry-count: integer
to-topic: string
message: string
or
to-incoming:
retry-count: integer
modbus-function-code: integer
modbus-device-id: integer
endianness: big/little
modbus-registers:
- register-address: integer
path: string
type: int8/int16/int32/int64/float32/float64
pattern: string
values: array
interval-request:
interval: integer
request:
modbus-function-code: integer
modbus-device-id: integer
endianness: big/little
modbus-registers:
- register-address: integer
path: string
type: int8/int16/int32/int64/float32/float64
pattern: string
values: array
or
request:
to-topic: string
reply-from-topic: string
message: string
General options:
Connection options:
Messages options:
Interval request options:
retry-count is used for all message structures and it specifies the number of retries for sending the message. If not specified, default value is 0.
All topics provide support for placeholders, by using {KEY}.
LPC will replace this KEY with either system variable under the key KEY or with the value from provided Java
argument.
For example, registration/device/{deviceId} will be replaced with registration/device/1 if the deviceId is 1.
Transforming messages from one structure to another is done with the help of a mapper. Each mapper has the following variables that are configurable:
type specifies the type to which value must be converted to. Possible values are: integer, float, double, date, datetime and string. When using Modbus, number of bits is required, so integer8 (or int8), int16, int32, int64 and also float32 and float64; int64 is converted to long and float64 is converted to double. date and datetime are converted to long representing the number of milliseconds since January 1, 1970, 00:00:00 GMT.
path specifies path to the value that will be used in new message structure. For XML/JSON this is done using XPath or JSON Pointer (e.g. /OutgoingEvent/currentStatus). For Modbus messages, register address must be provided.
pattern is needed only when type is datetime or date as it specifies the format of the provided temporal value at path.
values is needed only when value must be converted to the index of an array or index to some value from the array.
For easier explanation of options, we will use examples.
We will be transforming JSON structure of IncomingEvent to XML structure of OutgoingEvent. These two structures are used just as examples.
JSON IncomingEvent:
{
"datetime": "28-08-2023 12:00:35",
"status": "active",
"start": "28-08-2023",
"duration": 900
}
XML IEEE2030.5 Event:
<Event>
<creationTime>1702909917932</creationTime>
<EventStatus>
<currentStatus>1</currentStatus>
<dateTime>1693216835000</dateTime>
<potentiallySuperseded>false</potentiallySuperseded>
</EventStatus>
<interval>
<duration>900</duration>
<start>1693216835000</start>
</interval>
</Event>
connections:
- name: NATS-connection
type: NATS
host: nats://localhost
port: 4222
reconnect: true
- name: MQTT-connection
type: MQTT
ssl:
default: true
host: localhost
port: 8883
username: username
password: password
transformations:
- name: JSON IncomingEvent to XML IEEE2030.5 Event
description: Example showing transformation of messages from JSON to XML
connections:
incoming-connection:
- MQTT-connection
incoming-topic: topic1
incoming-format: JSON
outgoing-connection:
- NATS-connection
outgoing-topic: event/listen
outgoing-format: XML
to-outgoing:
to-topic: event/send
message: '<Event>
<creationTime>$timestamp</creationTime>
<EventStatus>
<currentStatus>
<lpc:mapping>
<path type="integer">/status</path>
<values>["scheduled", "active", "cancelled", "cancelled_with_r", "superseded"]</values>
</lpc:mapping>
</currentStatus>
<dateTime>
<lpc:mapping>
<path type="datetime">datetime</path>
<pattern>dd-MM-yyyy HH:mm:ss</pattern>
</lpc:mapping>
</dateTime>
<potentiallySuperseded>false</potentiallySuperseded>
</EventStatus>
<interval>
<duration>
<lpc:mapping>
<path type="integer">duration</path>
</lpc:mapping>
</duration>
<start>
<lpc:mapping>
<path type="date">/start</path>
<pattern>dd-MM-yyyy</pattern>
</lpc:mapping>
</start>
</interval>
</Event>
'
With this transformation we are specifying that the MQTT-connection will subscribe to topic topic1 and LPC will
transform the message to XML
structure, and it will send the transformed message using NATS-connection to topic event/send.
We can see that the mapping is done with the help of XML tag <lpc:mapping>.
For setting the value of OutgoingEvent/currentStatus, we are converting value "active" to integer 1.
Because the IncomingEvent/status is string, and we have provided values options, this means we will map
to the integer based on which index is the value of IncomingEvent/status. So "active" maps to 1.
For setting the value of OutgoingEvent/datetime, we are converting from datetime to long.
Because type is datetime, we need to provide the pattern of the incoming value, so LPC know how to parse the
value, hence dd-MM-yyyy HH:mm:ss. Also note that the leading / is not needed.
Same applies for setting the value of OutgoingEvent/interval/start, but here we are using date.
For setting the value of OutgoingEvent/interval/duration we just need to specify type, because mapping is
done 1 on 1.
There is also reserved keyword $timestamp which is used to set the value of OutgoingEvent/creationTime to
the current time in milliseconds.
For showcasing this we will use the example above but IncomingEvent in XML format and IEEE2030.5 Event in JSON format.
XML IncomingEvent:
<IncomingEvent>
<datetime>28-08-2023 12:00:35</datetime>
<status>active</status>
<start>28-08-2023</start>
<duration>900</duration>
</IncomingEvent>
JSON IEEE2030.5 Event:
{
"creationTime": 1702909917932,
"eventStatus": {
"currentStatus": 1,
"dateTime": 1693216835000,
"potentiallySuperseded": false
},
"interval": {
"duration": 900,
"start": 1693216835000
}
}
connections:
- name: NATS-connection
type: NATS
host: nats://localhost
port: 4222
reconnect: true
- name: MQTT-connection
type: MQTT
ssl:
default: true
host: localhost
port: 8883
username: username
password: password
transformations:
- name: XML IncomingEvent to JSON IEEE2030.5 Event
description: Example showing transformation of messages from XML to JSON
connections:
incoming-connection:
- MQTT-connection
incoming-topic: topic1
incoming-format: XML
outgoing-connection:
- NATS-connection
outgoing-topic: event/listen
outgoing-format: JSON
to-outgoing:
to-topic: event/send
message: '{
"creationTime": $timestamp,
"eventStatus": {
"currentStatus": {
"lpc:mapping": {
"path": "/IncomingEvent/status",
"type": "integer",
"values": ["scheduled", "active", "cancelled", "cancelled_with_r", "superseded"]
}
},
"dateTime": {
"lpc:mapping": {
"path": "/IncomingEvent/datetime",
"type": "datetime",
"pattern": "dd-MM-yyyy HH:mm:ss"
}
},
"potentiallySuperseded": false
},
"interval": {
"duration": {
"lpc:mapping": {
"path": "/IncomingEvent/duration",
"type": "integer"
}
},
"start": {
"lpc:mapping": {
"path": "/IncomingEvent/start",
"type": "date",
"pattern": "dd-MM-yyyy"
}
}
}
}
'
Here we see the mapping is similar as in the example above, but because it is in JSON
format, we are using lpc:mapping key.
For this example, we will use the Modbus connection to read data from the device and transform it to JSON IEEE2030.5 Event.
IncomingEvent structure in Modbus:
JSON IEEE2030.5 Event:
{
"creationTime": 1702909917932,
"eventStatus": {
"currentStatus": 1,
"dateTime": 1693216835000,
"potentiallySuperseded": false
},
"interval": {
"duration": 900,
"start": 1693216835000
}
}
connections:
- name: NATS-connection
type: NATS
host: nats://localhost
port: 4222
reconnect: true
- name: Modbus-connection
type: Modbus
device: /dev/ttya
baud-rate: 9600
data-bits: 8
transformations:
- name: XML IncomingEvent to JSON IEEE2030.5 Event
description: Example showing transformation of messages from XML to JSON
connections:
incoming-connection:
- Modbus-connection
outgoing-connection:
- NATS-connection
outgoing-topic: event/listen
outgoing-format: JSON
to-outgoing:
to-topic: event/send
message: '{
"creationTime": $timestamp,
"EventStatus": {
"currentStatus": {
"lpc:mapping": {
"path": "31010",
"type": "int8"
}
},
"dateTime": {
"lpc:mapping": {
"path": "31000",
"type": "int64"
}
},
"potentiallySuperseded": false
},
"interval": {
"duration": {
"lpc:mapping": {
"path": "31030",
"type": "int32"
}
},
"start": {
"lpc:mapping": {
"path": "31020",
"type": "int64"
}
}
}
}
'
interval-request:
interval: 10000
request:
modbus-function-code: 3
modbus-device-id: 1
endianness: little
modbus-registers:
- register-address: 31000
type: int64
- register-address: 31010
type: int8
- register-address: 31020
type: int64
- register-address: 31030
type: int32
Here we see that the mapping is done with register addresses, and we are specifying the type of the value at the register address.
Here we have specified that LPC will send a request to the device every 10 seconds to read the data from the registers.
LPC will for each register send new request with function code specified at modbus-function-code to specified
device atmodbus-device-id.
LPC can be deployed as a JAR or as a Docker container.
When deploying, path to the configuration file must be provided either as an argument or mounted as a volume.
Default path to the configuration folder is ./conf.
In this folder, multiple configuration files can be placed, and LPC will read all of them.
Configuration of logging is done with the help of Log4j library. So for configuring logging, one should create file log4j.xml and config logging per Log4j documentation.
Default configuration is following:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration name="kumuluzee">
<Appenders>
<Console name="console" target="SYSTEM_OUT">
<PatternLayout pattern="%d %p -- %c -- %marker %m %X %ex %n"/>
</Console>
<File name="file_debug_app" fileName="logs/debug_app.log">
<PatternLayout pattern="%d %p -- %c -- %marker %m %X %ex %n"/>
</File>
<File name="file_info_app" fileName="logs/app.log">
<PatternLayout pattern="%d %p -- %c -- %marker %m %X %ex %n"/>
</File>
<File name="file_lpc" fileName="logs/lpc.log">
<PatternLayout pattern="%d %p -- %c -- %marker %m %X %ex %n"/>
</File>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="console" level="info"/>
<AppenderRef ref="file_info_app" level="info"/>
<AppenderRef ref="file_debug_app" level="debug"/>
</Root>
<Logger name="si.sunesis.interoperability" level="debug">
<AppenderRef ref="file_lpc"/>
</Logger>
</Loggers>
</Configuration>
This will print out logs of level INFO or above in the console and in the file app.log.
This will print out logs of level DEBUG or above in the file debug_app.log.
Separate log file for Legacy Protocol Converter is created in order for easier troubleshooting of LPC at lpc.log.
If using different configuration, one should specify the location of the configuration.
Content type
Image
Digest
sha256:122fbdfbe…
Size
275.3 MB
Last updated
12 months ago
docker pull interstore/legacy-protocol-converter