Clone
3
Home
vps1_gitea_admin edited this page 2026-05-11 18:30:15 +00:00

monster

Reflects code as of 2a6a0bc · regenerated 2026-05-11 via npm run wiki:all If this banner is stale, the page may be out of date. Treat as informative, not authoritative.

1. What this node is

monster is an S88 Unit that runs an AQUON-scheduled flow-proportional sampling program. It aggregates measured + manual flow, blends a rain-scaled prediction, and emits a pulse whenever the integrated volume crosses m³ per pulse. Drives the physical "monsternamekast" (composite sampling cabinet) on a wastewater treatment plant.

2. Position in the platform

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
    op[(operator / AQUON)]
    weather[(Open-Meteo)]

    flow_up -->|flow.measured.upstream| monster
    flow_at -->|flow.measured.atequipment| monster
    op -->|set.schedule / data.flow / cmd.start| monster
    weather -->|set.rain| monster
    monster -->|child.register| parent
    monster -.evt.output.-> parent
    classDef pc fill:#0c99d9,color:#fff
    classDef unit fill:#50a8d9,color:#000
    classDef ctrl fill:#a9daee,color:#000

S88 colours: Process Cell #0c99d9, Unit #50a8d9, Control Module #a9daee. Source of truth: .claude/rules/node-red-flow-layout.md.

3. Capability matrix

Capability Status Notes
Flow-proportional sampling pulse Triggered when integrated temp_pulse ≥ 1.
Time-bounded sampling run Run length governed by constraints.samplingtime (hours).
AQUON schedule auto-start set.schedule parses AQUON rows; nextDate arms the next run.
Rain-scaled flow prediction set.rain aggregates Open-Meteo hourly precipitation into sumRain / avgRain.
Measured + manual flow blend flowTracker.getEffectiveFlow() picks measured if recent, else manual.
Minimum sample-interval cooldown Skips pulses inside minSampleIntervalSec window.
Bucket / weight tracking bucketVol, bucketWeight track the composite container.
Sub-sample volume override subSampleVolume fixed at 50 mL per schema.
Stateful FSM Monster is run/idle only — no formal state machine.

4. Code map

flowchart TB
    subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
        nc["buildDomainConfig()<br/>static DomainClass = Monster<br/>static commands"]
    end
    subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
        sc["Monster.configure()<br/>wires concerns, ChildRouter rules<br/>tick() → flowCalc → samplingProgram"]
    end
    subgraph concerns["src/ concern modules"]
        parameters["parameters/<br/>bounds + targets + rain index"]
        flow["flow/<br/>FlowTracker (measured + manual)"]
        rain["rain/<br/>RainAggregator (sum + avg)"]
        schedule["schedule/<br/>AQUON next-date parser"]
        sampling["sampling/<br/>samplingProgram + flowCalc"]
        io["io/<br/>output + statusBadge"]
        commands["commands/<br/>topic registry + handlers"]
    end
    nc --> sc
    sc --> parameters
    sc --> flow
    sc --> rain
    sc --> schedule
    sc --> sampling
    sc --> io
    nc --> commands
Module Owns Read first if you're changing…
parameters/ Bounds (minPuls, absMaxPuls, targetPuls), rain-index lookup, predicted-flow rate. Sampling bounds, rain scaling math.
flow/ FlowTracker — measured-child latch + manual flow + effective-flow pick. Flow source priority, dead-band logic.
rain/ RainAggregator — fold hourly Open-Meteo rows into sumRain / avgRain. Rain inputs, forecast horizon.
schedule/ AQUON schedule parsing, nextDate + daysPerYear derivation. Schedule arming, sample-name filtering.
sampling/ _beginRun / _endRun / _maybeEmitPulse — pulse integrator + cooldown guard. Pulse timing, cooldown behaviour.
io/ buildOutput, buildStatusBadge. Port-0 payload shape, status badge text.
commands/ Topic registry + payload validation + unit conversion. New input topics, alias deprecation.

5. Topic contract

Auto-generated from src/commands/index.js. Do NOT hand-edit between the markers. Re-run npm run wiki:contract.

