module.exports = function (RED) { function valve(config) { //create node RED.nodes.createNode(this, config); //call this => node so whenver you want to call a node function type node and the function behind it var node = this; try { const Valve = require("./dependencies/valveClass"); // Importeer de valve class const OutputUtils = require("../generalFunctions/helper/outputUtils"); // Importeer de OutputUtils class const valveConfig = { // Configuratie van de valve general: { name: config.name || "Default Valve", id: node.id, logging: { enabled: config.eneableLog, logLevel: config.logLevel } }, asset: { supplier: config.supplier || "Unknown", /* NOT USED type: config.valveType || "generic", subType: config.subType || "generic", model: config.model || "generic", valveCurve: config.valveCurve */ } }; const stateConfig = { // Configuratie van de state general: { logging: { enabled: config.eneableLog, logLevel: config.logLevel } }, /* NOT USED movement: { speed: Number(config.speed) }, time: { starting: Number(config.startup), warmingup: Number(config.warmup), stopping: Number(config.shutdown), coolingdown: Number(config.cooldown) } */ }; // Create valve instance const v = new Valve(valveConfig, stateConfig); // put m on node memory as source node.source = v; //load output utils const output = new OutputUtils(); //Hier worden node-red statussen en metingen geupdate function updateNodeStatus() { try { const mode = v.currentMode; // modus is bijv. auto, manual, etc. const state = v.state.getCurrentState(); //is bijv. operational, idle, off, etc. const flow = Math.round(v.measurements.type("flow").variant("predicted").position("downstream").getCurrentValue()); let deltaP = v.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue(); if (deltaP !== null) { deltaP = parseFloat(deltaP.toFixed(0)); } //afronden op 4 decimalen indien geen "null" if(isNaN(deltaP)) { deltaP = "∞"; } const roundedPosition = Math.round(v.state.getCurrentPosition() * 100) / 100; let symbolState; switch(state){ case "off": symbolState = "⬛"; break; case "idle": symbolState = "⏸️"; break; case "operational": symbolState = "⏵️"; break; case "starting": symbolState = "⏯️"; break; case "warmingup": symbolState = "🔄"; break; case "accelerating": symbolState = "⏩"; break; case "stopping": symbolState = "⏹️"; break; case "coolingdown": symbolState = "❄️"; break; case "decelerating": symbolState = "⏪"; break; } let status; switch (state) { case "off": status = { fill: "red", shape: "dot", text: `${mode}: OFF` }; break; case "idle": status = { fill: "blue", shape: "dot", text: `${mode}: ${symbolState}` }; break; case "operational": status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ΔP${deltaP} mbar`}; //deltaP toegevoegd break; case "starting": status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; break; case "warmingup": status = { fill: "green", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ΔP${deltaP} mbar`}; //deltaP toegevoegd break; case "accelerating": status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} | ${roundedPosition}% | 💨${flow}m³/h | ΔP${deltaP} mbar` }; //deltaP toegevoegd break; case "stopping": status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; break; case "coolingdown": status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState}` }; break; case "decelerating": status = { fill: "yellow", shape: "dot", text: `${mode}: ${symbolState} - ${roundedPosition}% | 💨${flow}m³/h | ΔP${deltaP} mbar`}; //deltaP toegevoegd break; default: status = { fill: "grey", shape: "dot", text: `${mode}: ${symbolState}` }; } return status; } catch (error) { node.error("Error in updateNodeStatus: " + error.message); return { fill: "red", shape: "ring", text: "Status Error" }; } } function tick() { // versturen van output messages --> tick van tick op de klop. Is tijd based en niet event based try { const status = updateNodeStatus(); node.status(status); //v.tick(); //get output const classOutput = v.getOutput(); const dbOutput = output.formatMsg(classOutput, v.config, "influxdb"); const pOutput = output.formatMsg(classOutput, v.config, "process"); //only send output on values that changed let msgs = []; msgs[0] = pOutput; msgs[1] = dbOutput; node.send(msgs); } catch (error) { node.error("Error in tick function: " + error); node.status({ fill: "red", shape: "ring", text: "Tick Error" }); } } // register child on first output this timeout is needed because of node - red stuff setTimeout( () => { /*---execute code on first start----*/ let msgs = []; msgs[2] = { topic : "registerChild" , payload: node.id, positionVsParent: "upStream" }; msgs[3] = { topic : "registerChild" , payload: node.id, positionVsParent: "downStream" }; //send msg node.send(msgs); }, 100 ); //declare refresh interval internal node setTimeout( () => { //---execute code on first start---- this.interval_id = setInterval(function(){ tick() },1000) }, 1000 ); node.on("input", function(msg, send, done) { // Functie die wordt aangeroepen wanneer er een input wordt ontvangen console.log("CKECK! Input received: ", msg.topic, msg.payload); // CHECKPOINT try { let result; switch(msg.topic) { case 'registerChild': const childId = msg.payload; const childObj = RED.nodes.getNode(childId); v.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent); break; case 'setMode': v.setMode(msg.payload); break; case 'execSequence': const { source: seqSource, action: seqAction, parameter } = msg.payload; v.handleInput(seqSource, seqAction, parameter); break; case 'execMovement': const { source: mvSource, action: mvAction, setpoint } = msg.payload; v.handleInput(mvSource, mvAction, Number(setpoint)); break; case 'emergencystop': const { source: esSource, action: esAction } = msg.payload; v.handleInput(esSource, esAction); break; case 'showcurve': v.showCurve(); send({ topic : "Showing curve" , payload: v.showCurve() }); break; case 'newFlow': //Als nieuwe flow van header node dan moet deltaP weer opnieuw worden berekend en doorgegeven aan header node const { source: nfSource, action: nfAction, parameter: nfParameter } = msg.payload; //parameter is new flow, action should be "calcNewDeltaP" v.handleInput(nfSource, nfAction, nfParameter); } if (done) done(); } catch (error) { node.error("Error processing input: " + error.message); if (done) done(error); } }); node.on('close', function(done) { // Functie die wordt aangeroepen wanneer de node wordt gesloten if (node.interval_id) clearTimeout(node.interval_id); if (node.tick_interval) clearInterval(node.tick_interval); if (done) done(); }); } catch (error) { node.error("Fatal error in node initialization: " + error.stack); node.status({fill: "red", shape: "ring", text: "Fatal Error"}); } } RED.nodes.registerType("valve", valve); };