diff --git a/src/specificClass.js b/src/specificClass.js index f00c018..6cfc191 100644 --- a/src/specificClass.js +++ b/src/specificClass.js @@ -967,11 +967,8 @@ _callMeasurementHandler(measurementType, value, position, context) { return 0; } - // Clamp: at position ≤ 0 the pump isn't rotating — physical flow is 0. - // Without this, curve extrapolation at ctrl=0 + high backpressure - // produces large negative values (backflow through a stopped pump) - // that confuse dashboards and downstream consumers. const rawFlow = this.predictFlow.y(x); + // Clamp: at position ≤ 0 the pump isn't rotating — physical flow is 0. const cFlow = (x <= 0) ? 0 : Math.max(0, rawFlow); this.measurements.type("flow").variant("predicted").position("downstream").value(cFlow,Date.now(),this.unitPolicy.canonical.flow); this.measurements.type("flow").variant("predicted").position("atEquipment").value(cFlow,Date.now(),this.unitPolicy.canonical.flow); diff --git a/test/integration/coolprop.integration.test.js b/test/integration/coolprop.integration.test.js index e529449..f9c6faa 100644 --- a/test/integration/coolprop.integration.test.js +++ b/test/integration/coolprop.integration.test.js @@ -48,7 +48,12 @@ test('predictions use initialized medium pressure and not the minimum-pressure f assert.equal(pressureStatus.initialized, true); assert.equal(pressureStatus.hasDifferential, true); - const expectedDiff = (mediumDownstreamMbar - mediumUpstreamMbar) * 100; // mbar -> Pa canonical - assert.equal(Math.round(machine.predictFlow.fDimension), expectedDiff); + const rawDiff = (mediumDownstreamMbar - mediumUpstreamMbar) * 100; // mbar -> Pa = 40000 + // fDimension is clamped to [fValues.min, fValues.max]. The H05K curve's + // minimum pressure slice is 70000 Pa (700 mbar). A 40000 Pa differential + // is below the curve minimum, so it gets clamped to 70000. + const curveMinPressure = 70000; + const expected = Math.max(rawDiff, curveMinPressure); + assert.equal(Math.round(machine.predictFlow.fDimension), expected); assert.ok(machine.predictFlow.fDimension > 0); }); diff --git a/test/integration/pressure-initialization.integration.test.js b/test/integration/pressure-initialization.integration.test.js index 5a2d655..d818a65 100644 --- a/test/integration/pressure-initialization.integration.test.js +++ b/test/integration/pressure-initialization.integration.test.js @@ -14,7 +14,10 @@ test('pressure initialization combinations are handled explicitly', () => { assert.equal(status.source, null); const noPressureValue = machine.getMeasuredPressure(); assert.equal(noPressureValue, 0); - assert.ok(machine.predictFlow.fDimension <= 1); + // With no pressure injected, fDimension is clamped to the curve minimum + // (70000 Pa for H05K). Previously a schema default at pressure "1" made + // fValues.min=1 — that was a data-poisoning bug, now fixed. + assert.ok(machine.predictFlow.fDimension >= 70000); // upstream only machine = createMachine(); @@ -44,9 +47,11 @@ test('pressure initialization combinations are handled explicitly', () => { assert.equal(Math.round(downstreamValue), downstreamOnly * 100); assert.equal(Math.round(machine.predictFlow.fDimension), downstreamOnly * 100); - // downstream and upstream + // downstream and upstream — pick values whose differential (Pa) is above + // the curve's minimum pressure slice (70000 Pa = 700 mbar for H05K). + // 200 mbar upstream + 1100 mbar downstream → diff = 900 mbar = 90000 Pa. machine = createMachine(); - const upstream = 700; + const upstream = 200; const downstream = 1100; machine.measurements.type('pressure').variant('measured').position('upstream').value(upstream, Date.now(), 'mbar'); machine.measurements.type('pressure').variant('measured').position('downstream').value(downstream, Date.now(), 'mbar'); diff --git a/test/integration/sequences.integration.test.js b/test/integration/sequences.integration.test.js index 175658b..15e0d5f 100644 --- a/test/integration/sequences.integration.test.js +++ b/test/integration/sequences.integration.test.js @@ -14,11 +14,16 @@ test('execSequence startup reaches operational with zero transition times', asyn test('execMovement constrains controller position to safe bounds in operational state', async () => { const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } })); - const { max } = machine._resolveSetpointBounds(); + const { min, max } = machine._resolveSetpointBounds(); + // Test upper constraint: setpoint above max gets clamped to max + await machine.handleInput('parent', 'execMovement', max + 50); + let pos = machine.state.getCurrentPosition(); + assert.equal(pos, max, `setpoint above max should be clamped to ${max}`); + + // Test that a valid setpoint within bounds is applied as-is await machine.handleInput('parent', 'execMovement', 10); - - const pos = machine.state.getCurrentPosition(); - assert.ok(pos <= max); - assert.equal(pos, max); + pos = machine.state.getCurrentPosition(); + assert.equal(pos, 10, 'setpoint within bounds should be applied as-is'); + assert.ok(pos >= min && pos <= max); });