Replaces the prior stub/partial wiki with a Home + Reference-{Architecture,
Contracts,Examples,Limitations} + _Sidebar structure. Topic-contract and
data-model sections wrapped in AUTOGEN markers for the future wiki-gen tool.
Source-vs-spec contradictions surfaced and flagged inline (not silently
fixed). Pending-review notes mark sections that need a full node review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.3 KiB
monster
A monster models the physical "monsternamekast" (composite-sampling cabinet) on a wastewater treatment plant. It runs an AQUON-scheduled, flow-proportional sampling program: aggregates measured + manual flow, blends in a rain-scaled prediction, integrates volume, and emits a pulse event whenever the integrated volume crosses m³ per pulse. Used downstream of reactor / settler for compliance + process diagnostics — but note that the node only integrates flow in the current implementation; multi-constituent reporting (NH4, NO3, COD, TSS, …) is a downstream consumer concern, not produced by this node.
Note
Pending full node review (2026-05). Content reflects
CONTRACT.mdand current source only.
At a glance
| Thing | Value |
|---|---|
| What it represents | One composite-sampling cabinet running AQUON-scheduled, flow-proportional sampling |
| S88 level | Unit |
| Use it when | You need scheduled composite samples with flow-proportional pulses for a benchtop analyser / lab pickup |
| Don't use it for | Generic flow totalising (use measurement), ad-hoc grab samples (fire cmd.start once instead), or analyser results — monster emits pulses, not analyte values |
| Children it accepts | measurement with asset.type='flow' (or unset) |
| Parents it talks to | Any node that issues cmd.start / set.schedule / set.rain / data.flow (typically a Process Cell coordinator) |
How it fits
flowchart LR
parent[plant parent<br/>Process Cell]:::pc
monster[monster<br/>Unit]:::unit
flow_up[measurement<br/>type=flow<br/>position=upstream]:::ctrl
flow_at[measurement<br/>type=flow<br/>position=atequipment]:::ctrl
flow_dn[measurement<br/>type=flow<br/>position=downstream]:::ctrl
op[(operator / AQUON)]
weather[(Open-Meteo)]
flow_up -->|flow.measured.upstream| monster
flow_at -->|flow.measured.atequipment| monster
flow_dn -->|flow.measured.downstream| monster
op -->|set.schedule / data.flow / cmd.start| monster
weather -->|set.rain| monster
monster -->|child.register| parent
monster -.evt output (pulse / bucketVol).-> parent
classDef pc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef ctrl fill:#a9daee,color:#000
S88 colours are anchored in .claude/rules/node-red-flow-layout.md. The editor node tile in monster.html is currently #4f8582 (teal) and pending cleanup — Section 16 of the layout rules tracks it.
Try it — 3-minute demo
Import the basic example flow, deploy, and drive the sampler through a single run.
curl -X POST -H 'Content-Type: application/json' \
--data @nodes/monster/examples/basic.flow.json \
http://localhost:1880/flow
What to click after deploy (the inject buttons map to the topics in Reference — Contracts):
set.rain— push an Open-Meteo precipitation snapshot sosumRain/avgRainpopulate (otherwise the rain-scaled prediction stays atnominalFlowMin).set.schedule— push an AQUON row array sonextDatearms.data.flow = {value: 240, unit: 'm3/h'}— supply a manual flow value (or wire ameasurementchild for the measured path).cmd.start = true— releases the start gate. On the next 1000 ms tick the sampling program checksvalidateFlowBounds; ifnominalFlowMin < flowMax,_beginRunfires,runningflips totrue, and the bucket integrator starts.- Watch Port 0:
pulseblips totruefor one tick whenevertemp_pulsecrosses 1;bucketVolrises in 50 mL steps (the hard-codedsubSampleVolume);sumPulsincrements. - After
constraints.samplingtimehours_endRunfires andrunningflips tofalse.
Important
GIF needed. Demo recording of steps 1–6 with the live status panel. Save as
wiki/_partial-gifs/monster/basic-demo.gif, target ≤ 1 MB aftergifsicle -O3 --lossy=80.
The seven things you'll send
| Topic | Aliases | Payload | What it does |
|---|---|---|---|
cmd.start |
i_start |
truthy / falsy | Sets source.i_start. On the next tick a sampling run begins if validateFlowBounds passes. |
set.schedule |
monsternametijden |
array of AQUON rows (SAMPLE_NAME, DESCRIPTION, SAMPLED_DATE, START_DATE, END_DATE) |
Stores the schedule and recomputes nextDate + daysPerYear for the configured aquonSampleName. |
set.rain |
rain_data |
per-location rain forecast (Open-Meteo shape) | Aggregates hourly precipitation into sumRain / avgRain; feeds the rain-scaled flow prediction. Ignored while running=true. |
data.flow |
input_q |
{ value: number, unit: string } |
Converts to m³/h and pushes into flow.manual.atequipment. Blends with measured-child flow in getEffectiveFlow(). |
set.mode |
setMode |
string | Reserved — handler delegates to source.setMode() which is currently undefined. No-op today. |
set.model-prediction |
model_prediction |
numeric | Reserved — handler delegates to source.setModelPrediction() which is currently undefined. No-op today. |
child.register |
registerChild |
string (child node id) | Register a measurement child. Port 2 wiring does this automatically. |
There is no query.* topic on monster (unlike rotatingMachine's query.curves / query.cog). All state is on Port 0.
What you'll see come out
Sample Port 0 message (mid-run snapshot, delta-compressed):
{
"topic": "monster#cabinet_1",
"payload": {
"running": true,
"pulse": false,
"bucketVol": 1.25,
"bucketWeight": 4.25,
"sumPuls": 25,
"m3PerPuls": 4,
"m3Total": 100.0,
"q": 215.4,
"predFlow": 240.0,
"predM3PerSec": 0.067,
"timeLeft": 12340,
"timePassed": 600,
"targetVolumeM3": 0.005,
"targetProgressPct": 41.6,
"targetDeltaL": -2.93,
"predictedRateM3h": 240.0,
"sumRain": 3.2,
"avgRain": 0.13,
"nextDate": 1746940800000,
"daysPerYear": 12,
"missedSamples": 0,
"sampleCooldownMs": 0,
"invalidFlowBounds": false
}
}
| Field | Meaning |
|---|---|
running |
Run/idle flag — monster has no formal FSM, just this boolean and the timer fields below. |
pulse |
true for the tick on which a pulse is emitted; falls back to false on the next tick. |
bucketVol / bucketWeight |
Accumulated composite volume (L) and total bucket mass (kg = vol + emptyWeightBucket). |
sumPuls / pulsesRemaining |
Pulses fired this run vs target ceiling. |
m3PerPuls |
Volume per pulse, set at _beginRun from predFlow / targetPuls. |
m3Total |
Total integrated flow this run (m³). |
q |
Effective flow (m³/h) — flowTracker.getEffectiveFlow() blends measured + manual. |
predFlow / predM3PerSec |
Predicted total volume over the run window (m³) and its average rate. |
timeLeft / timePassed |
Run-window seconds remaining / elapsed. |
targetVolumeM3 / targetProgressPct / targetDeltaL |
Target composite volume (m³), % of target accumulated, signed L delta against target. |
predictedRateM3h |
Rain-scaled flow prediction (m³/h) — between nominalFlowMin and flowMax. |
sumRain / avgRain |
Probability-weighted hourly precipitation, summed / averaged across locations. |
nextDate / daysPerYear |
Next scheduled run epoch ms; count of remaining runs this calendar year. |
missedSamples / sampleCooldownMs |
Cooldown-blocked pulse count; ms remaining on the active cooldown. |
invalidFlowBounds |
True when nominalFlowMin >= flowMax — gates _beginRun. |
Full Port-0 key list: see Reference — Contracts — Data model.
Key shape is flat scalars on the snapshot plus a MeasurementContainer.getFlattenedOutput() flush of flow.<variant>.<position> entries. monster does not use the four-segment <type>.<variant>.<position>.<childId> shape that rotatingMachine emits — no childId is appended because monster fans incoming children into the same three positions and the latest value wins.
Status badge
| State | Badge | Fill |
|---|---|---|
invalidFlowBounds=true |
Config error: nominalFlowMin (…) >= flowMax (…) |
red |
running=true + cooldown active |
SAMPLING (Ns) · <bucketVol>/<maxVolume> L |
yellow ring |
running=true + normal |
AI: RUNNING · <bucketVol>/<maxVolume> L |
green dot |
| idle | AI: IDLE |
grey ring |
Need more?
| Page | What you'll find |
|---|---|
| Reference — Contracts | Full topic contract, config schema, child registration filters |
| Reference — Architecture | Code map, sampling-program loop, prediction + cooldown pipeline |
| Reference — Examples | Shipped example flows + debug recipes |
| Reference — Limitations | When not to use, known limitations, open questions |