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>
207 lines
10 KiB
Markdown
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.
|