before functional changes by Codex
This commit is contained in:
@@ -33,3 +33,12 @@ test('handleInput ignores disallowed source/action combination', async () => {
|
||||
|
||||
assert.equal(before, after);
|
||||
});
|
||||
|
||||
test('warmingup is treated as active for prediction updates', () => {
|
||||
const machine = new Machine(
|
||||
makeMachineConfig(),
|
||||
makeStateConfig({ state: { current: 'warmingup' } })
|
||||
);
|
||||
|
||||
assert.equal(machine._isOperationalState(), true);
|
||||
});
|
||||
|
||||
@@ -34,6 +34,9 @@ test('input handler routes topics to source methods', () => {
|
||||
showCoG() {
|
||||
return { cog: 1 };
|
||||
},
|
||||
updateSimulatedMeasurement(type, position, value) {
|
||||
calls.push(['updateSimulatedMeasurement', type, position, value]);
|
||||
},
|
||||
updateMeasuredPressure(value, position) {
|
||||
calls.push(['updateMeasuredPressure', value, position]);
|
||||
},
|
||||
@@ -56,5 +59,83 @@ test('input handler routes topics to source methods', () => {
|
||||
assert.deepEqual(calls[0], ['setMode', 'auto']);
|
||||
assert.deepEqual(calls[1], ['handleInput', 'GUI', 'execSequence', 'startup']);
|
||||
assert.deepEqual(calls[2], ['registerChild', { id: 'child-source' }, 'downstream']);
|
||||
assert.deepEqual(calls[3], ['updateMeasuredPressure', 250, 'upstream']);
|
||||
assert.deepEqual(calls[3], ['updateSimulatedMeasurement', 'pressure', 'upstream', 250]);
|
||||
});
|
||||
|
||||
test('status shows warning when pressure inputs are not initialized', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
|
||||
inst.node = node;
|
||||
inst.source = {
|
||||
currentMode: 'virtualControl',
|
||||
state: {
|
||||
getCurrentState() {
|
||||
return 'operational';
|
||||
},
|
||||
getCurrentPosition() {
|
||||
return 50;
|
||||
},
|
||||
},
|
||||
getPressureInitializationStatus() {
|
||||
return { initialized: false, hasUpstream: false, hasDownstream: false, hasDifferential: false };
|
||||
},
|
||||
measurements: {
|
||||
type() {
|
||||
return {
|
||||
variant() {
|
||||
return {
|
||||
position() {
|
||||
return { getCurrentValue() { return 0; } };
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const status = inst._updateNodeStatus();
|
||||
const statusAgain = inst._updateNodeStatus();
|
||||
|
||||
assert.equal(status.fill, 'yellow');
|
||||
assert.equal(status.shape, 'ring');
|
||||
assert.match(status.text, /pressure not initialized/i);
|
||||
assert.equal(statusAgain.fill, 'yellow');
|
||||
assert.equal(node._warns.length, 1);
|
||||
assert.match(String(node._warns[0]), /Pressure input is not initialized/i);
|
||||
});
|
||||
|
||||
test('showWorkingCurves and CoG route reply messages to process output index', () => {
|
||||
const inst = Object.create(NodeClass.prototype);
|
||||
const node = makeNodeStub();
|
||||
inst.node = node;
|
||||
inst.RED = makeREDStub();
|
||||
inst.source = {
|
||||
childRegistrationUtils: { registerChild() {} },
|
||||
setMode() {},
|
||||
handleInput() {},
|
||||
showWorkingCurves() {
|
||||
return { curve: [1, 2, 3] };
|
||||
},
|
||||
showCoG() {
|
||||
return { cog: 0.77 };
|
||||
},
|
||||
};
|
||||
|
||||
inst._attachInputHandler();
|
||||
const onInput = node._handlers.input;
|
||||
const sent = [];
|
||||
const send = (out) => sent.push(out);
|
||||
|
||||
onInput({ topic: 'showWorkingCurves', payload: { request: true } }, send, () => {});
|
||||
onInput({ topic: 'CoG', payload: { request: true } }, send, () => {});
|
||||
|
||||
assert.equal(sent.length, 2);
|
||||
assert.equal(Array.isArray(sent[0]), true);
|
||||
assert.equal(sent[0].length, 3);
|
||||
assert.equal(sent[0][0].topic, 'showWorkingCurves');
|
||||
assert.equal(sent[0][1], null);
|
||||
assert.equal(sent[0][2], null);
|
||||
assert.equal(sent[1][0].topic, 'showCoG');
|
||||
});
|
||||
|
||||
107
test/integration/basic-flow-dashboard.integration.test.js
Normal file
107
test/integration/basic-flow-dashboard.integration.test.js
Normal file
@@ -0,0 +1,107 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
function loadBasicFlow() {
|
||||
const flowPath = path.join(__dirname, '../../examples/basic.flow.json');
|
||||
return JSON.parse(fs.readFileSync(flowPath, 'utf8'));
|
||||
}
|
||||
|
||||
function makeContextStub() {
|
||||
const store = {};
|
||||
return {
|
||||
get(key) {
|
||||
return store[key];
|
||||
},
|
||||
set(key, value) {
|
||||
store[key] = value;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('basic flow parser routes predicted_power to output index 2 with numeric payload', () => {
|
||||
const flow = loadBasicFlow();
|
||||
const parser = flow.find((n) => n.id === 'rm_parse_output');
|
||||
assert.ok(parser, 'rm_parse_output node should exist');
|
||||
assert.equal(parser.outputs, 11);
|
||||
|
||||
const func = new Function('msg', 'context', 'node', parser.func);
|
||||
const context = makeContextStub();
|
||||
const node = { send() {} };
|
||||
|
||||
const msg = {
|
||||
payload: {
|
||||
'flow.predicted.downstream.default': 220,
|
||||
'power.predicted.atequipment.default': 50,
|
||||
ctrl: 40,
|
||||
NCogPercent: 72,
|
||||
state: 'operational',
|
||||
mode: 'virtualControl',
|
||||
runtime: 10.2,
|
||||
moveTimeleft: 0,
|
||||
maintenanceTime: 150.5,
|
||||
},
|
||||
};
|
||||
|
||||
const out = func(msg, context, node);
|
||||
assert.ok(Array.isArray(out));
|
||||
assert.equal(out.length, 11);
|
||||
assert.equal(out[1].topic, 'predicted_power');
|
||||
assert.equal(typeof out[1].payload, 'number');
|
||||
assert.ok(Number.isFinite(out[1].payload));
|
||||
assert.equal(out[1].payload, 50);
|
||||
});
|
||||
|
||||
test('basic flow parser output index wiring matches chart nodes', () => {
|
||||
const flow = loadBasicFlow();
|
||||
const parser = flow.find((n) => n.id === 'rm_parse_output');
|
||||
const powerChart = flow.find((n) => n.id === 'rm_chart_power');
|
||||
assert.ok(parser, 'rm_parse_output node should exist');
|
||||
assert.ok(powerChart, 'rm_chart_power node should exist');
|
||||
|
||||
assert.equal(parser.wires[1][0], 'rm_chart_power');
|
||||
assert.equal(powerChart.type, 'ui-chart');
|
||||
assert.equal(powerChart.chartType, 'line');
|
||||
assert.equal(powerChart.xAxisType, 'time');
|
||||
});
|
||||
|
||||
test('basic flow parser routes pressure series to explicit pressure charts', () => {
|
||||
const flow = loadBasicFlow();
|
||||
const parser = flow.find((n) => n.id === 'rm_parse_output');
|
||||
const upChart = flow.find((n) => n.id === 'rm_chart_pressure_up');
|
||||
const downChart = flow.find((n) => n.id === 'rm_chart_pressure_down');
|
||||
const deltaChart = flow.find((n) => n.id === 'rm_chart_pressure_delta');
|
||||
|
||||
assert.ok(parser, 'rm_parse_output node should exist');
|
||||
assert.ok(upChart, 'rm_chart_pressure_up node should exist');
|
||||
assert.ok(downChart, 'rm_chart_pressure_down node should exist');
|
||||
assert.ok(deltaChart, 'rm_chart_pressure_delta node should exist');
|
||||
|
||||
assert.equal(parser.wires[5][0], 'rm_chart_pressure_up');
|
||||
assert.equal(parser.wires[6][0], 'rm_chart_pressure_down');
|
||||
assert.equal(parser.wires[7][0], 'rm_chart_pressure_delta');
|
||||
});
|
||||
|
||||
test('basic flow parser suppresses pressure chart messages when pressure inputs are incomplete', () => {
|
||||
const flow = loadBasicFlow();
|
||||
const parser = flow.find((n) => n.id === 'rm_parse_output');
|
||||
assert.ok(parser, 'rm_parse_output node should exist');
|
||||
|
||||
const func = new Function('msg', 'context', 'node', parser.func);
|
||||
const context = makeContextStub();
|
||||
const node = { send() {} };
|
||||
|
||||
// Only upstream present: downstream/delta chart outputs should be null
|
||||
let out = func({ payload: { 'pressure.measured.upstream.default': 950 } }, context, node);
|
||||
assert.equal(out[5]?.topic, 'pressure_upstream');
|
||||
assert.equal(out[6], null);
|
||||
assert.equal(out[7], null);
|
||||
|
||||
// Once downstream arrives, delta should be emitted as finite numeric payload
|
||||
out = func({ payload: { 'pressure.measured.downstream.default': 1200 } }, context, node);
|
||||
assert.equal(out[6]?.topic, 'pressure_downstream');
|
||||
assert.equal(out[7]?.topic, 'pressure_delta');
|
||||
assert.equal(typeof out[7].payload, 'number');
|
||||
assert.ok(Number.isFinite(out[7].payload));
|
||||
});
|
||||
@@ -20,3 +20,20 @@ test('calcEfficiency runs through coolprop path without mocks', () => {
|
||||
assert.equal(typeof eff, 'number');
|
||||
assert.ok(eff > 0);
|
||||
});
|
||||
|
||||
test('predictions use initialized medium pressure and not the minimum-pressure fallback', () => {
|
||||
const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
|
||||
|
||||
const mediumUpstreamMbar = 700;
|
||||
const mediumDownstreamMbar = 1100;
|
||||
machine.updateMeasuredPressure(mediumUpstreamMbar, 'upstream', { timestamp: Date.now(), unit: 'mbar', childName: 'test-pt-up' });
|
||||
machine.updateMeasuredPressure(mediumDownstreamMbar, 'downstream', { timestamp: Date.now(), unit: 'mbar', childName: 'test-pt-down' });
|
||||
|
||||
const pressureStatus = machine.getPressureInitializationStatus();
|
||||
assert.equal(pressureStatus.initialized, true);
|
||||
assert.equal(pressureStatus.hasDifferential, true);
|
||||
|
||||
const expectedDiff = mediumDownstreamMbar - mediumUpstreamMbar;
|
||||
assert.equal(Math.round(machine.predictFlow.fDimension), expectedDiff);
|
||||
assert.ok(machine.predictFlow.fDimension > 0);
|
||||
});
|
||||
|
||||
84
test/integration/pressure-initialization.integration.test.js
Normal file
84
test/integration/pressure-initialization.integration.test.js
Normal file
@@ -0,0 +1,84 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const Machine = require('../../src/specificClass');
|
||||
const { makeMachineConfig, makeStateConfig, makeChildMeasurement } = require('../helpers/factories');
|
||||
|
||||
test('pressure initialization combinations are handled explicitly', () => {
|
||||
const createMachine = () => new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
|
||||
|
||||
// nothing
|
||||
let machine = createMachine();
|
||||
let status = machine.getPressureInitializationStatus();
|
||||
assert.equal(status.initialized, false);
|
||||
assert.equal(status.source, null);
|
||||
const noPressureValue = machine.getMeasuredPressure();
|
||||
assert.equal(noPressureValue, 0);
|
||||
assert.ok(machine.predictFlow.fDimension <= 1);
|
||||
|
||||
// upstream only
|
||||
machine = createMachine();
|
||||
const upstreamOnly = 850;
|
||||
machine.measurements.type('pressure').variant('measured').position('upstream').value(upstreamOnly, Date.now(), 'mbar');
|
||||
status = machine.getPressureInitializationStatus();
|
||||
assert.equal(status.initialized, true);
|
||||
assert.equal(status.hasUpstream, true);
|
||||
assert.equal(status.hasDownstream, false);
|
||||
assert.equal(status.hasDifferential, false);
|
||||
assert.equal(status.source, 'upstream');
|
||||
const upstreamValue = machine.getMeasuredPressure();
|
||||
assert.equal(Math.round(upstreamValue), upstreamOnly);
|
||||
assert.equal(Math.round(machine.predictFlow.fDimension), upstreamOnly);
|
||||
|
||||
// downstream only
|
||||
machine = createMachine();
|
||||
const downstreamOnly = 1150;
|
||||
machine.measurements.type('pressure').variant('measured').position('downstream').value(downstreamOnly, Date.now(), 'mbar');
|
||||
status = machine.getPressureInitializationStatus();
|
||||
assert.equal(status.initialized, true);
|
||||
assert.equal(status.hasUpstream, false);
|
||||
assert.equal(status.hasDownstream, true);
|
||||
assert.equal(status.hasDifferential, false);
|
||||
assert.equal(status.source, 'downstream');
|
||||
const downstreamValue = machine.getMeasuredPressure();
|
||||
assert.equal(Math.round(downstreamValue), downstreamOnly);
|
||||
assert.equal(Math.round(machine.predictFlow.fDimension), downstreamOnly);
|
||||
|
||||
// downstream and upstream
|
||||
machine = createMachine();
|
||||
const upstream = 700;
|
||||
const downstream = 1100;
|
||||
machine.measurements.type('pressure').variant('measured').position('upstream').value(upstream, Date.now(), 'mbar');
|
||||
machine.measurements.type('pressure').variant('measured').position('downstream').value(downstream, Date.now(), 'mbar');
|
||||
status = machine.getPressureInitializationStatus();
|
||||
assert.equal(status.initialized, true);
|
||||
assert.equal(status.hasUpstream, true);
|
||||
assert.equal(status.hasDownstream, true);
|
||||
assert.equal(status.hasDifferential, true);
|
||||
assert.equal(status.source, 'differential');
|
||||
const differentialValue = machine.getMeasuredPressure();
|
||||
assert.equal(Math.round(differentialValue), downstream - upstream);
|
||||
assert.equal(Math.round(machine.predictFlow.fDimension), downstream - upstream);
|
||||
});
|
||||
|
||||
test('real pressure child data has priority over simulated dashboard pressure', async () => {
|
||||
const machine = new Machine(makeMachineConfig(), makeStateConfig({ state: { current: 'operational' } }));
|
||||
|
||||
machine.updateSimulatedMeasurement('pressure', 'upstream', 900, { unit: 'mbar', timestamp: Date.now() });
|
||||
machine.updateSimulatedMeasurement('pressure', 'downstream', 1200, { unit: 'mbar', timestamp: Date.now() });
|
||||
assert.equal(Math.round(machine.getMeasuredPressure()), 300);
|
||||
|
||||
const upstreamChild = makeChildMeasurement({ id: 'pt-up-real', name: 'PT Up', positionVsParent: 'upstream', type: 'pressure', unit: 'mbar' });
|
||||
const downstreamChild = makeChildMeasurement({ id: 'pt-down-real', name: 'PT Down', positionVsParent: 'downstream', type: 'pressure', unit: 'mbar' });
|
||||
|
||||
await machine.childRegistrationUtils.registerChild(upstreamChild, 'upstream');
|
||||
await machine.childRegistrationUtils.registerChild(downstreamChild, 'downstream');
|
||||
|
||||
upstreamChild.measurements.type('pressure').variant('measured').position('upstream').value(700, Date.now(), 'mbar');
|
||||
downstreamChild.measurements.type('pressure').variant('measured').position('downstream').value(1300, Date.now(), 'mbar');
|
||||
|
||||
assert.equal(Math.round(machine.getMeasuredPressure()), 600);
|
||||
const status = machine.getPressureInitializationStatus();
|
||||
assert.equal(status.source, 'differential');
|
||||
assert.equal(status.initialized, true);
|
||||
});
|
||||
Reference in New Issue
Block a user