P5 wave 1: extract rotatingMachine concerns into focused modules
src/curves/ loader + normalizer (with cross-pressure anomaly
detection) + reverseCurve helper
src/prediction/ predictors (predictFlow/Power/Ctrl) +
groupPredictors (lazy group-scope views) +
OperatingPoint (pressure-driven prediction setpoints)
src/drift/ DriftAssessor (per-metric drift) + PredictionHealth
(composes flow/power/pressure into HealthStatus +
confidence sibling — see OPEN_QUESTIONS 2026-05-10)
src/pressure/ VirtualPressureChildren (dashboard-sim) +
PressureInitialization (real-vs-virtual tracking) +
PressureRouter (dispatches by position)
src/state/ stateBindings (state.emitter listener helper) +
isOperationalState
src/measurement/ measurementHandlers (dispatcher for flow/power/temp/pressure)
src/flow/ flowController (handleInput body — execSequence,
execMovement, flowMovement, emergencystop)
src/display/ workingCurves (showWorkingCurves + showCoG admin)
src/commands/ canonical names: set.mode, cmd.startup/shutdown/estop,
set.setpoint, set.flow-setpoint,
data.simulate-measurement, query.curves, query.cog,
child.register. execSequence demuxes by payload.action
to canonical cmd.* handlers.
CONTRACT.md inputs/outputs/events/children surface
110 basic tests pass (100 new + 10 pre-existing).
specificClass.js / nodeClass.js untouched — integration in P5 wave 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
101
test/basic/pressureRouter.basic.test.js
Normal file
101
test/basic/pressureRouter.basic.test.js
Normal file
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const PressureRouter = require('../../src/pressure/pressureRouter');
|
||||
|
||||
const SILENT = { warn() {}, debug() {} };
|
||||
|
||||
function makeFakeMeasurements() {
|
||||
const writes = [];
|
||||
return {
|
||||
writes,
|
||||
type() { return this; },
|
||||
variant() { return this; },
|
||||
position(p) { this._pos = p; return this; },
|
||||
child(c) { this._child = c; return this; },
|
||||
value(v, t, u) { writes.push({ pos: this._pos, child: this._child, value: v, t, u }); },
|
||||
};
|
||||
}
|
||||
|
||||
test('route("upstream", 1, ctx) writes to the upstream pressure slot', () => {
|
||||
const meas = makeFakeMeasurements();
|
||||
const router = new PressureRouter({
|
||||
measurements: meas,
|
||||
virtualPressureChildIds: { upstream: 'sim-u', downstream: 'sim-d' },
|
||||
resolveMeasurementUnit: () => 'mbar',
|
||||
logger: SILENT,
|
||||
});
|
||||
router.route('upstream', 1, { childId: 'real-1', unit: 'mbar', timestamp: 1234 });
|
||||
assert.equal(meas.writes.length, 1);
|
||||
assert.equal(meas.writes[0].pos, 'upstream');
|
||||
assert.equal(meas.writes[0].child, 'real-1');
|
||||
assert.equal(meas.writes[0].value, 1);
|
||||
assert.equal(meas.writes[0].u, 'mbar');
|
||||
});
|
||||
|
||||
test('virtual source: refresh hooks NOT called', () => {
|
||||
const meas = makeFakeMeasurements();
|
||||
let posCalled = 0, driftCalled = 0, healthCalled = 0;
|
||||
const router = new PressureRouter({
|
||||
measurements: meas,
|
||||
virtualPressureChildIds: { upstream: 'sim-u', downstream: 'sim-d' },
|
||||
resolveMeasurementUnit: () => 'mbar',
|
||||
updatePosition: () => { posCalled++; },
|
||||
refreshDrift: () => { driftCalled++; },
|
||||
refreshHealth: () => { healthCalled++; },
|
||||
logger: SILENT,
|
||||
});
|
||||
router.route('upstream', 7, { childId: 'sim-u', unit: 'mbar' });
|
||||
assert.equal(posCalled, 0);
|
||||
assert.equal(driftCalled, 0);
|
||||
assert.equal(healthCalled, 0);
|
||||
});
|
||||
|
||||
test('real source: all refresh hooks called', () => {
|
||||
const meas = makeFakeMeasurements();
|
||||
let posCalled = 0, driftCalled = 0, healthCalled = 0;
|
||||
const router = new PressureRouter({
|
||||
measurements: meas,
|
||||
virtualPressureChildIds: { upstream: 'sim-u', downstream: 'sim-d' },
|
||||
resolveMeasurementUnit: () => 'mbar',
|
||||
updatePosition: () => { posCalled++; },
|
||||
refreshDrift: () => { driftCalled++; },
|
||||
refreshHealth: () => { healthCalled++; },
|
||||
logger: SILENT,
|
||||
});
|
||||
router.route('upstream', 7, { childId: 'real-pt-1', unit: 'mbar' });
|
||||
assert.equal(posCalled, 1);
|
||||
assert.equal(driftCalled, 1);
|
||||
assert.equal(healthCalled, 1);
|
||||
});
|
||||
|
||||
test('rejected unit returns false and skips the write', () => {
|
||||
const meas = makeFakeMeasurements();
|
||||
const warns = [];
|
||||
const router = new PressureRouter({
|
||||
measurements: meas,
|
||||
virtualPressureChildIds: {},
|
||||
resolveMeasurementUnit: () => { throw new Error('bad unit'); },
|
||||
logger: { warn(m) { warns.push(m); }, debug() {} },
|
||||
});
|
||||
const ok = router.route('upstream', 1, { childId: 'x', unit: 'wat' });
|
||||
assert.equal(ok, false);
|
||||
assert.equal(meas.writes.length, 0);
|
||||
assert.match(warns[0], /Rejected pressure update/);
|
||||
});
|
||||
|
||||
test('childId null is treated as not-virtual', () => {
|
||||
const meas = makeFakeMeasurements();
|
||||
let posCalled = 0;
|
||||
const router = new PressureRouter({
|
||||
measurements: meas,
|
||||
virtualPressureChildIds: { upstream: 'sim-u' },
|
||||
resolveMeasurementUnit: () => 'mbar',
|
||||
updatePosition: () => { posCalled++; },
|
||||
logger: SILENT,
|
||||
});
|
||||
router.route('upstream', 2, { unit: 'mbar' });
|
||||
assert.equal(posCalled, 1);
|
||||
});
|
||||
Reference in New Issue
Block a user