# generalFunctions
  
**generalFunctions** is the shared infrastructure every EVOLV node depends on. It provides the base classes all nodes extend (`BaseDomain`, `BaseNodeAdapter`), the command dispatch engine, the measurement store, unit-policy system, child-registration machinery, InfluxDB output formatting, and a set of domain utilities (PID, curve interpolation, prediction, statistics, coolprop). Nodes hold zero duplicated scaffolding — they only write the logic that differs.
---
## At a glance
| Thing | Value |
|:---|:---|
| What it is | The shared library — not a Node-RED node, never placed in a flow |
| Kind | Shared library (`require('generalFunctions')`) |
| Consumed by | All 12 EVOLV nodes (rotatingMachine, MGC, pumpingStation, valve, VGC, reactor, settler, monster, measurement, diffuser, dashboardAPI) |
| Import style | Package root only — `const { BaseDomain, UnitPolicy } = require('generalFunctions');` |
| Side effects on a flow | None — the library has no editor form, no node registration |
| Cross-node coupling | Through this library's API surface + Node-RED messages only — never direct imports between node packages |
---
## How it fits
```mermaid
flowchart LR
gf["generalFunctions
(shared library)"]:::lib
rm["rotatingMachine
Equipment"]:::equip
mgc["machineGroupControl
Unit"]:::unit
ps["pumpingStation
Process Cell"]:::proc
meas["measurement
Control Module"]:::ctrl
valve["valve
Equipment"]:::equip
vgc["valveGroupControl
Unit"]:::unit
reactor["reactor
Unit"]:::unit
settler["settler
Unit"]:::unit
monster["monster
Unit"]:::unit
diffuser["diffuser
Equipment"]:::equip
dashAPI["dashboardAPI
utility"]:::util
gf --> rm
gf --> mgc
gf --> ps
gf --> meas
gf --> valve
gf --> vgc
gf --> reactor
gf --> settler
gf --> monster
gf --> diffuser
gf --> dashAPI
classDef lib fill:#222,color:#fff,stroke:#444
classDef proc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef util fill:#dddddd,color:#000
```
Every EVOLV node declares `generalFunctions` as a `dependencies` entry and imports from the package root only. The library has no S88 level of its own — it is the substrate the S88-classified nodes are built on.
---
## How to import
Single root import, destructure what you need:
```js
const {
// Platform base classes
BaseDomain, BaseNodeAdapter, ChildRouter, UnitPolicy, HealthStatus, LatestWinsGate,
// Node-RED bridge
createRegistry, CommandRegistry, statusBadge, StatusUpdater,
// Measurement + config
MeasurementContainer, configManager, configUtils, validation,
// Output formatting + logging
outputUtils, logger,
// Child registration
childRegistrationUtils,
// Unit conversion + physics
convert, Fysics, gravity, coolprop,
// Control + prediction
PIDController, CascadePIDController, createPidController, createCascadePidController,
predict, interpolation, nrmse, stats, state,
// Editor menus
MenuManager,
// Asset registry
assetResolver, AssetResolver, FileBackend, HttpBackend,
// Constants
POSITIONS, POSITION_VALUES, isValidPosition,
} = require('generalFunctions');
```
> [!IMPORTANT]
> Never import internal paths (`require('generalFunctions/src/domain/UnitPolicy')`). Only the package root is contractual; internal layout may move.
For the full export list with signatures and stability tags, see [Reference — Contracts](Reference-Contracts).
---
## Module map — what lives where
```mermaid
flowchart TB
subgraph domain["src/domain/ — base classes"]
BD["BaseDomain.js"]
CR["ChildRouter.js"]
UP["UnitPolicy.js"]
LWG["LatestWinsGate.js"]
HS["HealthStatus.js"]
end
subgraph nodered["src/nodered/ — Node-RED adapter layer"]
BNA["BaseNodeAdapter.js"]
CMR["commandRegistry.js"]
SB["statusBadge.js"]
SU["statusUpdater.js"]
end
subgraph measurements["src/measurements/ — measurement store"]
MC["MeasurementContainer.js"]
MB["MeasurementBuilder.js"]
Meas["Measurement.js"]
end
subgraph helper["src/helper/ — shared utilities"]
LOG["logger.js"]
OU["outputUtils.js"]
CRU["childRegistrationUtils.js"]
CFG["configUtils.js"]
VAL["validationUtils.js"]
MU["menuUtils.js"]
GR["gravity.js"]
end
subgraph predict_grp["src/predict/ — curve prediction"]
PRED["predict_class.js"]
INTERP["interpolation.js"]
end
subgraph configs["src/configs/ — schema registry"]
CFGM["index.js (ConfigManager)"]
JSON["*.json — per-node schemas"]
end
subgraph math["numeric & domain utilities"]
PID["src/pid/"]
NRMSE["src/nrmse/"]
STATS["src/stats/"]
OUT["src/outliers/"]
STATE["src/state/"]
CONV["src/convert/"]
COOL["src/coolprop-node/"]
FYS["src/convert/fysics.js"]
end
subgraph menu_grp["src/menu/"]
MM["MenuManager"]
end
subgraph constants["src/constants/"]
POS["positions.js"]
end
BD --> CR
BD --> UP
BD --> MC
BD --> CRU
BD --> LOG
BNA --> BD
BNA --> CMR
BNA --> OU
BNA --> SU
```
| Directory | Primary export | Read first if you're changing… |
|:---|:---|:---|
| `src/domain/` | `BaseDomain`, `ChildRouter`, `UnitPolicy`, `LatestWinsGate`, `HealthStatus` | Base class contracts, child routing, unit system |
| `src/nodered/` | `BaseNodeAdapter`, `CommandRegistry`, `statusBadge`, `StatusUpdater` | Input dispatch, output loops, editor status |
| `src/measurements/` | `MeasurementContainer` | Measurement storage, statistics, 4-segment key output |
| `src/helper/` | `logger`, `outputUtils`, `childRegistrationUtils`, `configUtils`, `validationUtils`, `menuUtils`, `gravity` | Logging, InfluxDB formatting, child registration |
| `src/configs/` | `ConfigManager` + per-node JSON schemas | Schema loading, config validation, default values |
| `src/predict/` | `predict`, `interpolation` | Characteristic curve fitting + flow/power prediction |
| `src/pid/` | `PIDController`, `CascadePIDController` | Closed-loop control |
| `src/nrmse/` | `ErrorMetrics` (NRMSE) | Prediction quality scoring |
| `src/stats/` | `stats` (mean, stddev, median) | Statistical reducers |
| `src/outliers/` | `DynamicClusterDeviation` | Online outlier detection |
| `src/state/` | `state`, `StateManager`, `MovementManager` | FSM for valve / machine state machines |
| `src/convert/` | `convert`, `Fysics` | Unit conversion, physical constants |
| `src/coolprop-node/` | `coolprop` | Thermodynamic property lookup |
| `src/menu/` | `MenuManager` | Editor-form dropdown population |
| `src/registry/` | `assetResolver`, `AssetResolver`, `FileBackend`, `HttpBackend` | Asset metadata lookup (replaces ad-hoc JSON readers) |
| `src/constants/` | `POSITIONS`, `POSITION_VALUES`, `isValidPosition` | Canonical spatial position constants |
---
## What you'll send (the platform contract)
This library doesn't accept `msg.topic` directly — nodes do. But every node's `nodeClass.js` and `specificClass.js` route through the same primitives:
| Primitive | Role |
|:---|:---|
| `BaseNodeAdapter.input(msg)` | Routes incoming Node-RED messages through the node's `CommandRegistry`, applies unit normalisation, then dispatches to the handler. |
| `CommandRegistry` | Topic + alias map. Handlers are pure functions; `units: {measure, default}` triggers automatic `convert` normalisation. |
| `ChildRouter` | Declarative parent-side routing. `.onRegister(type, cb)`, `.onMeasurement(type, filter, cb)`, `.onPrediction(type, filter, cb)`. |
| `MeasurementContainer.type().variant().position().value()` | Chainable write. Flattened output emits 4-segment keys `...`. |
| `UnitPolicy.declare({canonical, output, curve?})` | The per-node unit triple. Used by `MeasurementContainer` (auto-convert on write) and by the output formatter (render in `output` units). |
| `outputUtils.formatMsg(snapshot, config, mode)` | Delta-compresses successive snapshots. Returns `undefined` when nothing changed. |
| `HealthStatus.ok / degraded / compose` | Frozen plain-object factory for prediction-quality state. |
| `LatestWinsGate.fire(value)` | Serialises async dispatches; the latest call wins, intermediates are marked `SUPERSEDED`. |
For full signatures and stability tags see [Reference — Contracts](Reference-Contracts).
---
## What you'll see come out
A node that imports `BaseNodeAdapter` automatically gets the three EVOLV ports:
| Port | Carries | Built by |
|:---|:---|:---|
| 0 (process) | Delta-compressed state snapshot (the `getOutput()` return) | `outputUtils.formatMsg(snapshot, config, 'process')` |
| 1 (telemetry) | InfluxDB line-protocol payload (same fields) | `outputUtils.formatMsg(snapshot, config, 'influxdb')` |
| 2 (register / control) | Parent-child handshake messages | `childRegistrationUtils` via `BaseNodeAdapter` |
The 4-segment key shape **`...`** is the contractual output of `MeasurementContainer.getFlattenedOutput()`. Position labels are normalised to lowercase. Changing this shape is a forbidden breaking change — see [Reference — Limitations](Reference-Limitations#stability--versioning).
---
## Capability matrix
| Capability | Status | Notes |
|:---|:---|:---|
| Base domain scaffolding (`BaseDomain`) | ✅ | Constructor, emitter, logger, measurements, child registry wired automatically |
| Base Node-RED adapter (`BaseNodeAdapter`) | ✅ | Tick/event loop, status badge, input dispatch, Port 0/1/2 output |
| Declarative command dispatch (`CommandRegistry`) | ✅ | Alias deprecation warnings, unit normalisation, `query.units` auto-topic |
| Declarative child-registration routing (`ChildRouter`) | ✅ | Replaces per-node `registerChild` switch blocks |
| Unit policy + conversion (`UnitPolicy`, `convert`) | ✅ | Canonical ↔ output ↔ curve unit sets; dual method/property access |
| Measurement store (`MeasurementContainer`) | ✅ | Chainable, windowed, auto-convert, 4-segment key output |
| InfluxDB + process output formatting (`outputUtils`) | ✅ | Delta-compressed; consumers must cache and merge |
| Status badge helpers (`statusBadge`, `StatusUpdater`) | ✅ | Converged look-and-feel across all nodes |
| Latest-wins async gate (`LatestWinsGate`) | ✅ | Extracted from MGC; shared by PS, VGC, MGC |
| Prediction quality / drift tracking (`HealthStatus`) | ✅ | Frozen plain-object shape; composable |
| Config schema registry (`configManager`) | ✅ | One JSON schema per node in `src/configs/` |
| PID control (`PIDController`, `CascadePIDController`) | ✅ | Full-featured discrete PID with bumpless transfer |
| Curve interpolation (`interpolation`, `predict`) | ✅ | Multidimensional characteristic-curve predictor |
| Statistical helpers (`stats`, `nrmse`, `outliers`) | ✅ | Mean, stddev, median, NRMSE, dynamic-cluster outlier detection |
| Thermodynamic properties (`coolprop`) | ✅ | CoolProp bindings for fluid/gas property lookup |
| FSM for valve/machine states (`state`) | ✅ | StateManager + MovementManager |
| Gravity calculations (`gravity`) | ✅ | WGS-84 model |
| Physical constants (`Fysics`) | ✅ | Air density, viscosity, etc. |
| Browser-side editor dropdowns (`MenuManager`, `menuUtils`) | ✅ | Node-RED editor form population |
| Asset metadata registry (`assetResolver`) | ✅ | Replaces `loadCurve`, `AssetCategoryManager`, ad-hoc JSON readers |
---
## Need more?
| Page | What you'll find |
|:---|:---|
| [Reference — Contracts](Reference-Contracts) | Full public API surface table — one row per export, with source file, stability tag, and signature |
| [Reference — Architecture](Reference-Architecture) | Three-tier rules, `src/` directory tree, how 12 nodes consume the library, additive-only export discipline |
| [Reference — Examples](Reference-Examples) | Usage patterns: extending `BaseDomain` and `BaseNodeAdapter`, registering commands, declaring child routes, `MeasurementContainer` chaining |
| [Reference — Limitations](Reference-Limitations) | Known issues (deprecated `loadCurve`, `outlierDetection` logs to console, `configUtils` silent strip, …) and stability/versioning rules |
[EVOLV master wiki](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Home) · [Platform CONTRACTS.md](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/.claude/refactor/CONTRACTS.md) · [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)