GOSK
Go SignalK implementation and more.
1. Design
This implementation consist of several small programs that collect, map, store and publish sensor data from one or more vessels.
1.1. Micro services
GOSK is a set of micro services that collect, map, store, transport and publish data available on a ship. GOSK publishes data in the SignalK Open Marine Data Standard. All data is stored in both a raw format (before any mapping is done) and in a mapped format.
1.1.1. Collectors
The only role of a collector is to collect raw data from the sensors on board. Collectors are made for specific protocols, e.g. a collector for NMEA0183 over UDP knows how to listen for data and a collector for Modbus over TCP knows how to pull data. Collectors have their individual configuration. Multiple collectors for the same protocol/transport can coexist in a system, e.g. 2 Canbus collectors for a port side and starboard engine.
1.1.2. Raw store
The only role of a raw store is to store the raw data in a time series database. See below for the storage format.
1.1.3. Mappers
The only role of a mapper is to translate data from a raw format to a mapped format. Mappers should not be aware of transport protocols, e.g. data coming from NMEA0183 over RS-422 and NMEA0183 over UDP should be handled by the same mapper.
1.1.4. Mapped store
The only role of a mapped store is to store the mapped data in a time series database. See below for the storage format.
1.1.5. Transporters
Transporters move (subsets) of mapped data from one network to another network. Transporters can be used to send data from the vessel to the cloud. Currently no transporters are implemented.
1.1.6. Publish
A publisher can provide the mapped data to other applications in different data formats and transport protocols. Currently a publisher for SignalK REST API and SignalK Streaming API.
1.2. Communication between micro services
For communication between the different micro services NNG is used. The messages are serialized using JSON encoding.
1.2.1 Raw JSON messages
The JSON message contains the following fields:
- collector, identifier for the source of the data;
- timestamp, the time when the data was receive in UTC and RFC3339Nano format;
- uuid, used to link raw and mapped data together;
- value, the actual value in base64 encoded format. This value contains all information needed for the mapper to map the data.
An example message for a NMEA0183 string:
{
"collector": "GPS",
"timestamp": "2022-02-09T16:12:29.481162381Z",
"uuid": "49175a7e-c4cb-455c-ae99-05eca8f7997d",
"value": "JEdQR0xMLDM3MjMuMjQ3NSxOLDEyMTU4LjM0MTYsVywxNjEyMjkuNDg3LEEsQSo0MQ=="
}
The value
is a base 64 encoded string of the NMEA018 sentence $GPGLL,3723.2475,N,12158.3416,W,161229.487,A,A*41
.
An example message for two modbus registers:
{
"collector": "CAT 3512",
"timestamp": "2022-02-09T12:03:57.431272983Z",
"uuid": "c67ae38f-64c6-427d-a91d-282a25623cc2",
"value": "AQAEyOwAAgABjnA="
}
The value
is a base64 encoded string of the following bytes
0x01
The slave id
0x00 0x04
The function code
0xc8 0xec
The address of the first register
0x00 0x02
The number of registers
0x00 0x01
The value of the first register
0x8e 0x70
The value of the second register
1.2.2 Mapped JSON messages
The basis is the Signal K Delta format with some extras. The label
, type
and src
properties in the source
section are used as follows. label
is filled with name
property of the collector config, type
is filled with protocol that is used and src
is filled with the url
property of the collector config. Each individual value
section has an extra property uuid
that is filled with the uuid
of the corresponding raw data.
Example:
{
"context": "vessels.urn:mrn:imo:mmsi:234567890",
"updates": [
{
"source": {
"label": "CAT 3512",
"type": "MODBUS",
"src": "tcp://127.0.0.1:5020"
},
"timestamp": "2010-01-07T07:18:44Z",
"values": [
{
"path": "propulsion.0.revolutions",
"value": 16.341667,
"uuid": "05a2ce62-6c52-4ce9-bb4f-07bb47cfa348"
},
{
"path": "propulsion.0.boostPressure",
"value": 45500,
"uuid": "05a2ce62-6c52-4ce9-bb4f-07bb47cfa348"
}
]
}
]
}
1.3. Storage
PostgreSQL and TimescaleDB are used as a time series database. Two tables are created. One table stores the raw data as received from the different collectors. The other table stores the mapped data as received from the different mappers.
1.3.1. Raw data
CREATE TABLE "raw_data" (
"time" TIMESTAMPTZ NOT NULL,
"collector" TEXT NOT NULL,
"extra_info" TEXT NOT NULL,
"value" TEXT NOT NULL -- base64 encoded binary data
);
1.3.2. Mapped data
CREATE TABLE "key_value_data" (
"time" TIMESTAMPTZ NOT NULL,
"context" TEXT NULL,
"source" JSON NOT NULL,
"path" TEXT NOT NULL,
"value" JSON NOT NULL
);