UNPKG

node-red-contrib-snap4city-user

Version:

Nodes for Snap4city project, targeted to standard user (no developer)

488 lines (437 loc) 21.4 kB
/* NODE-RED-CONTRIB-SNAP4CITY-USER Copyright (C) 2018 DISIT Lab http://www.disit.org - University of Florence This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ module.exports = function (RED) { function DashboardForm(config) { RED.nodes.createNode(this, config); var node = this; var WebSocket = require('ws'); var s4cUtility = require("./snap4city-utility.js"); const logger = s4cUtility.getLogger(RED, node); const uid = s4cUtility.retrieveAppID(RED); node.s4cAuth = RED.nodes.getNode(config.authentication); var wsServer = s4cUtility.settingUrl(RED,node, "wsServerUrl", "wss://www.snap4city.org", "/wsserver", true); var wsServerHttpOrigin = s4cUtility.settingUrl(RED,node, "wsServerHttpOrigin", "wss://www.snap4city.org", "", true); node.ws = null; node.notRestart = false; //Meccanismo di passaggio dei valori tra il menu di add/edit node e il codice del nodo node.name = "NR_" + node.id.replace(".", "_"); node.widgetTitle = config.name; node.username = config.username; node.flowName = config.flowName; node.selectedDashboardId = config.selectedDashboardId; node.dashboardId = config.dashboardId; node.valueType = config.valueType; node.lastValue = config.minValue; node.domain = config.domainType; node.dropdwonOrderedList = config.dashboardFormOrderedList; node.httpRoot = null; node.selected = null; node.on('input', function (msg) { logger.debug("Flow input received: " + JSON.stringify(msg)); var timeout = 0; if ((new Date().getTime() - node.wsStart) > parseInt(RED.settings.wsReconnectTimeout ? RED.settings.wsReconnectTimeout : 1200) * 1000) { if (node.ws != null) { node.ws.removeListener('error', node.wsErrorCallback); node.ws.removeListener('open', node.wsOpenCallback); node.ws.removeListener('message', node.wsMessageCallback); node.ws.removeListener('close', node.wsCloseCallback); node.ws.removeListener('pong', node.wsHeartbeatCallback); node.notRestart = true; node.ws.terminate(); node.ws = null; } else { logger.debug("Why ws is null? I am in node.on('input'"); } node.ws = new WebSocket(wsServer, { origin: wsServerHttpOrigin }); node.ws.on('error', node.wsErrorCallback); node.ws.on('open', node.wsOpenCallback); node.ws.on('message', node.wsMessageCallback); node.ws.on('close', node.wsCloseCallback); node.ws.on('pong', node.wsHeartbeatCallback); logger.debug("is reconnetting to open WebSocket"); timeout = 1000; } node.wsStart = new Date().getTime(); if (typeof node.widgetUniqueName != "undefined" && node.widgetUniqueName != "") { if (typeof msg.payload.form != "undefined"){ node.dashboardFormOrderedList = msg.payload.form; } var newMetricData = { msgType: "SendToEmitter", widgetUniqueName: node.widgetUniqueName, dashboardId: node.dashboardId, value: JSON.stringify(node.dashboardFormOrderedList) }; setTimeout(function () { try { logger.info("Send SendToEmitter to WebSocket: " + newMetricData.value); logger.debug("Send SendToEmitter to WebSocket: " + JSON.stringify(newMetricData)); node.ws.send(JSON.stringify(newMetricData)); } catch (e) { logger.error("Error sending data to WebSocket : " + e); } }, timeout); s4cUtility.eventLog(RED, msg, newMetricData, config, "Node-Red", "Dashboard", wsServer, "TX"); } else { node.error("Maybe the dashboardForm is not created on dashboard"); logger.error("Error, the dashboardForm is not created on dashboard. node.widgetUniqueName: " + node.widgetUniqueName); } }); node.on('close', function (removed, closedDoneCallback) { if (removed) { // Cancellazione nodo logger.debug("is being removed from flow"); node.deleteEmitter(); } else { // Riavvio nodo logger.debug("is being rebooted"); node.notRestart = true; node.ws.terminate(); } clearInterval(node.pingInterval); closedDoneCallback(); }); node.wsOpenCallback = function () { if (node.dashboardId != null && node.dashboardId != "") { node.status({ fill: "green", shape: "dot", text: "connected to " + wsServer }); if (RED.settings.hasOwnProperty('httpRoot')) { if (RED.settings.httpRoot !== '/') { node.httpRoot = RED.settings.httpRoot; } else { node.httpRoot = null; } } var payload = { msgType: "AddEmitter", name: node.name, valueType: node.valueType, user: node.username, startValue: JSON.stringify({"form":[]}), domainType: node.domain, endPointPort: (RED.settings.externalPort ? RED.settings.externalPort : 1895), endPointHost: (RED.settings.dashInNodeBaseUrl ? RED.settings.dashInNodeBaseUrl : "'0.0.0.0'"), httpRoot: node.httpRoot, appId: uid, flowId: node.z, flowName: node.flowName, nodeId: node.id, widgetType: "widgetForm", widgetTitle: node.widgetTitle, dashboardTitle: "", dashboardId: node.dashboardId, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid) }; logger.info("AddEmitter sent to WebSocket"); logger.debug("AddEmitter sent to WebSocket: " + JSON.stringify(payload)); if (node.pingInterval == null) { node.pingInterval = setInterval(function () { logger.silly("ping");; if (node.ws != null) { try { node.ws.ping(); } catch (e) { logger.debug("Errore on Ping " + e); } } }, 30000); } logger.info("is going connect to WS"); if (payload.accessToken != "") { setTimeout(function () { node.ws.send(JSON.stringify(payload)); }, Math.random() * 2000) } else { node.status({ fill: "red", shape: "dot", text: "Authentication Problem" }); logger.error("Problem with accessToken: " + payload.accessToken); } } else { node.status({ fill: "red", shape: "dot", text: "No dashboard selected" }); logger.error("No dashboard selected, dashboard Id: " + node.dashboardId); } }; node.wsMessageCallback = function (data) { var response = JSON.parse(data); logger.debug("Message received from WebSocket: " + data); switch (response.msgType) { case "AddEmitter": if (response.result === "Ok") { if (response.lastValue) { node.lastValue = response.lastValue; } logger.info("WebSocket server correctly AddEmitter type: " + response.result); node.status({ fill: "green", shape: "dot", text: "Created on dashboard" }); if (node.intervalID != null) { clearInterval(node.intervalID); node.intervalID = null; } if (typeof node.widgetUniqueName == "undefined") { if (node.ws != null) { node.ws.removeListener('error', node.wsErrorCallback); node.ws.removeListener('open', node.wsOpenCallback); node.ws.removeListener('message', node.wsMessageCallback); node.ws.removeListener('close', node.wsCloseCallback); node.ws.removeListener('pong', node.wsHeartbeatCallback); node.ws = null; } else { logger.debug("Why ws is null? I am in node.wsMessageCallback"); } node.wsInit(); } node.widgetUniqueName = response.widgetUniqueName; node.deliverInitialConfig(); } else { logger.error("WebSocket server could not AddEmitter type: " + response.error); node.status({ fill: "red", shape: "dot", text: response.error }); node.error(response.error); } break; case "DelEmitter": if (response.result === "Ok") { logger.info("WebSocket server correctly DelEmitter type: " + response.result); } else { logger.error("WebSocket server could not DelEmitter type: " + response.error); } logger.info("Closing webSocket server after DelMetric message"); node.notRestart = true; node.ws.terminate(); break; case "DataToEmitter": logger.info("WebSocket server correctly DataToEmitter type: " + response.newValue); if (response.newValue != "dashboardDeleted") { node.status({ fill: "green", shape: "dot", text: "connected to " + wsServer }); if (response.newValue) { try { node.dropdwonOrderedList = response.newValue.options; node.selected = response.newValue.selected; node.lastValue = response.newValue; var msg = { payload: node.lastValue }; node.send(msg); } catch (e) { logger.error("Problem Parsing data " + response.newValue); } } var ackMessage = JSON.stringify({ msgType: "DataToEmitterAck", widgetUniqueName: node.widgetUniqueName, result: "Ok", msgId: response.msgId, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid) }) node.ws.send(ackMessage); logger.debug("DataToEmitterAck sent to WebSocket: " + ackMessage); } else { node.status({ fill: "red", shape: "dot", text: "Dashboard deleted" }); } s4cUtility.eventLog(RED, msg, msg, config, "Node-Red", "Dashboard", RED.settings.httpRoot + node.name, "RX"); break; default: logger.debug(response.msgType); break; } }; node.wsCloseCallback = function (e) { logger.warn("Closed WebSocket. Reason: " + e); if (!(node.dashboardId != null && node.dashboardId != "")) { /*node.status({ fill: "red", shape: "dot", text: "No Dashboard Selected" });*/ logger.error("No dashboard selected, dashboard Id: " + node.dashboardId); } else { node.status({ fill: "red", shape: "ring", text: "lost connection from " + wsServer }); logger.warn("Lost connection from " + wsServer); } if (node.ws != null) { node.ws.removeListener('error', node.wsErrorCallback); node.ws.removeListener('open', node.wsOpenCallback); node.ws.removeListener('message', node.wsMessageCallback); node.ws.removeListener('close', node.wsCloseCallback); node.ws.removeListener('pong', node.wsHeartbeatCallback); node.ws = null; } else { logger.debug("Why ws is null? I am in node.wsCloseCallback"); } var wsServerRetryActive = (RED.settings.wsServerRetryActive ? RED.settings.wsServerRetryActive : "yes"); var wsServerRetryTime = (RED.settings.wsServerRetryTime ? RED.settings.wsServerRetryTime : 30); logger.debug("wsServerRetryActive: " + wsServerRetryActive + " node.notRestart: " + node.notRestart); if (wsServerRetryActive === 'yes' && !node.notRestart) { logger.info("will try to reconnect to WebSocket in " + parseInt(wsServerRetryTime) + "s"); if (!node.intervalID) { node.intervalID = setInterval(node.wsInit, parseInt(wsServerRetryTime) * 1000); } } node.notRestart = false; }; node.wsErrorCallback = function (e) { logger.error("got WebSocket error: " + e); }; node.deleteEmitter = function () { var newMsg = { msgType: "DelEmitter", nodeId: node.id, user: node.username, appId: uid, flowId: node.z, flowName: node.flowName, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid) }; try { node.ws.send(newMsg); logger.info("DelEmitter via webSocket"); logger.debug("DelEmitter via webSocket: " + newMsg); } catch (e) { logger.error("Error DelEmitter via webSocket: " + e); } }; node.wsHeartbeatCallback = function () { logger.silly("heartbeat callback"); }; //Lasciarlo, altrimenti va in timeout!!! https://nodered.org/docs/creating-nodes/node-js#closing-the-node node.closedDoneCallback = function () { logger.info("has been closed"); }; node.wsInit = function (e) { logger.info("is trying to open WebSocket"); try { node.status({ fill: "yellow", shape: "dot", text: "connecting to " + wsServer }); if (node.ws == null) { node.ws = new WebSocket(wsServer, { origin: wsServerHttpOrigin }); node.ws.on('error', node.wsErrorCallback); node.ws.on('open', node.wsOpenCallback); node.ws.on('message', node.wsMessageCallback); node.ws.on('close', node.wsCloseCallback); node.ws.on('pong', node.wsHeartbeatCallback); node.wsStart = new Date().getTime(); } else { logger.error("already open WebSocket"); } } catch (e) { logger.error("could not open WebSocket"); node.status({ fill: "red", shape: "ring", text: "unable to connect to " + wsServer }); node.wsCloseCallback(); } }; node.deliverInitialConfig = function() { logger.debug("Delivering initial configuration"); var timeout = 0; if ((new Date().getTime() - node.wsStart) > parseInt(RED.settings.wsReconnectTimeout ? RED.settings.wsReconnectTimeout : 1200) * 1000) { if (node.ws != null) { node.ws.removeListener('error', node.wsErrorCallback); node.ws.removeListener('open', node.wsOpenCallback); node.ws.removeListener('message', node.wsMessageCallback); node.ws.removeListener('close', node.wsCloseCallback); node.ws.removeListener('pong', node.wsHeartbeatCallback); node.notRestart = true; node.ws.terminate(); node.ws = null; } else { logger.debug("Why ws is null? I am in node.on('input'"); } node.ws = new WebSocket(wsServer, { origin: wsServerHttpOrigin }); node.ws.on('error', node.wsErrorCallback); node.ws.on('open', node.wsOpenCallback); node.ws.on('message', node.wsMessageCallback); node.ws.on('close', node.wsCloseCallback); node.ws.on('pong', node.wsHeartbeatCallback); logger.debug("is reconnetting to open WebSocket"); timeout = 1000; } node.wsStart = new Date().getTime(); if (typeof node.widgetUniqueName != "undefined" && node.widgetUniqueName != "") { /*var currentdashboardFormOrderedList = []; for (var i = 0; i < node.dropdwonOrderedList.length; i++) { if (node.dropdwonOrderedList[i].value != "") { currentdashboardFormOrderedList.push([node.dropdwonOrderedList[i].label, node.dropdwonOrderedList[i].value]); } } if(currentdashboardFormOrderedList.length > 0) {*/ var newMetricData = { msgType: "SendToEmitter", widgetUniqueName: node.widgetUniqueName, dashboardId: node.dashboardId, value: JSON.stringify({ "options": node.dropdwonOrderedList, "selected": "" }) }; setTimeout(function () { try { logger.info("Send SendToEmitter to WebSocket: " + newMetricData.value); logger.debug("Send SendToEmitter to WebSocket: " + JSON.stringify(newMetricData)); node.ws.send(JSON.stringify(newMetricData)); } catch (e) { logger.error("Error sending data to WebSocket : " + e); } }, timeout); s4cUtility.eventLog(RED, node.dropdwonOrderedList, newMetricData, config, "Node-Red", "Dashboard", wsServer, "TX"); // } } else { node.error("Maybe the dashboardForm is not created on dashboard"); logger.error("Error, the dashboardForm is not created on dashboard. node.widgetUniqueName: " + node.widgetUniqueName); } }; //Inizio del "main" try { node.wsInit(); } catch (e) { logger.error("got main exception connecting to WebSocket"); } } RED.nodes.registerType("dashboardForm", DashboardForm); };