UNPKG

@eflexsystems/node-red-contrib-open-protocol

Version:

A Node-RED node to interface with other systems using the Open Protocol

487 lines (365 loc) 15.3 kB
//@ts-check /* Copyright: (c) 2018-2020, Smart-Tech Controle e Automação GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) */ const openProtocol = require('@eflexsystems/node-open-protocol'); const base = require('../base.json'); const { EventEmitter } = require('events'); module.exports = function (RED) { // <Begin> --- Config --- function OpenProtocolConfig(values) { EventEmitter.call(this); RED.nodes.createNode(this, values); let node = this; node.controllerIP = values.controllerIP; node.controllerPort = Number(values.controllerPort); node.keepAlive = values.keepAlive; node.timeout = values.timeout; node.retries = values.retries; node.rawData = values.rawData; node.genericMode = values.generic; node.forceLinkLayer = values.linkLayer; node.disablemidparsing = values.disablemidparsing; node.userDisconnect = false; node.onClose = false; let parsingDisable = {}; node.disablemidparsing.split(/[,; ]+/).map((item) => { return Number(item); }).forEach((elm) => { parsingDisable[elm] = true; }); if (node.forceLinkLayer === 'false') { node.forceLinkLayer = false; } else { if (node.forceLinkLayer === 'true') { node.forceLinkLayer = true; } else { node.forceLinkLayer = undefined; } } let opts = { linkLayerActivate: node.forceLinkLayer, genericMode: node.genericMode, keepAlive: Number(node.keepAlive), rawData: node.rawData, timeOut: Number(node.timeout), retryTimes: Number(node.retries), disableMidParsing: parsingDisable }; node.connectionStatus = false; node.connect = function connect() { if (node.connectionStatus) { return; } clearTimeout(node.timerReconnect); node.op = openProtocol.createClient(node.controllerPort, node.controllerIP, opts, (data) => node.onConnect(data)); node.op.on("error", (err) => node.onErrorOP(err)); }; node.connect(); node.onConnect = function onConnect(data) { clearTimeout(node.timerReconnect); node.connectionStatus = true; node.op.on("__SubscribeData__", (data) => node.onSubscribeDataOP(data)); node.op.on("close", (error) => node.onCloseOP(error)); node.op.on("connect", (data) => node.onConnectOP(data)); node.op.on("data", (data) => node.onDataOP(data)); node.emit("connect", data); }; node.disconnect = function disconnect() { node.removeListenersOP(); node.op.close(); node.connectionStatus = false; node.emit("disconnect"); node.userDisconnect = true; }; node.reconnect = function reconnect() { clearTimeout(node.timerReconnect); if (node.onClose || node.userDisconnect || node.connectionStatus) { return; } node.connect(); }; // Begin::Event of Session Control Client - Open Protocol node.onErrorOP = function onErrorOP(error) { if (node.onClose) { return; } node.connectionStatus = false; node.emit("disconnect"); node.error(`${RED._("open-protocol.message.failed-connect")} ${error.address}:${error.port} ${error.code}`); node.removeListenersOP(); clearTimeout(node.timerReconnect); node.timerReconnect = setTimeout(() => node.reconnect(), 5000); }; node.onCloseOP = function onCloseOP(error) { if (node.onClose) { return; } node.connectionStatus = false; node.emit("disconnect"); node.removeListenersOP(); clearTimeout(node.timerReconnect); node.timerReconnect = setTimeout(() => node.reconnect(), 5000); }; node.onConnectOP = function onConnectOP(data) { node.emit("connect", data); }; node.onDataOP = function onDataOP(data) { node.emit("data", data); }; node.onSubscribeDataOP = function onSubscribeDataOP(data) { node.emit(data.key, data.data); }; node.removeListenersOP = function removeListenersOP() { node.op.removeListener("error", (err) => node.onErrorOP(err)); node.op.removeListener("__SubscribeData__", (data) => node.onSubscribeDataOP(data)); node.op.removeListener("close", (error) => node.onCloseOP(error)); node.op.removeListener("connect", (data) => node.onConnectOP(data)); node.op.removeListener("data", (data) => node.onDataOP(data)); }; // End::Event of Session Control Client - Open Protocol node.on("close", () => { node.onClose = true; clearTimeout(node.timerReconnect); node.removeListenersOP(); node.connectionStatus = false; node.emit("disconnect"); node.op.close(); }); } RED.nodes.registerType("op config", OpenProtocolConfig); // <End> --- Config // <Begin> --- Node function OpenProtocolNode(values) { RED.nodes.createNode(this, values); let node = this; node.config = RED.nodes.getNode(values.config); node.midGroup = values.midGroup; node.customMid = values.customMid; node.revision = values.revision; node.customRevision = values.customRevision; node.autoSubscribe = values.autoSubscribe; node.forwardErrors = values.forwardErrors; node.config.on("connect", (data) => node.onConnect(data)); node.config.on("disconnect", () => node.onDisconnect()); node.onConnect = function onConnect(data) { node.status({ fill: "green", shape: "ring", text: RED._("open-protocol.util.label.connected") }); if (node.midGroup === "Connect") { let message = {}; message.payload = data.payload; setMessage(message, data); node.send(message); } if (base[node.midGroup]) { if (node.autoSubscribe && base[node.midGroup].typeRequest === "SUBSCRIBE") { onInput(node, {}); } } }; node.onDisconnect = function onDisconnect() { node.status({ fill: "red", shape: "ring", text: RED._("open-protocol.util.label.disconnected") }); if (base[node.midGroup]) { let reference = base[node.midGroup]; node.config.removeListener(reference.family, node.onSubscribe); } }; node.onData = function onData(data) { let message = {}; message.payload = data.payload; setMessage(message, data); if (node.midGroup === "Custom") { node.send(message); } else { node.send([null, message]); } }; node.onSubscribe = function onSubscribe(data) { let message = {}; message.payload = data.payload; setMessage(message, data); //Alarm MID ->> [Feedback, Data, Status, ACK] if (node.midGroup === 71) { if (data.mid === 71) { //Data node.send([null, message, null, null]); return; } if (data.mid === 76) { //Status node.send([null, null, message, null]); return; } if (data.mid === 74) { //ACK node.send([null, null, null, message]); return; } } else { node.send([null, message]); } }; if (node.midGroup === "Custom") { node.config.on("data", (data) => node.onData(data)); } node.on("input", (msg) => { if (node.midGroup === "Custom") { let opts = { revision: msg.revision, payload: msg.payload }; node.config.op.sendMid(msg.mid, opts) .catch(err => { msg.error = err.stack || err; if (node.forwardErrors) { node.send([msg, null]); } node.error(`${RED._("open-protocol.message.error-send-mid")} - ${err}`, msg); }); return; } if (node.midGroup === "Connect") { if (msg.connect != undefined) { if (msg.connect) { node.config.connect(); } else { node.config.disconnect(); } } return; } onInput(node, msg); }); node.on("close", () => { node.config.removeListener("connect", (data) => node.onConnect(data)); node.config.removeListener("disconnect", () => node.onDisconnect()); if (base[node.midGroup]) { let reference = base[node.midGroup]; node.config.removeListener(reference.family, node.onSubscribe); } //Alarm MID if (node.midGroup === 71) { node.config.removeListener("alarmAcknowledged", node.onSubscribe); node.config.removeListener("alarmStatus", node.onSubscribe); } }); function onInput(node, msg) { let reference = base[node.midGroup]; let opts = {}; opts.revision = node.revision || msg.revision; opts.revision = node.adjustRevision(opts.revision); if (msg.payload) { opts.payload = msg.payload || ""; } switch (reference.typeRequest) { case "REQUEST": node.config.op.request(reference.family, opts) .then(data => { msg.payload = data.payload; setMessage(msg, data); node.send(msg); }) .catch(err => { msg.error = err.stack || err; if (node.forwardErrors) { node.send(msg); } node.error(`${RED._("open-protocol.message.error-request")} - ${err}`, msg); }); break; case "SUBSCRIBE": if (msg.subscribe !== undefined && !msg.subscribe) { node.config.op.unsubscribe(reference.family) .then(data => { msg.payload = data.payload; setMessage(msg, data); node.send([msg, null]); node.config.removeListener(reference.family, node.onSubscribe); //Alarm MID if (node.midGroup === 71) { node.config.removeListener("alarmAcknowledged", node.onSubscribe); node.config.removeListener("alarmStatus", node.onSubscribe); } }) .catch(err => { msg.error = err.stack || err; if (node.forwardErrors) { node.send([msg, null]); } node.error(`${RED._("open-protocol.message.error-unsubscribe")} - ${err}`, msg); }); return; } node.config.op.subscribe(reference.family, opts) .then(data => { msg.payload = data.payload; setMessage(msg, data); node.send([msg, null]); node.config.on(reference.family, node.onSubscribe); //Alarm MID if (node.midGroup === 71) { node.config.on("alarmAcknowledged", node.onSubscribe); node.config.on("alarmStatus", node.onSubscribe); } }) .catch(err => { msg.error = err.stack || err; if (node.forwardErrors) { node.send([msg, null]); } node.error(`${RED._("open-protocol.message.error-subscribe")} - ${err}`, msg); }); break; case "COMMAND": node.config.op.command(reference.family, opts) .then(data => { msg.payload = data.payload; setMessage(msg, data); node.send(msg); }) .catch(err => { msg.error = err.stack || err; if (node.forwardErrors) { node.send(msg); } node.error(`${RED._("open-protocol.message.error-command")} - ${err}`, msg); }); break; } } node.adjustRevision = function adjustRevision(revision) { if (revision === "Custom") { return node.customRevision; } if (revision === "Auto") { return undefined; } return Number(revision); }; function setMessage(msg, data) { msg.mid = data.mid; msg.revision = data.revision; msg.noAck = data.noAck; msg.stationID = data.stationID; msg.spindleID = data.spindleID; msg.sequenceNumber = data.sequenceNumber; msg.messageParts = data.messageParts; msg.messageNumber = data.messageNumber; if (node.config.rawData) { msg._rawData = data._raw; } } } RED.nodes.registerType("op node", OpenProtocolNode); // <End> --- Node };