# generalFunctions

![code-ref](https://img.shields.io/badge/code--ref-48fa543-blue) ![kind](https://img.shields.io/badge/kind-Shared_Library-dddddd) ![status](https://img.shields.io/badge/status-stable-brightgreen)

**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 &mdash; they only write the logic that differs.

---

## At a glance

| Thing | Value |
|:---|:---|
| What it is | The shared library &mdash; 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 &mdash; `const { BaseDomain, UnitPolicy } = require('generalFunctions');` |
| Side effects on a flow | None &mdash; the library has no editor form, no node registration |
| Cross-node coupling | Through this library's API surface + Node-RED messages only &mdash; 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 &mdash; 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 &mdash; Contracts](Reference-Contracts).

---

## Module map &mdash; 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 &mdash; 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 &mdash; 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 &mdash; see [Reference &mdash; 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 &harr; output &harr; 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 &mdash; Contracts](Reference-Contracts) | Full public API surface table &mdash; one row per export, with source file, stability tag, and signature |
| [Reference &mdash; Architecture](Reference-Architecture) | Three-tier rules, `src/` directory tree, how 12 nodes consume the library, additive-only export discipline |
| [Reference &mdash; Examples](Reference-Examples) | Usage patterns: extending `BaseDomain` and `BaseNodeAdapter`, registering commands, declaring child routes, `MeasurementContainer` chaining |
| [Reference &mdash; 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) &middot; [Platform CONTRACTS.md](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/development/.claude/refactor/CONTRACTS.md) &middot; [Topic Conventions](https://gitea.wbd-rd.nl/RnD/EVOLV/wiki/Topic-Conventions)
