console.log('Loading measurement.js module...'); // load helper modules from the generalFunctions folder const { outputUtils, MenuManager } = require('generalFunctions'); //internal node-red node dependencies this is the class that will handle the measurement const Measurement = require("./dependencies/measurement/measurement"); console.log('All dependencies loaded successfully'); module.exports = function (RED) { function measurement(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{ //load user defined config in the node-red UI const mConfig={ general: { name: config.name, id: node.id, unit: config.unit, logging:{ logLevel: config.logLevel, enabled: config.enableLog, }, }, asset: { tagCode: config.assetTagCode, supplier: config.supplier, subType: config.subType, model: config.model, }, scaling:{ enabled: config.scaling, inputMin: config.i_min, inputMax: config.i_max, absMin: config.o_min, absMax: config.o_max, offset: config.i_offset }, smoothing: { smoothWindow: config.count, smoothMethod: config.smooth_method, }, simulation: { enabled: config.simulator, }, } //make new measurement on creation to work with. const m = new Measurement(mConfig); // put m on node memory as source node.source = m; //load output utils const output = new outputUtils(); function updateNodeStatus(val) { //display status node.status({ fill: "green", shape: "dot", text: val + " " + mConfig.general.unit }); } //Update status only on event change m.emitter.on('mAbs', (val) => { updateNodeStatus(val); }); //never ending functions function tick(){ //kick class ticks for time move m.tick(); //get output const classOutput = m.getOutput(); const dbOutput = output.formatMsg(classOutput, m.config, "influxdb"); const pOutput = output.formatMsg(classOutput, m.config, "process"); //only send output on values that changed let msgs = []; msgs[0] = pOutput; msgs[1] = dbOutput; node.send(msgs); } // 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 this.send(msgs); }, 100 ); //declare refresh interval internal node setTimeout( () => { /*---execute code on first start----*/ this.intervalId = setInterval(function(){ tick() },1000) }, 1000 ); //-------------------------------------------------------------------->>what to do on input node.on("input", function (msg,send,done) { if(msg.topic == "simulator" ){ m.toggleSimulation(); } if(msg.topic == "outlierDetection" ){ m.toggleOutlierDetection(); } if(msg.topic == "calibrate" ){ m.calibrate(); } if(msg.topic == "measurement" && typeof msg.payload == "number"){ //feed input into the measurement node and calculate output m.inputValue = parseFloat(msg.payload); } done(); }); // tidy up any async code here - shutdown connections and so on. node.on('close', function(done) { if (node.intervalId) clearTimeout(node.intervalId); if (node.tickInterval) clearInterval(node.tickInterval); if (done) done(); }); }catch(e){ console.log(e); } } RED.nodes.registerType("measurement", measurement); const menuManager = new MenuManager(); RED.httpAdmin.get("/measurement/menu.js", (req, res) => { try { const script = menuManager.createEndpoint('measurement', ['asset']); res.set('Content-Type', 'application/javascript').send(script); } catch (error) { console.error("Error creating measurement menu script:", error); res.status(500).send(`// Failed to generate menu data: ${error.message}`); } }); console.log('Measurement node and its menu data endpoint are registered.'); };