|
|
|
|
@@ -63,9 +63,7 @@ class ValveGroupControl {
|
|
|
|
|
// Initialize measurements
|
|
|
|
|
this.measurements = new MeasurementContainer();
|
|
|
|
|
this.child = {};
|
|
|
|
|
this.valves = {}; // hold child object so we can get information from its child valves
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// this.valves = {}; // this.child; // hold child object so we can get information from its child valves
|
|
|
|
|
|
|
|
|
|
// Initialize variables
|
|
|
|
|
this.maxDeltaP = 0; // max deltaP is 0 als er geen child valves zijn
|
|
|
|
|
@@ -74,9 +72,71 @@ class ValveGroupControl {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerOnChildEvents() {}
|
|
|
|
|
registerChild(child, softwareType) { //checken welke child er zijn
|
|
|
|
|
console.log('=== DEBUGGING this.child ===');
|
|
|
|
|
console.log('Type of this.child:', typeof this.child);
|
|
|
|
|
console.log('this.child:', this.child);
|
|
|
|
|
|
|
|
|
|
if (Object.keys(this.child).length === 0) {
|
|
|
|
|
console.log('✅ this.child is empty');
|
|
|
|
|
} else {
|
|
|
|
|
console.log('✅ this.child contains:', Object.keys(this.child).length, 'items');
|
|
|
|
|
Object.keys(this.child).forEach(key => {
|
|
|
|
|
console.log(` - Key: ${key}, Value type: ${typeof this.child[key]}`);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
registerChild(child, positionVsParent) {
|
|
|
|
|
this.addEventListenerForChild(child, softwareType); //subscribe to measurement changes from this specific child
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//MAAK SPECIFIEKE CHILD EVENT LISTENER - KAN EVT IN REGISTERCHILD FUNCTIE WORDEN BIJGEVOEGD
|
|
|
|
|
addEventListenerForChild(child, softwareType) {
|
|
|
|
|
if (child && child.emitter) {
|
|
|
|
|
child.measurements.emitter.on('change', (data) => {
|
|
|
|
|
const childName = child.config?.general?.name || `${softwareType}_child`;
|
|
|
|
|
console.log('🔔 EVENT RECEIVED!', childName, data.type);
|
|
|
|
|
console.log('🔔 Data', data);
|
|
|
|
|
this.logger.info(`Received change event from child ${childName}:`, data);
|
|
|
|
|
this.handleChildChange(child, data, softwareType);
|
|
|
|
|
});
|
|
|
|
|
console.log(`✅ Event listener added for ${softwareType} child`);
|
|
|
|
|
} else {
|
|
|
|
|
console.log(`❌ Child or emitter missing:`, {
|
|
|
|
|
child: !!child,
|
|
|
|
|
emitter: !!child?.measurements.emitter
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handleChildChange(child, data, softwareType) {
|
|
|
|
|
switch(data.type) {
|
|
|
|
|
case 'flow':
|
|
|
|
|
console.log('softwareType', softwareType)
|
|
|
|
|
if (softwareType === 'machinegroup') {
|
|
|
|
|
this.logger.info("Total flow change from machineGroupControl detected by valveGroupControl");
|
|
|
|
|
//Opslaan nieuwe total flow
|
|
|
|
|
let value = data.value * 3.6 ; // convert l/s to m3/h
|
|
|
|
|
this.measurements.type("flow").variant("predicted").position("atEquipment").value(value);
|
|
|
|
|
// als totalflow van mg veranderd, bereken dan de nieuwe valve flows
|
|
|
|
|
this.calcValveFlows();
|
|
|
|
|
};
|
|
|
|
|
break;
|
|
|
|
|
case 'pressure':
|
|
|
|
|
if (data.position === 'delta') {
|
|
|
|
|
this.logger.info("DeltaP change from child valve detected by valveGroupControl");
|
|
|
|
|
// als deltaP van een van de childs valves verandert, bereken dan de nieuwe maxDeltaP
|
|
|
|
|
this.calcMaxDeltaP();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Insert actions this.calcMaxDeltaP();
|
|
|
|
|
this.logger.info("Kijk ik kom bij handle child change aan!")
|
|
|
|
|
break;
|
|
|
|
|
case 'position':
|
|
|
|
|
// Handle position changes
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
this.logger.debug(`Unhandled change type: ${data.type}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isValidSourceForMode(source, mode) {
|
|
|
|
|
@@ -99,7 +159,7 @@ class ValveGroupControl {
|
|
|
|
|
await this.executeSequence(parameter);
|
|
|
|
|
break;
|
|
|
|
|
case "totalFlowChange":
|
|
|
|
|
await this.updateFlow(parameter);
|
|
|
|
|
await this.updateFlow("predicted", parameter, "atEquipment");
|
|
|
|
|
break;
|
|
|
|
|
case "emergencyStop":
|
|
|
|
|
this.logger.warn(`Emergency stop activated by '${source}'.`);
|
|
|
|
|
@@ -175,12 +235,13 @@ updateFlow(variant,value,position) {
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
this.logger.warn(`Unrecognized variant '${variant}' for flow update.`);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
updateMeasurement(variant, subType, value, position) {
|
|
|
|
|
this.logger.debug(`---------------------- updating ${subType} ------------------ `);
|
|
|
|
|
this.logger.debug(`---------------------- updating ${subType} ------------------ NOT YET FURTHER DEFINED ------------------`);
|
|
|
|
|
switch (subType) {
|
|
|
|
|
case "pressure":
|
|
|
|
|
// Update pressure measurement
|
|
|
|
|
@@ -199,52 +260,129 @@ updateFlow(variant,value,position) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calcValveFlows() {
|
|
|
|
|
const totalFlow = this.measurements.type("flow").variant("measured").position("atEquipment").getCurrentValue(); // get the total flow from the measurement container
|
|
|
|
|
const totalFlow = this.measurements.type("flow").variant("predicted").position("atEquipment").getCurrentValue(); // get the total flow from the measurement container
|
|
|
|
|
let totalKv = 0;
|
|
|
|
|
|
|
|
|
|
this.logger.debug(`Calculating valve flows... ${totalFlow}`); //Checkpoint
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (const key in this.valves){ //bereken sum kv values om verdeling total flow te maken
|
|
|
|
|
this.logger.info('kv: ' + this.valves[key].kv); //CHECKPOINT
|
|
|
|
|
if (this.valves[key].state.getCurrentPosition() != null) {
|
|
|
|
|
totalKv += this.valves[key].kv;
|
|
|
|
|
this.logger.info('Total Kv = ' + totalKv); //CHECKPOINT
|
|
|
|
|
}
|
|
|
|
|
if(totalKv === 0) {
|
|
|
|
|
this.logger.warn('Total Kv is 0, cannot calculate flow distribution.');
|
|
|
|
|
return; // Avoid division by zero
|
|
|
|
|
// for (const key in this.child){ //bereken sum kv values om verdeling total flow te maken
|
|
|
|
|
// if (this.child[key].state.getCurrentPosition() != null) {
|
|
|
|
|
// totalKv += this.child[key].kv;
|
|
|
|
|
// this.logger.info('Total Kv = ' + totalKv); //CHECKPOINT
|
|
|
|
|
// }
|
|
|
|
|
// if(totalKv === 0) {
|
|
|
|
|
// this.logger.warn('Total Kv is 0, cannot calculate flow distribution.');
|
|
|
|
|
// return; // Avoid division by zero
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Loop door de geneste this.child structuur om total kv te berekenen
|
|
|
|
|
for (const [softwareType, typeData] of Object.entries(this.child)) {
|
|
|
|
|
// Filter alleen op valves
|
|
|
|
|
if (softwareType === 'valve') {
|
|
|
|
|
// Loop door categories (valves, etc.)
|
|
|
|
|
for (const [category, childrenArray] of Object.entries(typeData)) {
|
|
|
|
|
if (Array.isArray(childrenArray)) {
|
|
|
|
|
// Loop door alle valve objecten in deze array
|
|
|
|
|
childrenArray.forEach((valve, index) => {
|
|
|
|
|
if (valve && valve.state && valve.state.getCurrentPosition() != null) {
|
|
|
|
|
totalKv += valve.kv;
|
|
|
|
|
this.logger.info(`Adding valve ${index} Kv: ${valve.kv}, Total Kv now: ${totalKv}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const key in this.valves){
|
|
|
|
|
const valve = this.valves[key];
|
|
|
|
|
this.logger.debug(`Calculating ratio for valve total: ${totalKv} valve.kv: ${valve.kv} ratio : ${valve.kv / totalKv}`); //Checkpoint
|
|
|
|
|
const ratio = valve.kv / totalKv;
|
|
|
|
|
const flow = ratio * totalFlow; // bereken flow per valve
|
|
|
|
|
if (totalKv === 0) {
|
|
|
|
|
this.logger.warn('Total Kv is 0, cannot calculate flow distribution.');
|
|
|
|
|
return; // Avoid division by zero
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//update flow per valve in de object zelf wat daar vervolgens weer de nieuwe deltaP berekent
|
|
|
|
|
valve.updateFlow("predicted", flow, "downstream");
|
|
|
|
|
this.logger.info(`--> Sending updated flow to valves --> ${flow} `); //Checkpoint
|
|
|
|
|
// for (const key in this.child){
|
|
|
|
|
// const valve = this.child[key];
|
|
|
|
|
// this.logger.debug(`Calculating ratio for valve total: ${totalKv} valve.kv: ${valve.kv} ratio : ${valve.kv / totalKv}`); //Checkpoint
|
|
|
|
|
// const ratio = valve.kv / totalKv;
|
|
|
|
|
// const flow = ratio * totalFlow; // bereken flow per valve
|
|
|
|
|
|
|
|
|
|
// //update flow per valve in de object zelf wat daar vervolgens weer de nieuwe deltaP berekent
|
|
|
|
|
// valve.updateFlow("predicted", flow, "downstream");
|
|
|
|
|
// this.logger.info(`--> Sending updated flow to valves --> ${flow} `); //Checkpoint
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// Flow verdelen over alle valves
|
|
|
|
|
for (const [softwareType, typeData] of Object.entries(this.child)) {
|
|
|
|
|
if (softwareType === 'valve') {
|
|
|
|
|
for (const [category, childrenArray] of Object.entries(typeData)) {
|
|
|
|
|
if (Array.isArray(childrenArray)) {
|
|
|
|
|
childrenArray.forEach((valve, index) => {
|
|
|
|
|
if (valve && valve.state && valve.state.getCurrentPosition() != null) {
|
|
|
|
|
const ratio = valve.kv / totalKv;
|
|
|
|
|
const flow = ratio * totalFlow;
|
|
|
|
|
|
|
|
|
|
valve.updateFlow("predicted", flow, "downstream");
|
|
|
|
|
this.logger.info(`--> Sending updated flow to valve --> Flow: ${flow}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
calcMaxDeltaP() { // bereken de max deltaP van alle child valves
|
|
|
|
|
let maxDeltaP = 0; //max deltaP is 0 als er geen child valves zijn
|
|
|
|
|
this.logger.info('Calculating new max deltaP...');
|
|
|
|
|
for (const key in this.valves) {
|
|
|
|
|
const valve = this.valves[key]; //haal de child valve object op
|
|
|
|
|
const deltaP = valve.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue(); //get delta P
|
|
|
|
|
this.logger.info(`Delta P for valve ${key}: ${deltaP}`);
|
|
|
|
|
if (deltaP > maxDeltaP) { //als de deltaP van de child valve groter is dan de huidige maxDeltaP, dan update deze
|
|
|
|
|
maxDeltaP = deltaP;
|
|
|
|
|
}
|
|
|
|
|
let maxDeltaP = 0;
|
|
|
|
|
for (const [softwareType, typeData] of Object.entries(this.child)) {
|
|
|
|
|
// Filter alleen op valves
|
|
|
|
|
if (softwareType === 'valve') {
|
|
|
|
|
// Loop door categories (valves, etc.)
|
|
|
|
|
for (const [category, childrenArray] of Object.entries(typeData)) {
|
|
|
|
|
if (Array.isArray(childrenArray)) {
|
|
|
|
|
// Loop door alle valve objecten in deze array
|
|
|
|
|
childrenArray.forEach((valve, index) => {
|
|
|
|
|
try{
|
|
|
|
|
const deltaP = valve.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue();
|
|
|
|
|
|
|
|
|
|
if (deltaP > maxDeltaP) {
|
|
|
|
|
maxDeltaP = deltaP;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.logger.error(`Error retrieving deltaP for valve at index ${index}: ${error}`);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
this.logger.info('Max Delta P updated to: ' + maxDeltaP);
|
|
|
|
|
|
|
|
|
|
this.maxDeltaP = maxDeltaP; //update de max deltaP in de measurement container van de valveGroupControl class
|
|
|
|
|
this.measurements.type("maxDeltaP").variant("predicted").position("delta").value(maxDeltaP); //update de max deltaP in de measurement container van de valveGroupControl class
|
|
|
|
|
this.maxDeltaP = maxDeltaP; //update de max deltaP
|
|
|
|
|
this.logger.info('Max Delta P updated to: ' + maxDeltaP);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// let maxDeltaP = 0; //max deltaP is 0 als er geen child valves zijn
|
|
|
|
|
// this.logger.info('Calculating new max deltaP...');
|
|
|
|
|
// for (const key in this.child) {
|
|
|
|
|
// const valve = this.child[key]; //haal de child valve object op
|
|
|
|
|
// const deltaP = valve.measurements.type("pressure").variant("predicted").position("delta").getCurrentValue(); //get delta P
|
|
|
|
|
// this.logger.info(`Delta P for valve ${key}: ${deltaP}`);
|
|
|
|
|
// if (deltaP > maxDeltaP) { //als de deltaP van de child valve groter is dan de huidige maxDeltaP, dan update deze
|
|
|
|
|
// maxDeltaP = deltaP;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// this.logger.info('Max Delta P updated to: ' + maxDeltaP);
|
|
|
|
|
|
|
|
|
|
// this.measurements.type("maxDeltaP").variant("predicted").position("delta").value(maxDeltaP); //update de max deltaP in de measurement container van de valveGroupControl class
|
|
|
|
|
// this.maxDeltaP = maxDeltaP; //update de max deltaP
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getOutput() {
|
|
|
|
|
@@ -267,13 +405,12 @@ updateFlow(variant,value,position) {
|
|
|
|
|
|
|
|
|
|
//fill in the rest of the output object
|
|
|
|
|
output["mode"] = this.currentMode;
|
|
|
|
|
output["maxDeltaP"] = this.maxDeltaP;
|
|
|
|
|
//output["maxDeltaP"] = this.maxDeltaP;
|
|
|
|
|
|
|
|
|
|
//this.logger.debug(`Output: ${JSON.stringify(output)}`);
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = ValveGroupControl;
|
|
|
|
|
@@ -318,23 +455,23 @@ const stateConfig = {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const valve1 = new valve(valveConfig, stateConfig);
|
|
|
|
|
//const valve1 = new valve(valveConfig, stateConfig);
|
|
|
|
|
//const valve2 = new valve(valveConfig, stateConfig);
|
|
|
|
|
//const valve3 = new valve(valveConfig, stateConfig);
|
|
|
|
|
|
|
|
|
|
valve1.kv = 10; // Set Kv value for valve1
|
|
|
|
|
//valve1.kv = 10; // Set Kv value for valve1
|
|
|
|
|
//valve2.kv = 20; // Set Kv value for valve2
|
|
|
|
|
//valve3.kv = 30; // Set Kv value for valve3
|
|
|
|
|
|
|
|
|
|
valve1.updateMeasurement("measured", "pressure" , 500, "downstream");
|
|
|
|
|
//valve1.updateMeasurement("measured", "pressure" , 500, "downstream");
|
|
|
|
|
//valve2.updateMeasurement("measured" , "pressure" , 500, "downstream");
|
|
|
|
|
//valve3.updateMeasurement("measured" , "pressure" , 500, "downstream");
|
|
|
|
|
|
|
|
|
|
const vgc = new ValveGroupControl();
|
|
|
|
|
//const vgc = new ValveGroupControl();
|
|
|
|
|
|
|
|
|
|
vgc.childRegistrationUtils.registerChild(valve1, "atEquipment");
|
|
|
|
|
//vgc.childRegistrationUtils.registerChild(valve1, "atEquipment");
|
|
|
|
|
//vgc.childRegistrationUtils.registerChild(valve2, "atEquipment");
|
|
|
|
|
//vgc.childRegistrationUtils.registerChild(valve3, "atEquipment");
|
|
|
|
|
|
|
|
|
|
vgc.updateFlow("measured", 1000, "atEquipment"); // Update total flow to 100 m3/h
|
|
|
|
|
//vgc.updateFlow("measured", 1000, "atEquipment"); // Update total flow to 100 m3/h
|
|
|
|
|
|
|
|
|
|
|