Files
machineGroupControl/test/basic/totalsCalculator.basic.test.js

129 lines
5.1 KiB
JavaScript
Raw Normal View History

const test = require('node:test');
const assert = require('node:assert/strict');
const TotalsCalculator = require('../../src/totals/totalsCalculator');
const unitPolicy = {
canonical: { pressure: 'Pa', flow: 'm3/s', power: 'W', temperature: 'K' },
output: { pressure: 'Pa', flow: 'm3/s', power: 'W', temperature: 'K' },
};
const silent = { debug() {}, info() {}, warn() {}, error() {} };
function predictView(min, max) {
return { currentF: (min + max) / 2, currentFxyYMin: min, currentFxyYMax: max };
}
function makeMachine(id, opts = {}) {
const {
flowMin = 0.0, flowMax = 1.0,
powerMin = 100, powerMax = 1000,
state = 'operational',
hasCurve = true,
NCog = 0.5,
// Input-curve envelope (for calcAbsoluteTotals): { [pressureKey]: { y: [...] } }
inputCurve = null,
actFlow = 0,
actPower = 0,
} = opts;
const fakeInput = inputCurve || {
'50000': { y: [flowMin, (flowMin + flowMax) / 2, flowMax] },
};
const fakePower = inputCurve
? Object.fromEntries(Object.keys(inputCurve).map(k => [k, { y: [powerMin, (powerMin + powerMax) / 2, powerMax] }]))
: { '50000': { y: [powerMin, (powerMin + powerMax) / 2, powerMax] } };
return {
config: { general: { id } },
hasCurve,
state: { getCurrentState: () => state },
NCog,
predictFlow: { inputCurve: fakeInput, ...predictView(flowMin, flowMax) },
predictPower: { inputCurve: fakePower, ...predictView(powerMin, powerMax) },
_actFlow: actFlow,
_actPower: actPower,
};
}
function fakeOperatingPoint(/* machines */) {
return {
readChild(machine, type, _variant, _position /*, _unit */) {
if (type === 'flow') return machine._actFlow;
if (type === 'power') return machine._actPower;
return null;
},
};
}
test('calcAbsoluteTotals returns zeros when no machines', () => {
const tc = new TotalsCalculator({ machines: {}, unitPolicy, logger: silent });
const t = tc.calcAbsoluteTotals();
assert.deepEqual(t, { flow: { min: 0, max: 0 }, power: { min: 0, max: 0 } });
});
test('calcAbsoluteTotals scans curve envelope (sum of maxes, min of mins)', () => {
const machines = {
a: makeMachine('a', { flowMin: 0.1, flowMax: 0.5, powerMin: 100, powerMax: 500 }),
b: makeMachine('b', { flowMin: 0.2, flowMax: 0.8, powerMin: 200, powerMax: 700 }),
};
const tc = new TotalsCalculator({ machines, unitPolicy, logger: silent });
const t = tc.calcAbsoluteTotals();
assert.equal(t.flow.min, 0.1);
assert.equal(t.power.min, 100);
// max is summed across all machines
assert.equal(t.flow.max, 0.5 + 0.8);
assert.equal(t.power.max, 500 + 700);
});
test('calcDynamicTotals sums across machines and skips machines with no valid curve', () => {
const machines = {
a: makeMachine('a', { flowMin: 0.1, flowMax: 0.5, powerMin: 100, powerMax: 500, actFlow: 0.3, actPower: 300 }),
b: makeMachine('b', { flowMin: 0.2, flowMax: 0.7, powerMin: 200, powerMax: 600, actFlow: 0.4, actPower: 400 }),
skip: makeMachine('skip', { hasCurve: false }),
};
const tc = new TotalsCalculator({
machines, unitPolicy, logger: silent,
operatingPoint: fakeOperatingPoint(machines),
});
const t = tc.calcDynamicTotals();
assert.equal(t.flow.min, 0.1);
assert.equal(t.flow.max, 0.5 + 0.7);
assert.equal(t.flow.act, 0.3 + 0.4);
assert.equal(t.power.min, 100);
assert.equal(t.power.max, 500 + 600);
assert.equal(t.power.act, 300 + 400);
assert.equal(t.NCog, machines.a.NCog + machines.b.NCog);
});
test('activeTotals skips machines whose state is off or maintenance', () => {
const machines = {
a: makeMachine('a', { flowMin: 0.1, flowMax: 0.5, powerMin: 100, powerMax: 500, state: 'operational' }),
b: makeMachine('b', { flowMin: 0.2, flowMax: 0.7, powerMin: 200, powerMax: 600, state: 'off' }),
c: makeMachine('c', { flowMin: 0.3, flowMax: 0.9, powerMin: 300, powerMax: 900, state: 'maintenance' }),
d: makeMachine('d', { flowMin: 0.05, flowMax: 0.4, powerMin: 50, powerMax: 400, state: 'accelerating' }),
};
const tc = new TotalsCalculator({ machines, unitPolicy, logger: silent });
const t = tc.activeTotals();
assert.equal(t.countActiveMachines, 2); // a + d
assert.equal(t.flow.min, 0.1 + 0.05);
assert.equal(t.flow.max, 0.5 + 0.4);
assert.equal(t.power.min, 100 + 50);
assert.equal(t.power.max, 500 + 400);
});
test('activeTotals honours the injected isMachineActive override', () => {
const machines = {
a: makeMachine('a', { flowMin: 0.1, flowMax: 0.5, powerMin: 100, powerMax: 500, state: 'operational' }),
b: makeMachine('b', { flowMin: 0.2, flowMax: 0.7, powerMin: 200, powerMax: 600, state: 'operational' }),
};
const tc = new TotalsCalculator({
machines, unitPolicy, logger: silent,
isMachineActive: (id) => id === 'b',
});
const t = tc.activeTotals();
assert.equal(t.countActiveMachines, 1);
assert.equal(t.flow.max, 0.7);
});