Canonical topic Aliases Payload Unit Effect
cmd.start i_start any Trigger / release the sampler start gate.
set.schedule monsternametijden any Replace the sampling-times schedule.
set.rain rain_data any Push current rain-event data into the sampler logic.
data.flow input_q object Push the upstream flow measurement (payload: {value, unit}).
set.mode setMode any Switch the monster between auto / manual modes.
set.model-prediction model_prediction any Push the upstream rain-prediction snapshot used by the sampler.
child.register registerChild string Register a child node (typically a measurement) with this monster.

6. Child registration

Mirrors the ChildRouter declaration in specificClass.js → configure(). Monster only accepts measurement children whose asset.type is flow (or unset).

flowchart LR
    subgraph kids["accepted children (softwareType)"]
        m_up["measurement<br/>asset.type=flow<br/>position=upstream"]:::ctrl
        m_at["measurement<br/>asset.type=flow<br/>position=atequipment"]:::ctrl
        m_dn["measurement<br/>asset.type=flow<br/>position=downstream"]:::ctrl
    end
    m_up -->|flow.measured.upstream| ft[FlowTracker.handleMeasuredFlow]
    m_at -->|flow.measured.atequipment| ft
    m_dn -->|flow.measured.downstream| ft
    ft --> eff[getEffectiveFlow]
    classDef ctrl fill:#a9daee,color:#000
softwareType filter wired to side-effect
measurement asset.type=flow (or missing) flowTracker.handleMeasuredFlow Latches latest measured flow for getEffectiveFlow.
measurement asset.type anything else ignored Logged once at register.

7. Lifecycle — the sampling-program loop

sequenceDiagram
    participant child as measurement child
    participant ops as operator / AQUON
    participant monster as monster
    participant ft as flowTracker
    participant sp as samplingProgram
    participant out as Port-0 output

    child->>monster: flow.measured.<position>
    ops->>monster: set.schedule / cmd.start / data.flow
    Note over monster: every 1000 ms tick
    monster->>ft: getEffectiveFlow()
    monster->>monster: flowCalc → m3PerTick
    monster->>sp: samplingProgram
    alt cmd.start OR Date.now() ≥ nextDate
        sp->>sp: validateFlowBounds → _beginRun
    end
    alt running AND stop_time > now
        sp->>sp: integrate temp_pulse += m3PerTick / m3PerPuls
        sp->>sp: _maybeEmitPulse (cooldown-guarded)
    else stop_time elapsed
        sp->>sp: _endRun
    end
    monster->>out: msg{topic, payload (delta-compressed)}

One pulse per integrated m³ per pulse. The cooldown guard suppresses pulses inside minSampleIntervalSec and increments missedSamples.

8. Data model — getOutput()

What lands on Port 0. Built in buildOutput() from m.measurements.getFlattenedOutput() plus the sampling-run snapshot.

Key Type Unit Sample
avgRain number 0
bucketVol number 0
bucketWeight number 0
daysPerYear number 0
flowMax number 0
flowToNextPulseM3 number 0
invalidFlowBounds boolean false
m3PerPuls number 0
m3PerPulse number 0
m3Total number 0
maxVolume number 20
minSampleIntervalSec number 60
minVolume number 5
missedSamples number 0
nextDate undefined null
nominalFlowMin number 0
predFlow number 0
predM3PerSec number 0
predictedRateM3h number 0
pulse boolean false
pulseFraction number 0
pulsesRemaining number 200
q number 0
running boolean false
sampleCooldownMs number 0
sumPuls number 0
sumRain number 0
targetDeltaL number -10
targetDeltaM3 number -0.01
targetProgressPct number 0
targetVolumeM3 number 0.01
timeLeft number 0
timePassed number 0
timeToNextPulseSec number 0

Concrete sample (mid-run snapshot):

{
  "pulse": false,
  "running": true,
  "bucketVol": 1.25,
  "sumPuls": 25,
  "predFlow": 240.0,
  "m3PerPuls": 4,
  "q": 215.4,
  "timeLeft": 12340,
  "targetVolumeM3": 0.005,
  "targetProgressPct": 41.6,
  "sumRain": 3.2,
  "avgRain": 0.13,
  "nextDate": 1746940800000
}

Concrete samples must come from a known-good test run. Regenerate when concern modules change shape.

9. Configuration — editor form ↔ config keys

