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:
61
src/display/workingCurves.js
Normal file
61
src/display/workingCurves.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Read-only snapshots of the active machine curves and the centre-of-gravity
|
||||
* statistics. These back the rotatingMachine admin endpoints used by the
|
||||
* editor (`/rotatingMachine/working-curves`, `/rotatingMachine/cog`).
|
||||
*
|
||||
* Both functions accept a single `predictors` argument — an object describing
|
||||
* the current curve state. By taking everything via that one parameter the
|
||||
* helpers stay pure and trivially testable with a plain fixture; the host
|
||||
* just passes itself (or a slim adapter) in.
|
||||
*
|
||||
* Expected shape of `predictors`:
|
||||
* {
|
||||
* hasCurve: boolean,
|
||||
* predictFlow, predictPower, // generalFunctions/predict instances
|
||||
* getCurrentCurves(): { powerCurve, flowCurve },
|
||||
* calcCog(): { cog, cogIndex, NCog, minEfficiency },
|
||||
* cog, cogIndex, NCog,
|
||||
* minEfficiency,
|
||||
* currentEfficiencyCurve,
|
||||
* absDistFromPeak, relDistFromPeak,
|
||||
* }
|
||||
*/
|
||||
|
||||
const NO_CURVE_ERROR = 'No curve data available';
|
||||
|
||||
function showCoG(predictors) {
|
||||
if (!predictors || !predictors.hasCurve) {
|
||||
return { error: NO_CURVE_ERROR, cog: 0, NCog: 0, cogIndex: 0 };
|
||||
}
|
||||
const { cog, cogIndex, NCog, minEfficiency } = predictors.calcCog();
|
||||
return {
|
||||
cog,
|
||||
cogIndex,
|
||||
NCog,
|
||||
NCogPercent: Math.round(NCog * 100 * 100) / 100,
|
||||
minEfficiency,
|
||||
currentEfficiencyCurve: predictors.currentEfficiencyCurve,
|
||||
absDistFromPeak: predictors.absDistFromPeak,
|
||||
relDistFromPeak: predictors.relDistFromPeak,
|
||||
};
|
||||
}
|
||||
|
||||
function showWorkingCurves(predictors) {
|
||||
if (!predictors || !predictors.hasCurve) {
|
||||
return { error: NO_CURVE_ERROR };
|
||||
}
|
||||
const { powerCurve, flowCurve } = predictors.getCurrentCurves();
|
||||
return {
|
||||
powerCurve,
|
||||
flowCurve,
|
||||
cog: predictors.cog,
|
||||
cogIndex: predictors.cogIndex,
|
||||
NCog: predictors.NCog,
|
||||
minEfficiency: predictors.minEfficiency,
|
||||
currentEfficiencyCurve: predictors.currentEfficiencyCurve,
|
||||
absDistFromPeak: predictors.absDistFromPeak,
|
||||
relDistFromPeak: predictors.relDistFromPeak,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { showWorkingCurves, showCoG };
|
||||
Reference in New Issue
Block a user