84 lines
3.3 KiB
JavaScript
84 lines
3.3 KiB
JavaScript
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const test = require('node:test');
|
|||
|
|
const assert = require('node:assert/strict');
|
|||
|
|
|
|||
|
|
const { getOutput } = require('../../src/io/output.js');
|
|||
|
|
const MachineGroup = require('../../src/specificClass.js');
|
|||
|
|
|
|||
|
|
// Real declared unit policy so the m³/s → m³/h conversion is the production one.
|
|||
|
|
const unitPolicy = MachineGroup.unitPolicy;
|
|||
|
|
|
|||
|
|
// Minimal MGC stand-in exposing exactly the surface getOutput reads. The
|
|||
|
|
// measurement loop is short-circuited with an empty type list so the test
|
|||
|
|
// isolates the demand telemetry without needing curves / CoolProp.
|
|||
|
|
function mockMgc(overrides = {}) {
|
|||
|
|
return {
|
|||
|
|
measurements: { getTypes: () => [] },
|
|||
|
|
unitPolicy,
|
|||
|
|
mode: 'optimalControl',
|
|||
|
|
scaling: 'absolute',
|
|||
|
|
absDistFromPeak: 0,
|
|||
|
|
relDistFromPeak: 0,
|
|||
|
|
dynamicTotals: { flow: { min: 0.05, max: 0.25 } }, // m³/s
|
|||
|
|
machines: {},
|
|||
|
|
operatingPoint: {},
|
|||
|
|
_lastDemand: null,
|
|||
|
|
...overrides,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
test('demandFlow + demandPct emitted once a demand is resolved', () => {
|
|||
|
|
// Demand resolved to 0.15 m³/s inside a 0.05..0.25 envelope → midpoint = 50%.
|
|||
|
|
const out = getOutput(mockMgc({ _lastDemand: { canonical: 0.15, clamped: 0.15 } }));
|
|||
|
|
|
|||
|
|
// m³/s → m³/h is ×3600. 0.15 m³/s = 540 m³/h.
|
|||
|
|
assert.equal(out.demandFlow, 540);
|
|||
|
|
assert.ok(Math.abs(out.demandPct - 50) < 1e-9, `expected ~50%, got ${out.demandPct}`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('demandPct reflects the clamped setpoint, not the raw request', () => {
|
|||
|
|
// Operator asked for 0.40 m³/s but the envelope caps at 0.25 → 100%.
|
|||
|
|
const out = getOutput(mockMgc({ _lastDemand: { canonical: 0.40, clamped: 0.25 } }));
|
|||
|
|
assert.equal(out.demandFlow, 900); // 0.25 m³/s = 900 m³/h
|
|||
|
|
assert.equal(out.demandPct, 100);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('demandPct is 0 (never NaN) when the capacity span is zero', () => {
|
|||
|
|
const out = getOutput(mockMgc({
|
|||
|
|
dynamicTotals: { flow: { min: 0.1, max: 0.1 } },
|
|||
|
|
_lastDemand: { canonical: 0.1, clamped: 0.1 },
|
|||
|
|
}));
|
|||
|
|
assert.equal(out.demandPct, 0);
|
|||
|
|
assert.ok(Number.isFinite(out.demandFlow));
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('turnOff demand (0) emits a zero setpoint, not absent', () => {
|
|||
|
|
const out = getOutput(mockMgc({ _lastDemand: { canonical: 0, clamped: 0 } }));
|
|||
|
|
assert.equal(out.demandFlow, 0);
|
|||
|
|
assert.equal(out.demandPct, 0);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('demand telemetry is absent before the first demand (degraded state)', () => {
|
|||
|
|
const out = getOutput(mockMgc({ _lastDemand: null }));
|
|||
|
|
assert.ok(!('demandFlow' in out), 'demandFlow must be absent pre-first-demand');
|
|||
|
|
assert.ok(!('demandPct' in out), 'demandPct must be absent pre-first-demand');
|
|||
|
|
// The always-on capacity fields are still present, converted to the output
|
|||
|
|
// flow unit (m³/h): 0.05 m³/s → 180, 0.25 m³/s → 900.
|
|||
|
|
assert.equal(out.flowCapacityMin, 180);
|
|||
|
|
assert.equal(out.flowCapacityMax, 900);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('flow capacity is emitted in the output unit (m³/h), matching the flow series', () => {
|
|||
|
|
const out = getOutput(mockMgc({ dynamicTotals: { flow: { min: 0.1, max: 0.3 } } }));
|
|||
|
|
assert.equal(out.flowCapacityMin, 360); // 0.1 m³/s × 3600
|
|||
|
|
assert.equal(out.flowCapacityMax, 1080); // 0.3 m³/s × 3600
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('flow capacity falls back to 0 when the envelope is unresolved (Infinity)', () => {
|
|||
|
|
// Pre-first-equalize: dynamicTotals seeds min=Infinity, max=0.
|
|||
|
|
const out = getOutput(mockMgc({ dynamicTotals: { flow: { min: Infinity, max: 0 } } }));
|
|||
|
|
assert.equal(out.flowCapacityMin, 0);
|
|||
|
|
assert.equal(out.flowCapacityMax, 0);
|
|||
|
|
});
|