prisme-flow
Version:
prisme platform flow engine
331 lines (273 loc) • 7.77 kB
JavaScript
/**
* Created by prisme.io on 09/06/2017.
*/
module.exports = function (RED) {
'use strict'
var util = require('util')
var nodes7 = require('nodes7')
var EventEmitter = require('events').EventEmitter
// ---------- S7 Endpoint ----------
function createTranslationTable (vars) {
var res = {}
vars.forEach(function (elm) {
res[elm.name] = elm.addr
})
return res
}
function generateStatus (status, val) {
var obj
if (typeof val !== 'string' && typeof val !== 'number' && typeof val !== 'boolean') {
val = RED._('online')
}
switch (status) {
case 'online':
obj = {
fill: 'green',
shape: 'dot',
text: val.toString()
}
break
case 'badvalues':
obj = {
fill: 'yellow',
shape: 'dot',
text: RED._('failure')
}
break
case 'offline':
obj = {
fill: 'red',
shape: 'dot',
text: RED._('offline')
}
break
default:
obj = {
fill: 'grey',
shape: 'dot',
text: RED._('unknown')
}
}
return obj
}
function S7Endpoint (config) {
EventEmitter.call(this)
var node = this
var oldValues = {}
var status
var readInProgress = false
var readDeferred = false
var vars = config.vartable
var isVerbose = !!RED.settings.get('verbose')
node.writeInProgress = false
node.writeQueue = []
if (typeof vars === 'string') {
vars = JSON.parse(vars)
}
RED.nodes.createNode(this, config)
// avoids warnings when we have a lot of S7In nodes
this.setMaxListeners(0)
node._vars = createTranslationTable(vars)
node._conn = new nodes7({
silent: !isVerbose,
debug: isVerbose
})
node._conn.globalTimeout = parseInt(config.timeout) || 1500
node.getStatus = function getStatus () {
return status
}
node.writeVar = function writeVar (obj) {
node.writeQueue.push(obj)
if (!node.writeInProgress) {
writeNext()
}
}
function onWritten (err) {
node.writeInProgress = false
writeNext()
if (err) {
manageStatus('badvalues')
node.error(RED._('Failure (Bad values)'))
return
}
manageStatus('online')
}
function writeNext () {
var nextElm = node.writeQueue.shift()
if (nextElm) {
node._conn.writeItems(nextElm.name, nextElm.val, onWritten)
node.writeInProgress = true
}
}
function manageStatus (newStatus) {
if (status == newStatus) return
status = newStatus
node.emit('__STATUS__', {
status: status
})
}
function cycleCallback (err, values) {
readInProgress = false
if (readDeferred) {
doCycle()
readDeferred = false
}
if (err) {
manageStatus('badvalues')
node.error(RED._('Failure (Bad values)'))
return
}
manageStatus('online')
var changed = false
node.emit('__ALL__', values)
Object.keys(values).forEach(function (key) {
if (oldValues[key] !== values[key]) {
changed = true
node.emit(key, values[key])
node.emit('__CHANGED__', {key: key, value: values[key]})
oldValues[key] = values[key]
}
})
if (changed) node.emit('__ALL_CHANGED__', values)
}
function doCycle () {
if (!readInProgress) {
node._conn.readAllItems(cycleCallback)
readInProgress = true
} else {
readDeferred = true
}
}
function onConnect (err) {
if (err) {
manageStatus('offline')
node.error(RED._('Error connecting to PLC:') + err.toString())
return
}
manageStatus('online')
node._conn.setTranslationCB(function (tag) {
return node._vars[tag]
})
node._conn.addItems(Object.keys(node._vars))
node._td = setInterval(doCycle, config.cycletime)
}
node.on('close', function (done) {
clearInterval(node._td)
node._conn.dropConnection(function () {
done()
})
})
manageStatus('offline')
node._conn.initiateConnection({
host: config.address,
port: config.port,
rack: config.rack,
slot: config.slot
}, onConnect)
}
RED.nodes.registerType('s7 endpoint', S7Endpoint)
// ---------- S7 In ----------
function S7In (config) {
var node = this
var statusVal
RED.nodes.createNode(this, config)
node.endpoint = RED.nodes.getNode(config.endpoint)
if (!node.endpoint) {
return node.error(RED._('Missing configuration'))
}
function sendMsg (data, key, status) {
if (key === undefined) key = ''
var msg = {
payload: data,
topic: key
}
statusVal = status !== undefined ? status : data
node.send(msg)
node.status(generateStatus(node.endpoint.getStatus(), statusVal))
}
function onChanged (variable) {
sendMsg(variable.value, variable.key, null)
}
function onDataSplit (data) {
Object.keys(data).forEach(function (key) {
sendMsg(data[key], key, null)
})
}
function onData (data) {
sendMsg(data, config.mode == 'single' ? config.variable : '')
}
function onDataSelect (data) {
onData(data[config.variable])
}
function onEndpointStatus (s) {
node.status(generateStatus(s.status, statusVal))
}
node.endpoint.on('__STATUS__', onEndpointStatus)
if (config.diff) {
switch (config.mode) {
case 'all-split':
node.endpoint.on('__CHANGED__', onChanged)
break
case 'single':
node.endpoint.on(config.variable, onData)
break
case 'all':
default:
node.endpoint.on('__ALL_CHANGED__', onData)
}
} else {
switch (config.mode) {
case 'all-split':
node.endpoint.on('__ALL__', onDataSplit)
break
case 'single':
node.endpoint.on('__ALL__', onDataSelect)
break
case 'all':
default:
node.endpoint.on('__ALL__', onData)
}
}
node.on('close', function (done) {
node.endpoint.removeListener('__ALL__', onDataSelect)
node.endpoint.removeListener('__ALL__', onDataSplit)
node.endpoint.removeListener('__ALL__', onData)
node.endpoint.removeListener('__ALL_CHANGED__', onData)
node.endpoint.removeListener('__CHANGED__', onChanged)
node.endpoint.removeListener('__STATUS__', onEndpointStatus)
node.endpoint.removeListener(config.variable, onData)
done()
})
}
RED.nodes.registerType('s7 in', S7In)
// ---------- S7 Out ----------
function S7Out (config) {
var node = this
var statusVal
RED.nodes.createNode(this, config)
node.endpoint = RED.nodes.getNode(config.endpoint)
if (!node.endpoint) {
return node.error(RED._('Missing configuration'))
}
function onEndpointStatus (s) {
node.status(generateStatus(s.status, statusVal))
}
function onNewMsg (msg) {
var writeObj = {
name: config.variable || msg.variable,
val: msg.payload
}
if (!writeObj.name) return
statusVal = writeObj.val
node.endpoint.writeVar(writeObj)
node.status(generateStatus(node.endpoint.getStatus(), statusVal))
}
node.on('input', onNewMsg)
node.endpoint.on('__STATUS__', onEndpointStatus)
node.on('close', function (done) {
node.endpoint.removeListener('__STATUS__', onEndpointStatus)
done()
})
}
RED.nodes.registerType('s7 out', S7Out)
}