flowchart TB
    subgraph editor["Node-RED editor form"]
        f1[Sampling time hr]
        f2[Sampling period hr]
        f3[Min volume L]
        f4[Max weight kg]
        f5[Empty bucket weight kg]
        f6[Flowmeter on/off]
        f7[Min sample interval s]
    end
    subgraph config["Domain config slice"]
        c1[constraints.samplingtime]
        c2[constraints.samplingperiod]
        c3[constraints.minVolume]
        c4[constraints.maxWeight]
        c5[asset.emptyWeightBucket]
        c6[constraints.flowmeter]
        c7[constraints.minSampleIntervalSec]
    end
    f1 --> c1
    f2 --> c2
    f3 --> c3
    f4 --> c4
    f5 --> c5
    f6 --> c6
    f7 --> c7
Form field Config key Default Range Where used
Sampling time (hr) constraints.samplingtime 0 ≥ 0 run length, predFlow
Sampling period (hr) constraints.samplingperiod 24 ≥ 1 schedule arming
Min volume (L) constraints.minVolume 5 ≥ 5 bounds + invalid-flow guard
Max weight (kg) constraints.maxWeight 23 ≤ 23 bucket overload
Empty bucket weight (kg) asset.emptyWeightBucket 3 ≥ 0 bucketWeight
Sub-sample volume (mL) constraints.subSampleVolume 50 fixed per-pulse volume
Flowmeter present constraints.flowmeter true bool proportional vs time mode
Min sample interval (s) constraints.minSampleIntervalSec 60 ≥ 0 cooldown guard
Intake speed (m/s) constraints.intakeSpeed 0.3 ≥ 0 informational
Intake diameter (mm) constraints.intakeDiameter 12 ≥ 0 informational

10. State chart

Skipped — monster has no formal state machine. The running boolean toggles when _beginRun / _endRun fire. See section 7 for the sampling-program sequence, which is the closest analogue.

11. Examples

Tier File What it shows Status
Basic examples/basic.flow.json Inject + manual flow + dashboard, no parent in repo
Integration examples/integration.flow.json monster + measurement child + AQUON schedule in repo
Dashboard examples/monster-dashboard.flow.json Live FlowFuse charts (pulse, bucket, predFlow) in repo
Edge examples/edge.flow.json Cooldown guard + invalid-flow bounds in repo
API examples/monster-api-dashboard.flow.json dashboardAPI consumer wired in in repo

One screenshot per tier where helpful. PNG ≤ 200 KB under wiki/_partial-screenshots/monster/.

12. Debug recipes

Symptom First thing to check Where to look
pulse never fires Is running true? Check validateFlowBounds log — invalid bounds short-circuits the run. parameters/parameters.js
Pulses arrive too fast / skipped Cooldown guard active. Inspect missedSamples + minSampleIntervalSec. sampling/samplingProgram.js → _maybeEmitPulse
q always zero Measured-flow child not registered, manual flow not pushed. Watch Port 2 + data.flow history. flow/flowTracker.js
nextDate not arming set.schedule payload didn't include matching aquonSampleName row. schedule/schedule.js → regNextDate
sumRain zero with rain input rainAggregator.update ran while running=true (skipped). specificClass.js → updateRainData
Bucket overfilled before stop_time m3PerPuls rounded down; check predFlow vs effective q. sampling/samplingProgram.js → _beginRun

Never ship enableLog: 'debug' in a demo — fills the container log within seconds and obscures real errors. Use only for live debugging.

13. When you would NOT use this node

  • Use monster for AQUON-scheduled composite sampling on a wastewater plant. For a single grab sample on demand, fire cmd.start once and tear it down — monster is overkill for ad-hoc work.
  • Don't use monster as a generic flow totaliser. Use a measurement child with the right type/variant for raw flow integration.
  • Skip monster if you don't need pulse-proportional dosing — the time-mode (flowmeter=false) is currently informational only.

14. Known limitations / current issues

# Issue Tracked in
1 Edge test sampling-guards.edge.test.js cooldown-guard case is a pre-existing failure — the cooldown skip increments missedSamples but the assertion expects a different timing. test/edge/sampling-guards.edge.test.js
2 set.mode and set.model-prediction are reserved — handlers delegate to optional methods that don't exist yet. commands/handlers.js
3 Time-only mode (flowmeter=false) is not exercised — the sampling program assumes a flow source. sampling/samplingProgram.js
4 Sub-sample volume hard-coded at 50 mL (schema enforces min=max=50). generalFunctions/src/configs/monster.json