# node-red-contrib-gapit-modbus

The _gapit-modbus_ <a href="http://nodered.org" target="_new">Node-RED</a>
node reads modbus register and decode modbus registers acording to data types specified in the _Gapit code_.
The _Gapit code_ structures and names data, preparing it for
insertion into a database. The complementary _gapit-results-to-influx-batch_
node transforms the output of this node to the format required by the
_influx batch_ node (from `node-red-contrib-influxdb`).

So to read a standard set of data and insert it into InfluxDB, a flow
like this is all you need:

![Example Flow](images/example_flow.png)

Here's a fragment of a _Gapit code_:

```json
{
  "objects": [
    {
      "group_name": "EB Current Bar",
      "read_priority": 0,
      "address_type": "dec",
      "register_type": "holding",
      "group": [
        {
          "address": 8200,
          "description": "Breaker 1",
          "scaling_factor": 1,
          "unit": "A",
          "byte_type": "SINT16"
        },
        {
          "address": 8201,
          "description": "Breaker 2",
          "scaling_factor": 0.1,
          "unit": "A",
          "byte_type": "SINT32"
        }
      ]
    }
  ]
}
```

This would get inserted into a InfluxDB measurement called "EB Current Bar" (from the _group_name_ field),
with fields called "Breaker 1" and "Breaker 2" (from the _description_ field).

The output of `gapit-modbus`, which is parsed by `gapit-results-to-influx-batch`,
is `msg.gapit_results`. This has the same structure as the _Gapit code_,
except with `.value` added for each address.

### Other destinations/databases?

If you want to insert the data into a different database, you can parse
the `msg.gapit_results` structure, and transform it into the required
format.

### gapit-results-to-influx-batch node

The gapit-results-to-influx-batch node is packaged as a separate module,
`node-red-contrib-gapit-results`, which is automatically installed with
this module. It is a packaged as a separate module to enable sharing with
other nodes which yield results in the same `msg.gapit_results` structure.

The `node-red-contrib-gapit-results` module may be expanded with new
nodes in the future.

## Installation

### Install from the Node-RED interface

Install using _Manage palette_ -> _Install_.

You need to refresh your browser for the `gapit-results-to-influx-batch`
node to be available in the node palette. If you are running Node-RED 1.2
or older, you will need to restart Node-RED for it to be visible.

If you are using an ancient version of NPM, you will need to manually install
`node-red-contrib-gapit-results`.

### Install using the command line

Run the following command in your Node-RED user directory - typically `~/.node-red`

    npm install @gapit/node-red-contrib-gapit-modbus

and then restart Node-RED.

## Usage

### Example flow

This is the JSON for the flow pictured above.

```json
[
  {
    "id": "4baf4f334b779050",
    "type": "tab",
    "label": "gapit modbus",
    "disabled": false,
    "info": ""
  },
  {
    "id": "738d78ca6f672b59",
    "type": "influxdb batch",
    "z": "4baf4f334b779050",
    "influxdb": "99e22e92c3be98de",
    "precision": "",
    "retentionPolicy": "",
    "name": "",
    "database": "modbus",
    "precisionV18FluxV20": "ms",
    "retentionPolicyV18Flux": "",
    "org": "organisation",
    "bucket": "bucket",
    "x": 1240,
    "y": 280,
    "wires": []
  },
  {
    "id": "f1a98ce54346cd03",
    "type": "gapit-modbus",
    "z": "4baf4f334b779050",
    "name": "",
    "host": "nodered",
    "port": "502",
    "deviceName": "123-123-123",
    "deviceNameDbTag": "uid",
    "unitId": "1",
    "gapitCode": "{\"objects\":[{\"group_name\":\"EB Current Bar\",\"read_priority\":0,\"address_type\":\"dec\",\"register_type\":\"holding\",\"group\":[{\"address\":8200,\"description\":\"Breaker 1\",\"scaling_factor\":1,\"unit\":\"A\",\"byte_type\":\"SINT16\"},{\"address\":8201,\"description\":\"Breaker 2\",\"scaling_factor\":0.1,\"unit\":\"A\",\"byte_type\":\"SINT32\"}]}]}",
    "customTags": "{\"foo\":\"test\",\"baar\":10}",
    "scalingType": "general",
    "x": 420,
    "y": 280,
    "wires": [["d32ef3b037c73222", "3438922f9c6b25ca"]]
  },
  {
    "id": "c80bd9f665e8a5b9",
    "type": "debug",
    "z": "4baf4f334b779050",
    "name": "",
    "active": false,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "payload",
    "targetType": "msg",
    "statusVal": "",
    "statusType": "auto",
    "x": 1010,
    "y": 220,
    "wires": []
  },
  {
    "id": "d32ef3b037c73222",
    "type": "debug",
    "z": "4baf4f334b779050",
    "name": "",
    "active": false,
    "tosidebar": true,
    "console": false,
    "tostatus": false,
    "complete": "true",
    "targetType": "full",
    "statusVal": "",
    "statusType": "auto",
    "x": 590,
    "y": 220,
    "wires": []
  },
  {
    "id": "3438922f9c6b25ca",
    "type": "gapit-results-to-influx-batch",
    "z": "4baf4f334b779050",
    "use_timestamp_from_msg": false,
    "timestamp_property": "ts",
    "name": "",
    "x": 760,
    "y": 280,
    "wires": [["738d78ca6f672b59", "c80bd9f665e8a5b9"]]
  },
  {
    "id": "28060540b4b95271",
    "type": "inject",
    "z": "4baf4f334b779050",
    "name": "",
    "props": [],
    "repeat": "1",
    "crontab": "",
    "once": false,
    "onceDelay": 0.1,
    "topic": "",
    "x": 170,
    "y": 280,
    "wires": [["f1a98ce54346cd03"]]
  },
  {
    "id": "99e22e92c3be98de",
    "type": "influxdb",
    "hostname": "127.0.0.1",
    "port": "8086",
    "protocol": "http",
    "database": "database",
    "name": "modbus-test",
    "usetls": false,
    "tls": "8b3094b6.5fc088",
    "influxdbVersion": "1.8-flux",
    "url": "http://influx:8086",
    "rejectUnauthorized": false
  },
  {
    "id": "8b3094b6.5fc088",
    "type": "tls-config",
    "name": "",
    "cert": "",
    "key": "",
    "ca": "",
    "certname": "GapitBroker.crt",
    "keyname": "",
    "caname": "",
    "servername": "",
    "verifyservercert": false
  }
]
```

