UNPKG

@pickdata/node-red-contrib-emod-simulator

Version:

eManager is an IoT controller designed with eMOD tech for monitoring, control and automation applications. The following nodes are pre-installed in all eMOD devices, although here we present a simulator to practice & test our modular industrial solution.

249 lines (208 loc) 10.7 kB
/** Copyright (c) 2020,2021 PickData SL (https://www.pickdata.net/) All rights reserved. node-red-contrib-emod-simulator - The BSD 3-Clause License **/ module.exports = function(RED) { const ai7pr2_simulator = require('@pickdata/emod-js-simulator').AI7PR2; function EMOD7AI2PRS(config) { RED.nodes.createNode(this,config); var node = this; node.configNode = RED.nodes.getNode(config.modConfig); node.modNumEnabled = config.modNumEnabled; node.modNum = parseInt(config.modNum); if (!node.modNumEnabled) { node.modNum = 1; } node.config_error = false; if (node.configNode == null) { node.status({fill:"red",shape:"ring",text:"Config error"}); node.error("Node has no configuration"); node.config_error = true; return; } var compareModConfig = node.configNode.event_mode == "poll" ? ( function (node, other_node) { // Ambos nodos deberían tener la misma configuración, ya que corresponden al mismo módulo físico. if (!other_node.config_error && other_node.configNode.id != node.configNode.id) { throw new Error("Configuration mismatch between nodes "+node.id+" and "+other_node.id); } } ) : ( function (node, other_node) { // En modo "event" sólo puede haber un único nodo por módulo físico. throw new Error("Only a single node can be configured in event mode"); } ); try { RED.nodes.eachNode(function(n) { //Si node-red aún no ha instanciado el nodo n o está presente en un flow deshabilitado, esta función devuelve null y puede ser ignorado. var other_node = RED.nodes.getNode(n.id); if (other_node != null && other_node.id != node.id) { if (other_node.type == node.type && other_node.modNum == node.modNum) { // Esta función se llama con "other_node" para todos los nodos instanciados que sean del mismo tipo y número (mismo módulo físico) que "node" compareModConfig(node, other_node); } } }); } catch (e) { node.error(e.message); node.status({fill:"red",shape:"ring",text:"Config error"}); node.config_error = true; return; } node.simulator = ai7pr2_simulator.getInstance(node.modNum); node.simulator.relaySetPulseWitdth(node.configNode.pulse_width_ms); node.simulator.inputsSetThrehold(node.configNode.threshold); node.simulator.inputsSetRange(node.configNode.low_limit, node.configNode.high_limit); switch (node.configNode.event_mode) { case "poll": node.simulator.inputsSetIntervalCallback(null); node.simulator.inputsSetWithinRangeCallback(null); node.simulator.inputsSetOutOfRangeCallback(null); node.simulator.inputsSetThresholdCallback(null); break; case "interval": node.simulator.inputsSetIntervalCallback(function(index, value) { if ((node.input_mode_mask & (1 << index)) != 0) { node.send([{payload: {"current": {"index": index, "value": value}}}, null]); } else { node.send([{payload: {"voltage": {"index": index, "value": value}}}, null]); } }, node.configNode.interval_ms); node.simulator.inputsSetWithinRangeCallback(null); node.simulator.inputsSetOutOfRangeCallback(null); node.simulator.inputsSetThresholdCallback(null); break; case "threshold": node.simulator.inputsSetIntervalCallback(null); node.simulator.inputsSetWithinRangeCallback(null); node.simulator.inputsSetOutOfRangeCallback(null); node.simulator.inputsSetThresholdCallback(function(index, value) { if ((node.input_mode_mask & (1 << index)) != 0) { node.send([{payload: {"current": {"index": index, "value": value}}}, null]); } else { node.send([{payload: {"voltage": {"index": index, "value": value}}}, null]); } }); break; case "within-range": node.simulator.inputsSetIntervalCallback(null); node.simulator.inputsSetWithinRangeCallback(function(index, value) { if ((node.input_mode_mask & (1 << index)) != 0) { node.send([{payload: {"current": {"index": index, "value": value}}}, null]); } else { node.send([{payload: {"voltage": {"index": index, "value": value}}}, null]); } }); node.simulator.inputsSetOutOfRangeCallback(null); node.simulator.inputsSetThresholdCallback(null); break; case "out-of-range": node.simulator.inputsSetIntervalCallback(null); node.simulator.inputsSetWithinRangeCallback(null); node.simulator.inputsSetOutOfRangeCallback(function(index, value) { if ((node.input_mode_mask & (1 << index)) != 0) { node.send([{payload: {"current": {"index": index, "value": value}}}, null]); } else { node.send([{payload: {"voltage": {"index": index, "value": value}}}, null]); } }); node.simulator.inputsSetThresholdCallback(null); break; } node.input_mode_mask = 0; for (var i = 0; i < 7; i++) { if (node.configNode.mode[i] === "current") { node.input_mode_mask |= 1 << i; } } node.on('input', function(msg) { if ("relay_set_one" in msg.payload && typeof msg.payload.relay_set_one === 'object' && msg.payload.relay_set_one !== null) { var relay_index = msg.payload.relay_set_one.index; var relay_value = msg.payload.relay_set_one.value; if (!Number.isInteger(relay_index) || relay_index < 0 || relay_index > 2) { node.error("relay index must be in range."); return; } if (typeof relay_value !== "boolean") { node.error("relay value must be a boolean."); return; } node.simulator.relaySetOne(relay_index, relay_value); // Sends current status var relay_status = node.simulator.relayGetOne(relay_index); node.send([null, {payload: {status: {index: relay_index, value: relay_status}}}]); } else if ("relay_set_all" in msg.payload && typeof msg.payload.relay_set_all === "boolean") { node.simulator.relaySetAll(msg.payload.relay_set_all); // Sends current status var relay_status = node.simulator.relayGetAll(); node.send([null, {payload: {all_status: relay_status}}]); } else if ("relay_set" in msg.payload && Array.isArray(msg.payload.relay_set)) { var relay_status = msg.payload.relay_set; if (relay_status.length != 2) { node.error("node input array length must be 2"); return; } node.simulator.relaySet(relay_status); // Sends current status var relay_status = node.simulator.relayGetAll(); node.send([null, {payload: {all_status: relay_status}}]); } else if ("relay_get_all" in msg.payload) { var relay_status = node.simulator.relayGetAll(); node.send([null, {payload: {all_status: relay_status}}]); } else if ("relay_get_one" in msg.payload && Number.isInteger(msg.payload.relay_get_one)) { var relay_index = msg.payload.relay_get_one; if (relay_index < 0 || relay_index > 1) { node.error("relay index must be in range."); return; } var relay_status = node.simulator.relayGetOne(relay_index); node.send([null, {payload: {status: {index: relay_index, value: relay_status}}}]); } else if ("ai_get_all" in msg.payload) { if (node.configNode.event_mode != "poll") { node.warn("Polling request made in non-polling mode"); return; } var samples = node.simulator.inputsGetAll(); var values = new Array(samples.length).fill({}); for (var i = 0; i < samples.length; i++) { if ((node.input_mode_mask & (1 << i)) != 0) { values[i] = {"current": {"index": i, "value": samples[i]}}; } else { values[i] = {"voltage": {"index": i, "value": samples[i]}}; } } node.send([{payload: {values: values}}, null]); } else if ("ai_get_one" in msg.payload) { if (node.configNode.event_mode != "poll") { node.warn("Polling request made in non-polling mode"); return; } var ai_index = msg.payload.ai_get_one; if (ai_index < 0 || ai_index > 6) { node.error("input index must be in range."); return; } var samples = node.simulator.inputsGetOne(ai_index); if ((node.input_mode_mask & (1 << ai_index)) != 0) { node.send([{payload: {"current": {"index": ai_index, "value": samples}}}, null]); } else { node.send([{payload: {"voltage": {"index": ai_index, "value": samples}}}, null]); } } else { node.error("unknown method call."); } }); node.simulator.startSimulation(); node.status({}); } RED.nodes.registerType("7AI+2PR-S",EMOD7AI2PRS); }