diff --git a/src/nodeClass.js b/src/nodeClass.js index 6e02b28..072a596 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -221,6 +221,27 @@ class nodeClass { this.source.setManualInflow(val, ts, unit); break; } + case 'Qd': { + // Manual demand: operator sets the target output via a + // dashboard slider. Only accepted when PS is in 'manual' + // mode — mirrors how rotatingMachine gates commands by + // mode (virtualControl vs auto). + const demand = Number(msg.payload); + if (!Number.isFinite(demand)) { + this.source.logger.warn(`Invalid Qd value: ${msg.payload}`); + break; + } + if (this.source.mode === 'manual') { + this.source.forwardDemandToChildren(demand).catch((err) => + this.source.logger.error(`Failed to forward demand: ${err.message}`) + ); + } else { + this.source.logger.debug( + `Qd ignored in ${this.source.mode} mode. Switch to manual to use the demand slider.` + ); + } + break; + } } done(); }); diff --git a/src/specificClass.js b/src/specificClass.js index f5d6076..ca96884 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -254,6 +254,11 @@ class PumpingStation { const percControl = this._scaleLevelToFlowPercent(level); this.logger.debug(`Controllevel based => Level ${level} control applying to pump : ${percControl}`); await this._applyMachineLevelControl(percControl); + // Also forward to machine groups (e.g. MGC) — the level-based + // control originally only addressed direct-child machines, but in + // a hierarchical topology (PS → MGC → pumps) the machines sit + // under MGC and PS.machines is empty. + await this._applyMachineGroupLevelControl(percControl); } if (level < stopLevel && direction === 'draining') { @@ -272,6 +277,38 @@ class PumpingStation { // placeholder for flow-based logic } + /** + * Forward a manual demand value to all child machine groups + direct + * machines. Called from the 'Qd' topic handler when PS is in manual + * mode — mirrors how rotatingMachine gates commands by mode. + * @param {number} demand - the operator-set demand (interpretation + * depends on MGC scaling: 'absolute' = m³/h, 'normalized' = 0-100%) + */ + async forwardDemandToChildren(demand) { + this.logger.info(`Manual demand forwarded: ${demand}`); + // Forward to machine groups (MGC) + if (this.machineGroups && Object.keys(this.machineGroups).length > 0) { + await Promise.all( + Object.values(this.machineGroups).map((group) => + group.handleInput('parent', demand).catch((err) => { + this.logger.error(`Failed to forward demand to group: ${err.message}`); + }) + ) + ); + } + // Forward to direct machines (if any) + if (this.machines && Object.keys(this.machines).length > 0) { + const perMachine = demand / Object.keys(this.machines).length; + for (const machine of Object.values(this.machines)) { + try { + await machine.handleInput('parent', 'execMovement', perMachine); + } catch (err) { + this.logger.error(`Failed to forward demand to machine: ${err.message}`); + } + } + } + } + async _applyMachineGroupLevelControl(percentControl) { if (!this.machineGroups || Object.keys(this.machineGroups).length === 0) return; await Promise.all(