### Supported data types

The _Gapit-modbus_ supports these data types.

| Parameter | Description                                                                                            |
| --------- | ------------------------------------------------------------------------------------------------------ |
| BIT1      | Decodes a single bit register to integer value                                                         |
| BIT16     | Decodes a single bit register to integer value depending on witch bit is selected in the gapit code    |
| BIT16-SW  | Decodes a single bit SW register to integer value depending on witch bit is selected in the gapit code |
| BIT32     | Decodes a single bit register to integer value depending on witch bit is selected in the gapit code    |
| BIT32-SW  | Decodes a single bit SW register to integer value depending on witch bit is selected in the gapit code |
| UINT8     | Decodes a 8 bit register to unsigned integer value                                                     |
| UINT16    | Decodes a 16 bit register to unsigned integer value                                                    |
| UINT32    | Decodes a 32 bit register to unsigned integer value                                                    |
| UINT64    | Decodes a 16 bit register to unsigned integer value                                                    |
| SINT16    | Decodes a 16 bit register to signed integer value                                                      |
| SINT32    | Decodes a 32 bit register to signed integer value                                                      |
| SINT64    | Decodes a 16 bit register to signed integer value                                                      |
| FLOAT32   | Decodes a 32 bit register to floating point value                                                      |
| FLOAT64   | Decodes a 64 bit register to floating point value                                                      |
| DUMMY16   | Skipping a 16 bit register                                                                             |
| DUMMY32   | Skipping a 32 bit register                                                                             |
| DUMMY64   | Skipping a 64 bit register                                                                             |
| HEX       | Decodes a register depending on length specified in the gapit code to a hex decimal value              |
| ASCII     | Decodes a register depending on length specified in the gapit code to a ASCII value                    |
| ASCII-SW  | Decodes a SW register depending on length specified in the gapit code to a ASCII value                 |

### Gapit codes

A library of ready-made _Gapit codes_ is forthcoming.

### gapit-modbus

The node needs to be configured with, at a minimum:

- MODBUS config (IP address, port, Unit ID etc.)
- Gapit code

| Parameter          | Description                                                        |
| ------------------ | ------------------------------------------------------------------ |
| Name               | Node name                                                          |
| Host               | Modbus host                                                        |
| Port               | Modbus port                                                        |
| Device name        | The device name database records will be tagged with (e.g. "ups1") |
| Device name db-tag | The database tag for device name (e.g. "devname")                  |
| Unit ID            | The ID modbus uses to select which unit to read                    |
| Custom tags        | Extra tags to add to database records, see below                   |
| Gapit code         | The Gapit code JSON                                                |
| Scaling            | _Specify scaling type_                                             |

#### Custom tags

Add database tags, formatted as JSON with `tag_name: value`.

Example:

```json
{
  "floor": "1",
  "room": "101"
}
```

### gapit-results-to-influx-batch

To use a Node-RED timestamp with influxdb (enabled by default),
add a property containing a timestamp in your inject node. The
default name is `msg.ts`.

| Parameter                  | Description                                                    |
| -------------------------- | -------------------------------------------------------------- |
| Name                       | Node name                                                      |
| Use timestamp from Message | Use a timestamp injected in the flow (_default on_)            |
| Timestamp property         | The `msg` property to get the timestamp from (_default: "ts"_) |

#### N.B.! Check the InfluxDB time precision

If using timestamps from Node-RED, note that while this works "out-of-the-box"
with InfluxDB v2, you will have to set the `influx batch` node to match the
millisecond resolution of Node-RED if you're using InfluxDB v1. You can do this
by checking `Advanced Query Options` on the `influx batch` node, and then
choosing `Milliseconds (ms)` from the `Time Precision` popup menu.
