Files
EVOLV/.claude/refactor/MODULE_SPLIT.md
znetsixe 91e4255ef5 Add refactor planning docs (.claude/refactor/)
Platform-wide refactor plan: README, CONVENTIONS, CONTRACTS,
MODULE_SPLIT, TASKS, OPEN_QUESTIONS. Source of truth for the
phased refactor across all 12 submodules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 18:22:35 +02:00

207 lines
10 KiB
Markdown

# Per-node module split
Where each concern lives **after** the refactor. All paths are relative
to `nodes/<nodeName>/src/`.
## Generic node template (any node post-refactor)
```
nodes/<name>/
<name>.js # Node-RED entry: registerType + admin endpoints (≤ 50 lines)
<name>.html # Form template + thin oneditprepare/oneditsave (≤ 250 lines)
CONTRACT.md # Generated from commands/ + hand-written events
examples/
01-basic.json
02-integration.json
03-dashboard.json # optional
src/
nodeClass.js # extends BaseNodeAdapter; ~25 lines
specificClass.js # extends BaseDomain; orchestrator only; ~150 lines
editor.js # client-side JS for HTML, served via admin endpoint (only if non-trivial UI)
commands/
index.js # the command registry array
handlers.js # the handler functions
<concern>/ # one folder per domain concern (see per-node sections below)
...
test/
basic/
integration/
edge/
```
## pumpingStation (Process Cell — L5, `#0c99d9`)
```
src/
nodeClass.js # ~25 lines, extends BaseNodeAdapter
specificClass.js # ~150 lines, orchestrator
editor.js # extracted SVG/redraw logic from the .html (~260 lines)
commands/
index.js # set.mode | set.demand | set.inflow | calibrate.* | child.register
handlers.js
basin/
BasinGeometry.js # initBasinProperties + level<->volume conversions
thresholdValidator.js # _validateThresholdOrdering — pure function
measurement/
flowAggregator.js # _selectBestNetFlow + _updatePredictedVolume + _computeRemainingTime + _levelRate + _deriveDirection
measurementRouter.js # _handleMeasurement + _onLevelMeasurement + _onPressureMeasurement
calibration.js # calibratePredictedVolume + calibratePredictedLevel + setManualInflow
control/
levelBased.js # _controlLevelBased + _scaleLevelToFlowPercent + _applyMachineGroupLevelControl
flowBased.js # placeholder for the flow mode; clearly stubbed
manual.js # forwardDemandToChildren
index.js # { 'levelbased': ..., 'flowbased': ..., 'manual': ... }
safety/
safetyController.js # evaluate() — split internally into dryRunRule + overfillRule
io/
statusBadge.js # getStatusBadge composition (was nodeClass._updateNodeStatus)
output.js # getOutput, mostly a pass-through to measurements + basin snapshot
configBuilder.js # extracted _loadConfig mapping
examples/
standalone-demo.js # extracted from the bottom of specificClass.js
```
## measurement (Control Module — L2, `#a9daee`)
The good news: `Channel.js` already exists and is pure. Most of the
analog mode in `specificClass.js` is duplication that vanishes when the
analog path also goes through `Channel`.
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~150 lines, orchestrator over modes
channel/
Channel.js # KEEP — already clean, the model for everything else
modes/
analogMode.js # one Channel built from flat config; routes msg.payload number
digitalMode.js # N channels from config.channels[]; routes msg.payload object
index.js # { analog, digital }
simulation/
simulator.js # simulateInput — random walk over the configured range
calibration/
calibrator.js # calibrate + isStable + standardDeviation helpers (drop duplicates of the static helpers in Channel)
commands/
index.js # set.simulator | set.outlierDetection | cmd.calibrate | data.measurement
handlers.js
```
`statistics/` (mean/stdDev/median/etc.) — promote to
`generalFunctions/src/stats/`. Both `Channel.static helpers` and the
calibrator use them.
## machineGroupControl (Unit — L4, `#50a8d9`)
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~200 lines orchestrator; tick/handlePressureChange/handleInput
groupOps/
groupOperatingPoint.js # _equalizeOperatingPoint, _readChildMeasurement, _writeMeasurement
groupCurves.js # _groupFlow, _groupPower, _groupNCog, _groupCalcPower
totals/
totalsCalculator.js # calcDynamicTotals, calcAbsoluteTotals, activeTotals
combinatorics/
pumpCombinations.js # validPumpCombinations + checkSpecialCases
optimizer/
bestCombination.js # calcBestCombination (CoG-based)
bepGravitation.js # calcBestCombinationBEPGravitation + redistributeFlowBySlope + estimateSlopesAtBEP
index.js # picks the optimizer by config
efficiency/
groupEfficiency.js # calcGroupEfficiency + calcDistanceBEP + helpers
dispatch/
demandDispatcher.js # uses LatestWinsGate; handleInput + per-machine fanout
registration/ # auto via ChildRouter — file may be tiny
commands/
index.js # set.mode | set.scaling | set.demand | child.register
handlers.js
```
## rotatingMachine (Equipment Module — L3, `#86bbdd`)
The biggest specificClass (1760 lines). The split mirrors the natural
boundaries the existing comments suggest.
```
src/
nodeClass.js # extends BaseNodeAdapter
specificClass.js # ~250 lines orchestrator
curves/
curveLoader.js # loadCurve wrapper + model resolution
curveNormalizer.js # _normalizeMachineCurve + _normalizeCurveSection (unit conversion + anomaly detection)
reverseCurve.js # the existing reverseCurve helper
prediction/
predictors.js # owns predictFlow / predictPower / predictCtrl (delegates to generalFunctions/predict)
groupPredictors.js # group-scope predictors used when an MGC parent calls setGroupOperatingPoint
operatingPoint.js # current operating point: pressure source, derived flow & power
drift/
driftAssessor.js # _updateMetricDrift + assessDrift + _applyDriftPenalty
predictionHealth.js # composes flow/power/pressure drift into a HealthStatus
pressure/
virtualChildren.js # _initVirtualPressureChildren + dashboard-sim children
pressureInitialization.js # getPressureInitializationStatus + tracking real children
pressureRouter.js # updateMeasuredPressure + per-position handling
state/ # adapter to generalFunctions/state — thin glue, lifecycle hooks
stateBindings.js # the position/state event handlers that fire _updateState etc.
measurement/
measurementHandlers.js # updateMeasured{Flow,Power,Temperature} + _callMeasurementHandler
flow/
flowController.js # handleInput dispatch by source/action/parameter — feeds state machine
display/
workingCurves.js # showWorkingCurves + showCoG (admin endpoints)
commands/
index.js # set.mode | cmd.startup | cmd.shutdown | cmd.estop | cmd.setpoint | cmd.flow-setpoint | data.simulate-measurement | query.curves | query.cog
handlers.js
```
## remaining nodes (skeleton — they get the platform refactor only)
| Node | Notes |
|---|---|
| `valve` | Equipment Module. Smaller than rotatingMachine — concern split likely just `state/`, `commands/`, `position/`. |
| `valveGroupControl` | Unit. Similar to MGC but no flow-power optimization — straightforward `position-aggregator` + `commands/`. |
| `reactor` | Unit. Domain is biological kinetics (ASM); will need a `kinetics/` folder. Big — second-tier candidate for deeper split. |
| `settler` | Unit. Has the recently-fixed `_connectReactor` integration; keep that wired through `ChildRouter`. |
| `monster` | Unit. Multi-parameter monitoring; the parameter set itself is config-driven. |
| `diffuser` | Equipment Module. Aeration controller. Likely small. |
| `dashboardAPI` | Utility. InfluxDB endpoints. Likely no `BaseDomain` — it's a passive HTTP server. |
The "skeleton" refactor for these is just:
- Convert `nodeClass.js` to extend `BaseNodeAdapter`.
- Convert `specificClass.js` to extend `BaseDomain`.
- Move the input switch to `commands/`.
- Add `getStatusBadge()` if not present.
- Use `ChildRouter` for registration.
- File splits driven by file size — if `specificClass` < 300 lines, leave it alone for now.
## generalFunctions itself
```
src/
configs/ # unchanged — JSON schemas per node
helper/ # eventually split into infra/ + domain/, but not in this refactor
measurements/ # MeasurementContainer — unchanged
nodered/ # NEW — node-RED-side infra
BaseNodeAdapter.js
commandRegistry.js
statusBadge.js # composition helpers
statusUpdater.js # the 1 Hz status-loop wrapper
index.js
domain/ # NEW — domain-side infra
BaseDomain.js
UnitPolicy.js
ChildRouter.js
LatestWinsGate.js
HealthStatus.js
index.js
stats/ # NEW — promoted from measurement (mean, std, median, mad, lerp)
index.js
```
Existing exports (`logger`, `configManager`, `outputUtils`,
`MeasurementContainer`, `predict`, `interpolation`, `state`, …) stay
exactly where they are. Imports keep working unchanged.
`generalFunctions/index.js` adds new exports alongside existing ones.
Nothing is removed in this refactor.