const test = require('node:test'); const assert = require('node:assert/strict'); const { MeasurementContainer, POSITIONS } = require('generalFunctions'); const GroupOperatingPoint = require('../../src/groupOps/groupOperatingPoint'); const unitPolicy = { canonical: { pressure: 'Pa', flow: 'm3/s', power: 'W', temperature: 'K' }, output: { pressure: 'Pa', flow: 'm3/s', power: 'W', temperature: 'K' }, }; const silentLogger = { debug() {}, info() {}, warn() {}, error() {} }; function makeContainer() { return new MeasurementContainer({ defaultUnits: unitPolicy.output, preferredUnits: unitPolicy.output, canonicalUnits: unitPolicy.canonical, storeCanonical: true, autoConvert: true, }); } function makeMachine(id, pressures = {}) { // pressures: { down?: Pa, up?: Pa } — written into a real container const m = { config: { general: { id } }, measurements: makeContainer(), setGroupOperatingPointCalls: [], setGroupOperatingPoint(down, up) { this.setGroupOperatingPointCalls.push({ down, up }); }, }; const now = Date.now(); if (pressures.down != null) { m.measurements.type('pressure').variant('measured').position(POSITIONS.DOWNSTREAM).value(pressures.down, now, 'Pa'); } if (pressures.up != null) { m.measurements.type('pressure').variant('measured').position(POSITIONS.UPSTREAM).value(pressures.up, now, 'Pa'); } return m; } test('readChild returns value in requested unit when present', () => { const machines = {}; const m = makeMachine('m1', { down: 150000 }); machines[m.config.general.id] = m; const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines, unitPolicy, logger: silentLogger }); const v = gop.readChild(m, 'pressure', 'measured', POSITIONS.DOWNSTREAM, 'Pa'); assert.equal(v, 150000); }); test('readChild returns null when measurement missing', () => { const m = makeMachine('m1'); const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines: { m1: m }, unitPolicy, logger: silentLogger }); const v = gop.readChild(m, 'pressure', 'measured', POSITIONS.UPSTREAM, 'Pa'); assert.equal(v, null); }); test("writeOwn writes to the group's measurements container", () => { const ownC = makeContainer(); const gop = new GroupOperatingPoint({ measurements: ownC, machines: {}, unitPolicy, logger: silentLogger }); gop.writeOwn('flow', 'predicted', POSITIONS.AT_EQUIPMENT, 0.1, 'm3/s'); const v = ownC.type('flow').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('m3/s'); assert.equal(v, 0.1); }); test('writeOwn skips non-finite values', () => { const ownC = makeContainer(); const gop = new GroupOperatingPoint({ measurements: ownC, machines: {}, unitPolicy, logger: silentLogger }); gop.writeOwn('flow', 'predicted', POSITIONS.AT_EQUIPMENT, NaN, 'm3/s'); const v = ownC.type('flow').variant('predicted').position(POSITIONS.AT_EQUIPMENT).getCurrentValue('m3/s'); assert.equal(v, null); }); test('equalize() pushes the worst-case header onto each machine when 3 pressures differ', () => { // No group header → max child downstream, min positive child upstream. // max(120k, 140k, 100k) = 140000, min(80k, 90k, 70k) = 70000. const machines = { a: makeMachine('a', { down: 120000, up: 80000 }), b: makeMachine('b', { down: 140000, up: 90000 }), c: makeMachine('c', { down: 100000, up: 70000 }), }; const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines, unitPolicy, logger: silentLogger }); gop.equalize(); for (const id of ['a', 'b', 'c']) { const last = machines[id].setGroupOperatingPointCalls.at(-1); assert.ok(last, `machine ${id} should have been called`); assert.equal(last.down, 140000); assert.equal(last.up, 70000); } }); test('equalize() is a no-op when there is no pressure data', () => { const machines = { a: makeMachine('a'), b: makeMachine('b') }; const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines, unitPolicy, logger: silentLogger }); gop.equalize(); assert.equal(machines.a.setGroupOperatingPointCalls.length, 0); assert.equal(machines.b.setGroupOperatingPointCalls.length, 0); }); test('equalize() is a no-op when machines map is empty', () => { const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines: {}, unitPolicy, logger: silentLogger }); assert.doesNotThrow(() => gop.equalize()); }); test('equalize() falls back to direct fDimension when setGroupOperatingPoint is missing', () => { const m = { config: { general: { id: 'old' } }, measurements: makeContainer(), predictFlow: { fDimension: 0 }, predictPower: { fDimension: 0 }, predictCtrl: { fDimension: 0 }, }; m.measurements.type('pressure').variant('measured').position(POSITIONS.DOWNSTREAM).value(200000, Date.now(), 'Pa'); m.measurements.type('pressure').variant('measured').position(POSITIONS.UPSTREAM).value(100000, Date.now(), 'Pa'); const gop = new GroupOperatingPoint({ measurements: makeContainer(), machines: { old: m }, unitPolicy, logger: silentLogger }); gop.equalize(); assert.equal(m.predictFlow.fDimension, 100000); assert.equal(m.predictPower.fDimension, 100000); assert.equal(m.predictCtrl.fDimension, 100000); });