'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const Machine = require('../../src/specificClass'); const { makeMachineConfig, makeStateConfig } = require('../helpers/factories'); /** * Reproduction harness for the dashboard report: after the pressure-router * fix, the user sees absDistFromPeak=0, NCog=0, efficiency=0, predicted * atEquipment flow blank, even after the machine is running and pressure * sliders are being moved. * * This test mirrors the actual dashboard interaction: * 1. start the machine (reach operational at ctrl=0) * 2. set virtual pressure (dashboard slider equivalent) * 3. move setpoint to non-zero ctrl * 4. read the host fields + measurement values * * Every value should be non-zero after step 3. If anything is 0 here, the * failure is reproducible at the unit level and we can patch it directly. */ async function makeRunningMachine() { const cfg = makeMachineConfig({ general: { id: 'rm-bep', name: 'BEP-test', unit: 'm3/h', logging: { enabled: false, logLevel: 'error' } }, asset: { supplier: 'hidrostal', category: 'pump', type: 'Centrifugal', model: 'hidrostal-H05K-S03R', unit: 'm3/h', curveUnits: { pressure: 'mbar', flow: 'm3/h', power: 'kW', control: '%' }, }, }); const m = new Machine(cfg, makeStateConfig()); await m.handleInput('parent', 'execSequence', 'startup'); assert.equal(m.state.getCurrentState(), 'operational'); return m; } test('after startup + pressure + ctrl move: NCog / efficiency / absDistFromPeak / flow-at-equipment are all non-zero', async () => { const m = await makeRunningMachine(); // Dashboard slider equivalent — fire as virtual children (this is what // simulateMeasurement does): m.updateSimulatedMeasurement('pressure', 'upstream', 200, { unit: 'mbar' }); m.updateSimulatedMeasurement('pressure', 'downstream', 1100, { unit: 'mbar' }); // Move to a non-zero ctrl position. await m.handleInput('parent', 'execMovement', 50); // Read every metric the user reports as 0. const flowDn = m.measurements.type('flow').variant('predicted').position('downstream').getCurrentValue('m3/h'); const flowAtEq = m.measurements.type('flow').variant('predicted').position('atEquipment').getCurrentValue('m3/h'); const powerAtEq = m.measurements.type('power').variant('predicted').position('atEquipment').getCurrentValue('kW'); const efficiency = m.measurements.type('efficiency').variant('predicted').position('atEquipment').getCurrentValue(); console.log(JSON.stringify({ state: m.state.getCurrentState(), ctrl: m.state.getCurrentPosition(), flowDn, flowAtEq, powerAtEq, efficiency, NCog: m.NCog, cog: m.cog, cogIndex: m.cogIndex, absDistFromPeak: m.absDistFromPeak, relDistFromPeak: m.relDistFromPeak, minEfficiency: m.minEfficiency, }, null, 2)); assert.ok(Number.isFinite(flowDn) && flowDn > 0, `flow downstream should be > 0, got ${flowDn}`); assert.ok(Number.isFinite(flowAtEq) && flowAtEq > 0, `flow at-equipment should be > 0, got ${flowAtEq}`); assert.ok(Number.isFinite(powerAtEq) && powerAtEq > 0, `power at-equipment should be > 0, got ${powerAtEq}`); // Hydraulic efficiency η = (Q·ΔP)/P is a dimensionless 0..1 ratio. For // a reasonable pump operating point it should be at least a few percent. assert.ok(Number.isFinite(efficiency) && efficiency > 0.01, `efficiency should be a meaningful 0..1 ratio (>1%), got ${efficiency}`); assert.ok(efficiency <= 1.0, `efficiency must be <= 1 (dimensionless ratio), got ${efficiency}`); // Peak efficiency (cog) likewise should be a meaningful ratio. assert.ok(Number.isFinite(m.cog) && m.cog > 0.01 && m.cog <= 1.0, `cog (peak efficiency) should be a meaningful 0..1 ratio, got ${m.cog}`); // NCog is the normalized flow at peak — depending on the curve, BEP can // land at peakIndex=0 (yielding NCog=0). Just require finiteness here. assert.ok(Number.isFinite(m.NCog) && m.NCog >= 0 && m.NCog <= 1, `NCog should be finite 0..1, got ${m.NCog}`); // Distance-from-peak is what the user actually reads. It should be finite // and at non-BEP positions it should be > 0. assert.ok(Number.isFinite(m.absDistFromPeak) && m.absDistFromPeak >= 0, `absDistFromPeak should be finite >= 0, got ${m.absDistFromPeak}`); assert.ok(Number.isFinite(m.relDistFromPeak) && m.relDistFromPeak >= 0 && m.relDistFromPeak <= 1, `relDistFromPeak should be finite 0..1, got ${m.relDistFromPeak}`); // At ctrl=50 the current efficiency must differ from peak (we're off BEP), // so absDistFromPeak should be non-zero. assert.ok(m.absDistFromPeak > 0, `absDistFromPeak must be > 0 when off BEP, got ${m.absDistFromPeak}`); });