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>
267 lines
13 KiB
Markdown
267 lines
13 KiB
Markdown
# 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<br/>(shared library)"]:::lib
|
|
|
|
rm["rotatingMachine<br/>Equipment"]:::equip
|
|
mgc["machineGroupControl<br/>Unit"]:::unit
|
|
ps["pumpingStation<br/>Process Cell"]:::proc
|
|
meas["measurement<br/>Control Module"]:::ctrl
|
|
valve["valve<br/>Equipment"]:::equip
|
|
vgc["valveGroupControl<br/>Unit"]:::unit
|
|
reactor["reactor<br/>Unit"]:::unit
|
|
settler["settler<br/>Unit"]:::unit
|
|
monster["monster<br/>Unit"]:::unit
|
|
diffuser["diffuser<br/>Equipment"]:::equip
|
|
dashAPI["dashboardAPI<br/>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 `<type>.<variant>.<position>.<childId>`. |
|
|
| `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 **`<type>.<variant>.<position>.<childId>`** 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)
|