Files
machineGroupControl/ggc.js
znetsixe 33a62674c6 update
2025-05-26 17:43:20 +02:00

223 lines
6.9 KiB
JavaScript

module.exports = function (RED) {
function ggc(config) {
RED.nodes.createNode(this, config);
var node = this;
try {
// Load Machine class and curve data
const Ggc = require("./dependencies/ggc/ggc");
const OutputUtils = require("../generalFunctions/helper/outputUtils");
const ggcConfig = {
general: {
name: config.name || "Unknown",
id: node.id,
logging: {
enabled: config.eneableLog,
logLevel: config.logLevel
}
},
asset: {
supplier: config.supplier || "Unknown",
type: config.machineType || "generic",
subType: config.subType || "generic",
model: config.model || "generic",
}
};
const stateConfig = {
general: {
logging: {
enabled: config.eneableLog,
logLevel: config.logLevel
}
},
movement: {
speed: Number(config.speed)
},
time: {
starting: Number(config.startup),
warmingup: Number(config.warmup),
stopping: Number(config.shutdown),
coolingdown: Number(config.cooldown)
}
};
// Create machine instance
const ggc = new Ggc(ggcConfig, stateConfig);
// put m on node memory as source
node.source = ggc;
//load output utils
const output = new OutputUtils();
function updateNodeStatus() {
try {
const mode = "auto";//ggc.currentMode;
const state = ggc.state;
const totPower = Math.round(ggc.measurements.type("power").variant("measured").position('wire').getCurrentValue()) || 0;
const SYMBOL_MAP = {
gateGroupClosed: "G1🔴 & G2🔴",
gateGroupOpened: "G1🟢 & G2🟢",
gateGroupStopped: "G1🟥 & G2🟥",
gateGroupAccelerating: "G1🟡 & G2🟡",
gateGroupDecelerating: "G1🟠 & G2🟠",
gateOneAccelerating: "G1🟡 & G2🟢",
gateTwoAccelerating: "G1🟢 & G2🟡",
gateOneDecelerating: "G1🟠 & G2🟢",
gateTwoDecelerating: "G1🟢 & G2🟠",
oneGateOpenOneGateClosed: "G1🟢 & G2🔴",
gatePushingStop: "G1⚡ & G2⚡",
unknown: "❓ & ❓",
};
symbolState = SYMBOL_MAP[state] || "Unknown";
const position = "" ; //ggc.getGatePositions();
const roundedPosition = Math.round(position * 100) / 100;
let status;
switch (state) {
// —— gateGroup states first ——
case "gateGroupClosed":
status = {
fill: "red",
shape: "dot",
text: `${mode}: ${symbolState}`
};
break;
case "gateGroupOpened":
status = {
fill: "green",
shape: "dot",
text: `${mode}: ${symbolState}`
};
break;
case "gateGroupStopped":
status = {
fill: "red",
shape: "dot",
text: `${mode}: ${symbolState}`
};
break;
case "oneGateOpenOneGateClosed":
status = {
fill: "green",
shape: "dot",
text: `${mode}: ${symbolState}`
};
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() {
try {
const status = updateNodeStatus();
node.status(status);
//get output
const classOutput = ggc.getOutput();
const dbOutput = output.formatMsg(classOutput, ggc.config, "influxdb");
const pOutput = output.formatMsg(classOutput, ggc.config, "process");
//console.log(pOutput);
//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
this.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) {
try {
/* Update to complete event based node by putting the tick function after an input event */
switch(msg.topic) {
case 'registerChild':
const childId = msg.payload;
const childObj = RED.nodes.getNode(childId);
ggc.childRegistrationUtils.registerChild(childObj.source ,msg.positionVsParent);
break;
case 'setMode':
ggc.setMode(msg.payload);
break;
case 'execSequence':
const { source, action, parameter } = msg.payload;
ggc.handleInput(source, action, parameter);
break;
case 'emergencystop':
const { source: esSource, action: esAction } = msg.payload;
ggc.handleInput(esSource, esAction);
break;
case 'safetySensor':
if(typeof msg.payload === "boolean") {
const safetySensor = msg.payload;
ggc.safetySensor = safetySensor;
}
break;
}
if (done) done();
} catch (error) {
node.error("Error processing input: " + error.message);
if (done) done(error);
}
});
node.on('close', function(done) {
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("ggc", ggc);
};