diff --git a/src/specificClass.js b/src/specificClass.js index 7dcc278..2979856 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -74,6 +74,16 @@ class MachineGroup { // Combination curve data this.dynamicTotals = { flow: { min: Infinity, max: 0 }, power: { min: Infinity, max: 0 } , NCog : 0}; this.absoluteTotals = { flow: { min: Infinity, max: 0 }, power: { min: Infinity, max: 0 }}; + + // Dispatch serialization. PS ticks demand into MGC at 1 Hz, but + // a real pump ramp takes several seconds — without this gate + // every PS tick aborts the in-flight dispatch and starts a new + // one, so pumps never reach their setpoint. Mirrors + // rotatingMachine state.delayedMove: while a dispatch is in + // flight the latest demand is parked here for pickup when the + // current dispatch settles. Latest-wins. + this._dispatchInFlight = false; + this._delayedCall = null; //this always last in the constructor this.childRegistrationUtils = new childRegistrationUtils(this); @@ -1274,6 +1284,37 @@ class MachineGroup { async handleInput(source, demand, powerCap = Infinity, priorityList = null) { + // Serialize dispatches: if a previous handleInput is still + // awaiting pump movements, park the latest demand and return. + // The in-flight dispatch's `finally` block will pick it up. + // See rotatingMachine state.delayedMove for the analogous + // pattern at the pump level. + if (this._dispatchInFlight) { + this._delayedCall = { source, demand, powerCap, priorityList }; + this.logger.debug(`Dispatch in flight; deferring demand=${demand} until current pump moves complete.`); + return; + } + + this._dispatchInFlight = true; + try { + return await this._runDispatch(source, demand, powerCap, priorityList); + } finally { + this._dispatchInFlight = false; + // Pick up the latest deferred call (intermediate values were + // stomped while we were busy — only the last one matters). + if (this._delayedCall) { + const next = this._delayedCall; + this._delayedCall = null; + this.logger.debug(`Dispatch finished; picking up deferred demand=${next.demand}.`); + // Recursive call re-enters the gate; safe because + // _dispatchInFlight has been reset to false above. + await this.handleInput(next.source, next.demand, next.powerCap, next.priorityList); + } + } + } + + async _runDispatch(source, demand, powerCap = Infinity, priorityList = null) { + const demandQ = parseFloat(demand); if(!Number.isFinite(demandQ)){