feat: level-based control now reaches machine groups + manual Qd forwarding
Two additions to pumpingStation: 1. _controlLevelBased now calls _applyMachineGroupLevelControl in addition to _applyMachineLevelControl when the basin is filling above startLevel. Previously only direct-child machines received the level-based percent-control signal; in a hierarchical topology (PS → MGC → pumps) the machines sit under MGC and PS.machines is empty, so the level control never reached them. 2. New 'Qd' input topic + forwardDemandToChildren() method. When PS is in 'manual' mode (matching the pattern from rotatingMachine's virtualControl), operator demand from a dashboard slider is forwarded to all child machine groups and direct machines. When PS is in any other mode (levelbased, flowbased, etc.), the Qd msg is silently dropped with a debug log so the automatic control isn't overridden. No breaking changes — existing flows that don't send 'Qd' are unaffected, and _controlLevelBased's additional call to machineGroupLevelControl is a no-op when no machine groups are registered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -221,6 +221,27 @@ class nodeClass {
|
|||||||
this.source.setManualInflow(val, ts, unit);
|
this.source.setManualInflow(val, ts, unit);
|
||||||
break;
|
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();
|
done();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -254,6 +254,11 @@ class PumpingStation {
|
|||||||
const percControl = this._scaleLevelToFlowPercent(level);
|
const percControl = this._scaleLevelToFlowPercent(level);
|
||||||
this.logger.debug(`Controllevel based => Level ${level} control applying to pump : ${percControl}`);
|
this.logger.debug(`Controllevel based => Level ${level} control applying to pump : ${percControl}`);
|
||||||
await this._applyMachineLevelControl(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') {
|
if (level < stopLevel && direction === 'draining') {
|
||||||
@@ -272,6 +277,38 @@ class PumpingStation {
|
|||||||
// placeholder for flow-based logic
|
// 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) {
|
async _applyMachineGroupLevelControl(percentControl) {
|
||||||
if (!this.machineGroups || Object.keys(this.machineGroups).length === 0) return;
|
if (!this.machineGroups || Object.keys(this.machineGroups).length === 0) return;
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|||||||
Reference in New Issue
Block a user