P5 wave 2: convert rotatingMachine to BaseDomain + extract helper modules
specificClass.js: 1760 → 400 lines.
Machine extends BaseDomain. configure() wires curves + predictors +
drift + pressure + state bindings + measurement handlers + flow
controller. ChildRouter handles pressure/flow/power/temperature
measurement events; custom registerChild override preserves the
dedup + virtual-vs-real pressure tracking the integration tests
pin.
Added small host-aware helper modules to fit the 400-line cap:
src/prediction/predictionMath.js (calcFlow/Power/Ctrl)
src/prediction/efficiencyMath.js (calcCog/EfficiencyCurve/etc.)
src/pressure/pressureSelector.js (getMeasuredPressure source preference)
src/state/sequenceController.js (executeSequence/setpoint/wait helpers)
src/measurement/childRegistrar.js (custom registerChild path)
src/drift/healthRefresh.js (drift status update wrappers)
src/io/output.js (buildOutput + buildStatusBadge)
unitPolicy: live UnitPolicy methods .canonical()/.output()/.curve()
bridged to legacy property-path readers via a frozen view object —
same pattern as MGC. See OPEN_QUESTIONS.md.
nodeClass.js: 433 → 61 lines.
Extends BaseNodeAdapter. tickInterval=null (event-driven on state +
measurement events). buildDomainConfig stamps the rotatingMachine
state + errorMetrics slices on the domain config so configure()
builds them from there.
5 tests adjusted (4 nodeClass-config, 1 error-paths) — pre-refactor
they pinned private methods (_loadConfig, _setupSpecificClass,
_attachInputHandler, _updateNodeStatus) that no longer exist. New
versions drive the public BaseNodeAdapter surface or call extracted
io/state-machine helpers directly. See OPEN_QUESTIONS.md 2026-05-10
"private nodeClass tests" for the deferred rewrite plan.
196 / 196 tests pass (basic 110 + integration ~80 + edge ~6).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
86
src/state/sequenceController.js
Normal file
86
src/state/sequenceController.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Sequence + setpoint orchestration. Pre-refactor lived inline on
|
||||
* Machine; extracted so the orchestrator stays focused. All behaviour
|
||||
* is preserved verbatim including the interruptible-shutdown abort
|
||||
* dance and the operational-state ramp-to-zero before shutdown.
|
||||
*/
|
||||
|
||||
function resolveSetpointBounds(host) {
|
||||
const stateMin = Number(host.state?.movementManager?.minPosition);
|
||||
const stateMax = Number(host.state?.movementManager?.maxPosition);
|
||||
const curveMin = Number(host.predictFlow?.currentFxyXMin);
|
||||
const curveMax = Number(host.predictFlow?.currentFxyXMax);
|
||||
const minCands = [stateMin, curveMin].filter(Number.isFinite);
|
||||
const maxCands = [stateMax, curveMax].filter(Number.isFinite);
|
||||
const fbMin = Number.isFinite(stateMin) ? stateMin : 0;
|
||||
const fbMax = Number.isFinite(stateMax) ? stateMax : 100;
|
||||
let min = minCands.length ? Math.max(...minCands) : fbMin;
|
||||
let max = maxCands.length ? Math.min(...maxCands) : fbMax;
|
||||
if (min > max) {
|
||||
host.logger.warn(`Invalid setpoint bounds detected (min=${min}, max=${max}). Falling back to movement bounds.`);
|
||||
min = fbMin; max = fbMax;
|
||||
}
|
||||
return { min, max };
|
||||
}
|
||||
|
||||
async function setpoint(host, target) {
|
||||
try {
|
||||
if (!Number.isFinite(target)) { host.logger.error('Invalid setpoint: Setpoint must be a finite number.'); return; }
|
||||
const { min, max } = resolveSetpointBounds(host);
|
||||
const constrained = Math.min(Math.max(target, min), max);
|
||||
if (constrained !== target) host.logger.warn(`Requested setpoint ${target} constrained to ${constrained} (min=${min}, max=${max})`);
|
||||
host.logger.info(`Setting setpoint to ${constrained}. Current position: ${host.state.getCurrentPosition()}`);
|
||||
await host.state.moveTo(constrained);
|
||||
} catch (e) { host.logger.error(`Error setting setpoint: ${e}`); }
|
||||
}
|
||||
|
||||
function waitForOperational(host, timeoutMs = 2000) {
|
||||
if (host.state.getCurrentState() === 'operational') return Promise.resolve('operational');
|
||||
return new Promise((resolve) => {
|
||||
let done = false;
|
||||
const timer = setTimeout(() => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
host.state.emitter.off('stateChange', onChange);
|
||||
resolve(host.state.getCurrentState());
|
||||
}, timeoutMs);
|
||||
const onChange = (newState) => {
|
||||
if (done) return;
|
||||
if (newState === 'operational') {
|
||||
done = true; clearTimeout(timer);
|
||||
host.state.emitter.off('stateChange', onChange);
|
||||
resolve('operational');
|
||||
}
|
||||
};
|
||||
host.state.emitter.on('stateChange', onChange);
|
||||
});
|
||||
}
|
||||
|
||||
async function executeSequence(host, rawName) {
|
||||
const name = typeof rawName === 'string' ? rawName.toLowerCase() : rawName;
|
||||
const sequence = host.config.sequences[name];
|
||||
if (!sequence || sequence.size === 0) {
|
||||
host.logger.warn(`Sequence '${name}' not defined.`);
|
||||
return;
|
||||
}
|
||||
const interruptible = new Set(['shutdown', 'emergencystop']);
|
||||
if (interruptible.has(name)) host.state.delayedMove = null;
|
||||
const current = host.state.getCurrentState();
|
||||
if (interruptible.has(name) && (current === 'accelerating' || current === 'decelerating')) {
|
||||
host.logger.warn(`Sequence '${name}' requested during '${current}'. Aborting active movement.`);
|
||||
host.state.abortCurrentMovement(`${name} sequence requested`, { returnToOperational: true });
|
||||
await waitForOperational(host, 2000);
|
||||
}
|
||||
if (host.state.getCurrentState() === 'operational' && name === 'shutdown') {
|
||||
host.logger.info(`Machine will ramp down to position 0 before performing ${name} sequence`);
|
||||
await setpoint(host, 0);
|
||||
}
|
||||
host.logger.info(` --------- Executing sequence: ${name} -------------`);
|
||||
for (const s of sequence) {
|
||||
try { await host.state.transitionToState(s); }
|
||||
catch (e) { host.logger.error(`Error during sequence '${name}': ${e}`); break; }
|
||||
}
|
||||
host.updatePosition();
|
||||
}
|
||||
|
||||
module.exports = { setpoint, executeSequence, resolveSetpointBounds, waitForOperational };
|
||||
Reference in New Issue
Block a user