UNPKG

node-red-contrib-snap4city-d3-dashboard-widgets

Version:

Nodes for Snap4city project for D3 Dashboard Widgets

353 lines (316 loc) 15.3 kB
/* NODE-RED-CONTRIB-SNAP4CITY-D3-DASHBOARD-WIDGETS Copyright (C) 2022 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/>. Please note that the usage of this module is subjected to the D3.JS license that can be recovered from https://github.com/d3/d3 */ module.exports = function (RED) { function DashboardCalendarNode(config) { var WebSocket = require('ws'); var util = require('util'); var s4cUtility = require("./snap4city-utility.js"); var uid = s4cUtility.retrieveAppID(RED); RED.nodes.createNode(this, config); var node = this; 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; node.name = config.name; node.username = config.username; node.flowName = config.flowName; node.selectedDashboardId = config.selectedDashboardId; node.dashboardId = config.dashboardId; node.metricName = "NR_" + node.id.replace(".", "_"); node.metricType = config.metricType; node.startValue = config.startValue; node.metricShortDesc = config.metricName; node.metricFullDesc = config.metricName; node.httpRoot = null; node.widgetUniqueName = null; node.on('input', function (msg) { util.log("Flow input received for dashboard-calendar node " + node.name + ": " + 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 { util.log("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); util.log("dashboard-calendar node " + node.name + " is reconnetting to open WebSocket"); timeout = 1000; } node.wsStart = new Date().getTime(); var newMetricData = { msgType: "AddMetricData", nodeId: node.id, metricName: encodeURIComponent(node.metricName), metricType: node.metricType, newValue: msg.payload, appId: uid, user: node.username, flowId: node.z, flowName: node.flowName, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid) }; setTimeout(function () { try { node.ws.send(JSON.stringify(newMetricData)); } catch (e) { util.log("Error sending data to WebSocket for dashboard-calendar node " + node.name + ": " + e); } }, timeout); s4cUtility.eventLog(RED, msg, newMetricData, config, "Node-Red", "Dashboard", wsServer, "TX"); }); node.on('close', function (removed, closedDoneCallback) { if (removed) { // Cancellazione nodo util.log("dashboard-calendar node " + node.name + " is being removed from flow"); node.deleteMetric(); } else { // Riavvio nodo util.log("dashboard-calendar node " + node.name + " 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; } } //Registrazione della nuova metrica presso il Dashboard Manager var payload = { msgType: "AddEditMetric", metricName: encodeURIComponent(node.metricName), metricType: node.metricType, nodeId: node.id, startValue: node.startValue, user: node.username, metricShortDesc: node.metricShortDesc, metricFullDesc: node.metricFullDesc, appId: uid, flowId: node.z, flowName: node.flowName, widgetType: "widgetCalendar", widgetTitle: node.name, dashboardTitle: "", dashboardId: node.dashboardId, httpRoot: node.httpRoot, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid), widgetUniqueName: node.widgetUniqueName }; //util.log(payload); if (node.pingInterval == null) { node.pingInterval = setInterval(function () { //util.log(node.name + "ping"); if (node.ws != null) { try { node.ws.ping(); } catch (e) { util.log("Errore on Ping " + e); } } }, 30000); } util.log("dashboard-calendar node " + node.name + " IS GOING TO CONNECT 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" }); } } else { node.status({ fill: "red", shape: "dot", text: "No dashboard selected" }); } }; node.wsMessageCallback = function (data) { var response = JSON.parse(data); util.log(response); switch (response.msgType) { case "AddEditMetric": if (response.result === "Ok") { node.widgetUniqueName = response.widgetUniqueName; util.log("WebSocket server correctly added/edited metric type for dashboard-calendar node " + node.name + ": " + response.result); if (node.intervalID != null) { clearInterval(node.intervalID); node.intervalID = null; } } else { //TBD - CASI NEGATIVI DA FARE util.log("WebSocket server could not add/edit metric type for dashboard-calendar node " + node.name + ": " + response.result); node.status({ fill: "red", shape: "dot", text: response.error }); node.error(response.error); } break; case "DelMetric": if (response.result === "Ok") { util.log("WebSocket server correctly deleted metric type for dashboard-calendar node " + node.name + ": " + response.result); } else { //TBD - CASI NEGATIVI DA FARE util.log("WebSocket server could not delete metric type for dashboard-calendar node " + node.name + ": " + response.result); } util.log("Closing webSocket server for dashboard-calendar node " + node.name); node.notRestart = true; node.ws.terminate(); break; default: util.log(response.msgType); break; } }; node.wsCloseCallback = function (e) { util.log("dashboard-calendar node " + node.name + " closed WebSocket"); util.log("dashboard-calendar closed reason " + e); if (!(node.dashboardId != null && node.dashboardId != "")) { node.status({ fill: "red", shape: "dot", text: "No dashboard selected" }); } else { node.status({ fill: "red", shape: "ring", text: "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 { util.log("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); //util.log("dashboard-calendar wsServerRetryActive: " + wsServerRetryActive); //util.log("dashboard-calendar node.notRestart: " + node.notRestart); if (wsServerRetryActive === 'yes' && !node.notRestart) { util.log("dashboard-calendar node " + node.name + " 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) { util.log("dashboard-calendar node " + node.name + " got WebSocket error: " + e); }; node.deleteMetric = function () { util.log("Deleting metric via webSocket for dashboard-calendar node " + node.name); var newMsg = { msgType: "DelMetric", nodeId: node.id, metricName: encodeURIComponent(node.metricName), metricType: node.metricType, user: node.username, appId: uid, flowId: node.z, flowName: node.flowName, accessToken: s4cUtility.retrieveAccessToken(RED, node, config.authentication, uid) }; try { node.ws.send(JSON.stringify(newMsg)); } catch (e) { util.log("Error deleting metric via webSocket for dashboard-calendar node " + node.name + ": " + e); } }; node.wsHeartbeatCallback = function () { logger.silly("heartbeat callback"); }; //Lasciare così, sennò va in timeout!!! https://nodered.org/docs/creating-nodes/node-js#closing-the-node node.closedDoneCallback = function () { util.log("dashboard-calendar node " + node.name + " has been closed"); }; node.wsInit = function (e) { util.log("dashboard-calendar node " + node.name + " 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 { util.log("dashboard-calendar node " + node.name + " already open WebSocket"); } } catch (e) { util.log("dashboard-calendar node " + node.name + " could not open WebSocket"); node.status({ fill: "red", shape: "ring", text: "unable to connect to " + wsServer }); node.wsCloseCallback(); } }; //Inizio del "main" try { node.wsInit(); } catch (e) { util.log("dashboard-calendar node " + node.name + " got main exception connecting to WebSocket"); } } RED.nodes.registerType("dashboard-calendar", DashboardCalendarNode); };