# Virtual/Objects in Node-RED
Virtual/Objects aka V/O is the Comonway metadata system for linking all type of objects from real to virtual.
V/O defined a metadata definition adapted for YAML, JSON, etc., data serialization standard. This serialization is adapted for all programming languages.
Actually, V/O development is in progress with a Golang database engine as a service and a metadata structure definition.
# Modbus TCP data to InfluxDB
A modbus TCP PLC collect objects data. All data are referred inside a 32-bits datatable collected in 16-bits through Node-RED, arranged and saved to InfluxDB by measurements and tags.

Requirements:
- This tutorial use a YAML file available in path
/workdir/nodered/config/VirtualObjects.yml
Node-RED nodes:
- node-red-contrib-modbus 3.4.0
- node-red-contrib-influxdb 0.2.1
Node-RED JS libraries:
- filteredVOForDatasource.js
- modbusArrangeToInfluxDBFromVO.js
- modbusPrepareRequestFromVO.js
Note: These nodes are available in Comongate Node-RED in
import>library>vo-modbus-to-influxdb.json
# Nodes Configuration
- Rate limit: Update the rate limit depending on the response speed of Modbus TCP PLC
- Modbus Flex Getter: Define the modbus server only, the rest is defined inside V/O metadata
- influxdb: Define influxdb server only, measurement is defined inside V/O metadata
# YAML file example
A complete file is available in config/
Note: Repeat the object vo as many as physical objects
# VIRTUAL OBJECTS
# Database definition
# version 0.1
virtual_objects:
- vo: # Virtual Object
name: Huile_thermique # Name
tags: # Tags
client: Cricad Energie SA # Tag #1
compteur_nom: Huile thermique # Tag #2
compteur_adresse: 21 # Tag #3
items: [status, n_serie, puissance, debit, temp_arr, temp_ret, temp_delta, energie, error]
datasources: # Datasources
modbus_tcp: # Modbus datasource
servername: T00 # Modbus TCP server reference
fc: 3 # function code
unitid: 14 # unitid
address: 10420 # address
quantity: 20 # quantity
data: # dataset
status: {pos: 1, format: U16, scale: 1} #0 unsigned 16-bits data
n_serie: {pos: 2, format: U32, scale: 1} #1 unsigned 32-bits data
puissance: {pos: 4, format: U32, scale: 1} #2 unsigned 32-bits data
debit: {pos: 7, format: U16, scale: 0.1} #3 unsigned 16-bits data
temp_arr: {pos: 9, format: U16, scale: 0.1} #4 unsigned 16-bits data
temp_ret: {pos: 11, format: U16, scale: 0.1} #5 unsigned 16-bits data
temp_delta: {pos: 13, format: U32, scale: 0.1} #6 unsigned 32-bits data
energie: {pos: 14, format: U32, scale: 1} #7 unsigned 32-bits data
error: {pos: 19, format: U16, scale: 1} #8 unsigned 16-bits data
influxdb: # Influxdb datasource
measurement: heat_energy_meter # Measurement
# Installation
Copy content below to clipboard and paste it in Node-RED.
[{"id":"69e74a84.f06be4","type":"modbus-flex-getter","z":"f17b2099.15a2e","name":"","showStatusActivities":false,"showErrors":false,"server":"a6d7cd5c.42f59","useIOFile":false,"ioFile":"","useIOForPayload":true,"x":990,"y":240,"wires":[["823572c0.2df94","3be16aea.e9e5e6"],[]]},{"id":"14148712.f75c19","type":"function","z":"f17b2099.15a2e","name":"Modbus request from V/O","func":"// Modbus request prepare from V/O\nmsg.payload = { \n 'value': msg.payload.vo, \n 'fc': msg.payload.vo.datasources.modbus_tcp.fc, \n 'unitid': msg.payload.vo.datasources.modbus_tcp.unitid, \n 'address': msg.payload.vo.datasources.modbus_tcp.address, \n 'quantity': msg.payload.vo.datasources.modbus_tcp.quantity\n}\nreturn msg;","outputs":1,"noerr":0,"x":770,"y":240,"wires":[["69e74a84.f06be4"]]},{"id":"20f1433b.ef8ffc","type":"bigsplitter","z":"f17b2099.15a2e","name":"","property":"payload","x":380,"y":240,"wires":[["c5818915.ef96e8"],[]]},{"id":"9c744cfa.14c61","type":"function","z":"f17b2099.15a2e","name":"Filtered V/O to payload","func":"// filterVOForDatasource filter V/O array for the destination datasource\nfunction filterVOForDatasource(element) {\n if (element.vo.datasources[dsType]) {\n if (element.vo.datasources[dsType].servername == dsName) {\n return element;\n }\n }\n}\n\nvar meters = msg.payload.virtual_objects\n\n/*****************************/\n/* Datasource definition */\n/*****************************/\nvar dsType = \"modbus_tcp\"\nvar dsName = \"T00\"\n/*****************************/\n\nvar filteredMeters = meters.filter(filterVOForDatasource);\n\nreturn {payload: filteredMeters}","outputs":1,"noerr":0,"x":200,"y":240,"wires":[["20f1433b.ef8ffc"]]},{"id":"4be0d19f.1978e","type":"debug","z":"f17b2099.15a2e","name":"data","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","x":470,"y":420,"wires":[]},{"id":"c5818915.ef96e8","type":"delay","z":"f17b2099.15a2e","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"100","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"x":560,"y":240,"wires":[["14148712.f75c19"]]},{"id":"72f8e408.9f321c","type":"comment","z":"f17b2099.15a2e","name":"Collect Heatmeters via Modbus TCP","info":"","x":180,"y":200,"wires":[]},{"id":"c4003b46.46fb38","type":"function","z":"f17b2099.15a2e","name":"Arrange Modbus payload with V/O","func":"/*\n * Translate array of value for saving realtime data to InfluxDB measurement\n * with help of Comonway Virtual/Objects metadata\n */\n// Initialization\ntags = {};\nitems = {};\n\n// Virtual/Objects used metadata\ntag = msg.input.payload.value.tags;\nitem = msg.input.payload.value.items;\ndata = msg.input.payload.value.datasources.modbus_tcp.data;\n\n// Populate tags\nfor (var i in tag) {\n tags[i] = tag[i]\n}\n\n// Populate items\nfor (var i in item) {\n if (data[item[i]]) {\n switch (data[item[i]].format) {\n case \"U16\":\n items[item[i]] = msg.payload[data[item[i]].pos] * data[item[i]].scale;\n break;\n case \"U32\":\n items[item[i]] = ((msg.payload[data[item[i]].pos] << 16) + msg.payload[data[item[i]].pos + 1]) * data[item[i]].scale;\n break;\n } \n }\n}\n\nmsg.payload = [items, tags];\n\n// Define measurement name\nmsg.measurement = msg.input.payload.value.datasources.influxdb.measurement\n\nreturn msg;","outputs":1,"noerr":0,"x":240,"y":380,"wires":[["4be0d19f.1978e","1ca7e067.27f"]]},{"id":"823572c0.2df94","type":"link out","z":"f17b2099.15a2e","name":"CollectEnergyMeters","links":["e26ec2f8.e7505"],"x":1135,"y":240,"wires":[]},{"id":"e26ec2f8.e7505","type":"link in","z":"f17b2099.15a2e","name":"","links":["823572c0.2df94"],"x":60,"y":380,"wires":[["c4003b46.46fb38"]]},{"id":"1ca7e067.27f","type":"influxdb out","z":"f17b2099.15a2e","influxdb":"9838e9c4.cf5248","name":"","measurement":"","precision":"","retentionPolicy":"","x":520,"y":380,"wires":[]},{"id":"8f7de12c.a64c2","type":"yaml","z":"f17b2099.15a2e","property":"payload","name":"","x":450,"y":100,"wires":[["4af78a7a.7cd3b4","6ccebb55.a72d84"]]},{"id":"294641a.fc341be","type":"file in","z":"f17b2099.15a2e","name":"V/O datasource","filename":"/workdir/config/VirtualObjects.yml","format":"utf8","chunk":false,"sendError":false,"x":280,"y":100,"wires":[["8f7de12c.a64c2"]]},{"id":"a3def7e5.a8d9d8","type":"inject","z":"f17b2099.15a2e","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":120,"y":100,"wires":[["294641a.fc341be"]]},{"id":"6ccebb55.a72d84","type":"debug","z":"f17b2099.15a2e","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":610,"y":140,"wires":[]},{"id":"5bad7371.30fc6c","type":"comment","z":"f17b2099.15a2e","name":"Load Energy metering configuration","info":"","x":180,"y":60,"wires":[]},{"id":"2374d846.e4d3b8","type":"comment","z":"f17b2099.15a2e","name":"Arrange and save to database","info":"","x":160,"y":340,"wires":[]},{"id":"4af78a7a.7cd3b4","type":"link out","z":"f17b2099.15a2e","name":"LoadConfigEnergyMeters","links":["37283705.7af558"],"x":555,"y":100,"wires":[]},{"id":"37283705.7af558","type":"link in","z":"f17b2099.15a2e","name":"","links":["4af78a7a.7cd3b4"],"x":55,"y":240,"wires":[["9c744cfa.14c61"]]},{"id":"3be16aea.e9e5e6","type":"debug","z":"f17b2099.15a2e","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1190,"y":280,"wires":[]},{"id":"a6d7cd5c.42f59","type":"modbus-client","z":"","name":"T00","clienttype":"tcp","bufferCommands":true,"stateLogEnabled":false,"tcpHost":"192.168.0.14","tcpPort":"502","tcpType":"DEFAULT","serialPort":"/dev/ttyUSB","serialType":"RTU-BUFFERD","serialBaudrate":"9600","serialDatabits":"8","serialStopbits":"1","serialParity":"none","serialConnectionDelay":"100","unit_id":"14","commandDelay":"1","clientTimeout":"1000","reconnectTimeout":"2000"},{"id":"9838e9c4.cf5248","type":"influxdb","z":"","hostname":"influxdb","port":"8086","protocol":"http","database":"monitor","name":"","usetls":false,"tls":""}]