prisme-flow
Version:
prisme platform flow engine
330 lines (282 loc) • 10.3 kB
JavaScript
/**
* Created by prisme.io on 09/06/2017.
*/
function timestamp () {
return new Date().toISOString().replace(/T/, ' ') // replace T with a space
.replace(/\..+/, '')
}
function log (msg, args) {
if (args) { console.log(timestamp() + ': ' + msg, args) } else { console.log(timestamp() + ': ' + msg) }
}
module.exports = function (RED) {
var modbus = require('jsmodbus')
var util = require('util')
function ModbusTCPServerNode (config) {
RED.nodes.createNode(this, config)
this.host = config.host
this.port = config.port
this.reconnect = config.reconnect
this.reconnecttimeout = config.reconnecttimeout
this.unit_id = config.unit_id
this.modbusconn = null
var node = this
var consettings = {
'host': node.host,
'port': node.port,
'unitId': Number(node.unit_id),
'timeout': 15000
/* 'logEnabled' : true,
'logLevel' : 'debug' */
}
node.initializeModbusTCPConnection = function (handler) {
log('Connecting to modbustcp slave at ' + node.host + ':' + node.port + ' unit_id: ' + node.unit_id)
if (Number(node.reconnecttimeout) > 0) {
consettings.autoReconnect = true
consettings.reconnectTimeout = Number(node.reconnecttimeout) * 1000
}
node.modbusconn = modbus.client.tcp.complete(consettings)
node.modbusconn.on('error', function (err) {
node.error('ModbusTCPConnection: ' + util.inspect(err, false, null))
})
node.modbusconn.connect()
handler(node.modbusconn)
}
node.on('close', function () {
log('Disconnecting from modbustcp slave at ' + node.host + ':' + node.port)
node.modbusconn.close()
node.modbusconn = null
})
}
RED.nodes.registerType('modbustcp-server', ModbusTCPServerNode)
function ModbusTCPWrite (config) {
RED.nodes.createNode(this, config)
this.name = config.name
this.dataType = config.dataType
this.adr = Number(config.adr)
this.quantity = config.quantity
var node = this
var modbusTCPServer = RED.nodes.getNode(config.server)
node.receiveEvent1 = function () {
log(node.name + ' was Disconnected')
node.status({fill: 'grey', shape: 'dot', text: 'Disconnected'})
}
node.receiveEvent2 = function () {
node.status({fill: 'green', shape: 'dot', text: 'Connected'})
}
modbusTCPServer.initializeModbusTCPConnection(function (connection) {
node.connection = connection
node.connection.on('close', node.receiveEvent1)
node.connection.on('connect', node.receiveEvent2)
})
function set_successful_write (resp) {
node.status({fill: 'green', shape: 'dot', text: 'Successfully Written'})
}
function modbus_error_check (err) {
if (err) {
node.status({fill: 'red', shape: 'dot', text: 'Error'})
log(err)
node.error(node.name + ': ' + JSON.stringify(err))
return false
}
return true
}
node.on('input', function (msg) {
if (node.connection.getState() === 'closed') {
if (!node.connection.autoReconnect) {
node.connection.connect()
}
}
if (!(msg && msg.hasOwnProperty('payload'))) return
if (msg.payload == null) {
node.error(node.name + ': ' + 'Invalid msg.payload!')
return
}
node.status(null)
switch (node.dataType) {
case 'Coil': // FC: 5
node.connection.writeSingleCoil(node.adr, Number(msg.payload)).then(function (resp, err) {
if (modbus_error_check(err) && resp) {
set_successful_write(resp)
}
})
break
case 'HoldingRegister': // FC: 6
node.connection.writeSingleRegister(node.adr, Number(msg.payload)).then(function (resp, err) {
if (modbus_error_check(err) && resp) {
set_successful_write(resp)
}
})
break
case 'Coils': // FC: 15
if (Array.isArray(msg.payload)) {
var values = []
for (i = 0; i < msg.payload.length; i++) {
values.push(parseInt(msg.payload[i]))
}
} else {
node.error(node.name + ': ' + 'msg.payload not an array')
break
}
node.connection.writeMultipleCoils(node.adr, values).then(function (resp, err) {
if (modbus_error_check(err) && resp) {
set_successful_write(resp)
}
})
break
case 'HoldingRegisters': // FC: 16
if (Array.isArray(msg.payload)) {
var values = []
for (i = 0; i < msg.payload.length; i++) {
values.push(parseInt(msg.payload[i]))
}
} else {
node.error(node.name + ': ' + 'msg.payload not an array')
break
}
node.connection.writeMultipleRegisters(node.adr, values).then(function (resp, err) {
if (modbus_error_check(err) && resp) {
set_successful_write(resp)
}
})
break
default:
break
}
}
)
node.on('close', function () {
log(node.name + ':' + 'Closing')
node.status({fill: 'grey', shape: 'dot', text: 'Disconnected'})
node.connection.removeListener('connect', node.receiveEvent2)
node.connection.removeListener('close', node.receiveEvent1)
node.connection.close()
})
}
RED.nodes.registerType('modbustcp-write', ModbusTCPWrite)
function ModbusTCPRead (config) {
RED.nodes.createNode(this, config)
this.name = config.name
this.dataType = config.dataType
this.adr = config.adr
this.quantity = config.quantity
this.rate = config.rate
this.rateUnit = config.rateUnit
this.connection = null
var node = this
var modbusTCPServer = RED.nodes.getNode(config.server)
var timerID
node.receiveEvent1 = function () {
log(node.name + ' was disconnected or was unable to connect')
node.status({fill: 'grey', shape: 'dot', text: 'Disconnected'})
clearInterval(timerID)
timerID = null
}
node.receiveEvent2 = function () {
node.status({fill: 'green', shape: 'dot', text: 'Connected: Rate:' + node.rate + ' ' + node.rateUnit})
clearInterval(timerID)
timerID = null
ModbusMaster() // fire once at start
if (!timerID) {
timerID = setInterval(function () {
ModbusMaster()
}, calcRate())
}
}
modbusTCPServer.initializeModbusTCPConnection(function (connection) {
node.connection = connection
node.status({fill: 'blue', shape: 'dot', text: 'Initiating.....'})
node.connection.on('close', node.receiveEvent1)
node.connection.on('connect', node.receiveEvent2)
})
function set_connected_waiting () {
node.status({fill: 'green', shape: 'dot', text: 'Connected: Rate:' + node.rate + ' ' + node.rateUnit})
}
function set_connected_polling () {
node.status({fill: 'yellow', shape: 'dot', text: 'Polling'})
}
function modbus_error_check (err) {
if (err) {
node.status({fill: 'red', shape: 'dot', text: 'Error'})
log(err)
node.error('ModbusTCPClient: ' + JSON.stringify(err))
return false
}
return true
}
function calcRate () {
switch (node.rateUnit) {
case 'ms':
rate = node.rate // milliseconds
break
case 's':
rate = node.rate * 1000 // seconds
break
case 'm':
rate = node.rate * 60000 // minutes
break
case 'h':
rate = node.rate * 3600000 // hours
break
default:
break
}
return rate
}
function ModbusMaster () {
var msg = {}
msg.topic = node.name
switch (node.dataType) {
case 'Coil': // FC: 1
set_connected_polling()
node.connection.readCoils(Number(node.adr), Number(node.quantity)).then(function (resp, error) {
if (modbus_error_check(error) && resp) {
set_connected_waiting()
msg.payload = resp.coils // array of coil values
node.send(msg)
}
})
break
case 'Input': // FC: 2
set_connected_polling()
node.connection.readDiscreteInputs(Number(node.adr), Number(node.quantity)).then(function (resp, error) {
if (modbus_error_check(error) && resp) {
set_connected_waiting()
msg.payload = resp.coils // array of discrete input values
node.send(msg)
}
})
break
case 'HoldingRegister': // FC: 3
set_connected_polling()
node.connection.readHoldingRegisters(Number(node.adr), Number(node.quantity)).then(function (resp, error) {
if (modbus_error_check(error) && resp) {
set_connected_waiting()
msg.payload = resp.register // array of register values
node.send(msg)
}
})
break
case 'InputRegister': // FC: 4
set_connected_polling()
node.connection.readInputRegisters(Number(node.adr), Number(node.quantity)).then(function (resp, error) {
if (modbus_error_check(error) && resp) {
set_connected_waiting()
msg.payload = resp.register // array of register values
node.send(msg)
}
})
break
}
}
node.on('close', function () {
log(node.name + ':' + 'Closing')
clearInterval(timerID)
timerID = null
node.connection.removeListener('connect', node.receiveEvent2)
node.connection.removeListener('close', node.receiveEvent1)
node.status({fill: 'grey', shape: 'dot', text: 'Disconnected'})
node.connection.close()
})
}
RED.nodes.registerType('modbustcp-read', ModbusTCPRead)
}