'use strict'; const test = require('node:test'); const assert = require('node:assert/strict'); const MachineGroup = require('../../src/specificClass'); const Machine = require('../../../rotatingMachine/src/specificClass'); const baseCurve = require('../../../generalFunctions/datasets/assetData/curves/hidrostal-H05K-S03R.json'); /** * After fixing rotatingMachine + MGC to use hydraulic efficiency * (η = Q·ΔP / P_shaft) instead of raw flow/power, every BEP-related output * on MGC should be in the dimensionless 0..1 range and respond to demand * changes. This check ties the whole chain together: * - per-machine cog updates after equalize * - group efficiency measurement is hydraulic (matches scale of cogs) * - calcDistanceBEP(eff, mean(cog), min(cog)) is non-degenerate */ const stateConfig = { time: { starting: 0, warmingup: 0, stopping: 0, coolingdown: 0 }, movement: { speed: 1200, mode: 'staticspeed', maxSpeed: 1800 }, }; function machineConfig(id, label) { return { general: { logging: { enabled: false, logLevel: 'error' }, name: label, id, unit: 'm3/h' }, functionality: { softwareType: 'machine', role: 'rotationaldevicecontroller' }, asset: { model: 'hidrostal-H05K-S03R', unit: 'm3/h' }, mode: { current: 'auto', allowedActions: { auto: ['execsequence', 'execmovement', 'flowmovement', 'statuscheck'] }, allowedSources: { auto: ['parent', 'GUI'] }, }, sequences: { startup: ['starting', 'warmingup', 'operational'], shutdown: ['stopping', 'coolingdown', 'idle'], emergencystop: ['emergencystop', 'off'], }, }; } function groupConfig() { return { general: { logging: { enabled: false, logLevel: 'error' }, name: 'TestGroup' }, functionality: { softwareType: 'machinegroup', role: 'groupcontroller' }, mode: { current: 'optimalcontrol' }, }; } async function setupGroupWithTwoPumps() { const m1 = new Machine(machineConfig(1, 'pump-1'), stateConfig); const m2 = new Machine(machineConfig(2, 'pump-2'), stateConfig); m1.config.asset.machineCurve = baseCurve; m2.config.asset.machineCurve = baseCurve; await m1.handleInput('parent', 'execSequence', 'startup'); await m2.handleInput('parent', 'execSequence', 'startup'); const mgc = new MachineGroup(groupConfig(), stateConfig); // Mutate the existing machines object — replacing the reference would // strand operatingPoint/totals/efficiency on the original empty bag. mgc.machines[1] = m1; mgc.machines[2] = m2; // Set header (system) pressure differential: 800/1200 mbar => 400 mbar = 40 kPa mgc.measurements.type('pressure').variant('measured').position('upstream').value(80000, Date.now(), 'Pa'); mgc.measurements.type('pressure').variant('measured').position('downstream').value(120000, Date.now(), 'Pa'); mgc.operatingPoint.equalize(); return { mgc, m1, m2 }; } test('after equalize, each child cog is a dimensionless 0..1 hydraulic efficiency', async () => { const { m1, m2 } = await setupGroupWithTwoPumps(); // Trigger updatePosition by setting ctrl explicitly m1.updatePosition(); m2.updatePosition(); for (const m of [m1, m2]) { assert.ok(Number.isFinite(m.cog), `cog must be finite, got ${m.cog}`); assert.ok(m.cog >= 0 && m.cog <= 1.0, `cog must be a 0..1 hydraulic efficiency, got ${m.cog}`); } }); test('operatingPoint.headerDiffPa is set by equalize and matches measured differential', async () => { const { mgc, m1 } = await setupGroupWithTwoPumps(); // Equalize reads from host measurements; falls back to children when // header is missing. Either path should produce headerDiffPa > 0. // headerDiff must equal the measured differential (40 kPa) once any // pressure source is populated. assert.equal(mgc.operatingPoint.headerDiffPa, 40000, `headerDiffPa should equal downstream-upstream = 40000 Pa, got ${mgc.operatingPoint.headerDiffPa}`); // Sanity: the host's child reference is still consumable for diagnostics. void m1.measurements; });