Source-tree mirror of EVOLV.wiki.git refactor (27a42ee on wiki.git): - 7 master pages rewritten with clean design (Home, Architecture, Topology-Patterns, Topic-Conventions, Telemetry, Getting-Started, Glossary). Tables and Mermaid for visuals, gitea alert callouts for warnings, shields badges for metadata only. No emoji as decoration. - Archive.md becomes a removal-changelog pointing readers to git history and to the successor pages. - _Sidebar.md updated to navigate the new flat-name layout. - Concept / finding / manual pages: uniform mini-header (badges + "reference page" callout) added without rewriting domain content. - Every internal link now uses the flat naming that resolves on the live gitea wiki (Concept-ASM-Models, Finding-BEP-..., etc.). On wiki.git: 29 Archive-* pages hard-deleted (the git history preserves them; Archive.md documents the removal). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
266 lines
9.2 KiB
Markdown
266 lines
9.2 KiB
Markdown
# Telemetry
|
|
|
|

|
|

|
|
|
|
> [!NOTE]
|
|
> Every node sends on three output ports: Port 0 (process data), Port 1 (InfluxDB line protocol), Port 2 (registration / control plumbing). All output is formatted by `outputUtils.formatMsg` with delta compression: only changed fields are sent each tick. InfluxDB cardinality discipline: tags = identity (low cardinality), fields = numbers. Source of truth: `.claude/refactor/CONTRACTS.md` §10.
|
|
|
|
---
|
|
|
|
## Three-port model
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
sc["specificClass — 'output-changed' or tick()"]
|
|
ou["outputUtils.formatMsg — delta-compress"]
|
|
p0[("Port 0 — process")]
|
|
p1[("Port 1 — InfluxDB line")]
|
|
p2[("Port 2 — register / control")]
|
|
dl["Downstream Node-RED — dashboards, functions"]
|
|
influx[("InfluxDB")]
|
|
parent["Parent EVOLV node"]
|
|
|
|
sc -- getOutput() --> ou
|
|
ou --> p0 --> dl
|
|
ou --> p1 --> influx
|
|
sc -. child.register .-> p2 --> parent
|
|
|
|
class sc tier3
|
|
class ou tier2
|
|
class p0 p0c
|
|
class p1 p1c
|
|
class p2 p2c
|
|
class dl,parent dn
|
|
class influx ext
|
|
|
|
classDef tier3 fill:#50a8d9,color:#000
|
|
classDef tier2 fill:#86bbdd,color:#000
|
|
classDef p0c fill:#0c99d9,color:#fff
|
|
classDef p1c fill:#50a8d9,color:#000
|
|
classDef p2c fill:#a9daee,color:#000
|
|
classDef dn fill:#dddddd,color:#000
|
|
classDef ext fill:#fff2cc,color:#000
|
|
```
|
|
|
|
### Port summary
|
|
|
|
| Port | Carries | Format | Configured by | Trigger |
|
|
|:---|:---|:---|:---|:---|
|
|
| 0 (process) | Snapshot of changed measurement / state keys | JSON delta object | `outputUtils.formatMsg(..., 'process')` via `config.output.process` | `'output-changed'` on the emitter, or each tick |
|
|
| 1 (telemetry) | Numeric fields only — time-series | InfluxDB line protocol | `outputUtils.formatMsg(..., 'influxdb')` via `config.output.dbase` | Same trigger as Port 0 |
|
|
| 2 (register) | `child.register` plus internal control msgs | Plain object | `BaseNodeAdapter` init | Once 100ms after init; on demand |
|
|
|
|
---
|
|
|
|
## Port 0 — Process data (delta-compressed)
|
|
|
|
Purpose: feed downstream Node-RED logic such as dashboards, alarms, control function nodes.
|
|
|
|
Shape: `msg.payload` contains only keys that changed since the last tick. Consumers must cache and merge.
|
|
|
|
> [!IMPORTANT]
|
|
> Why delta compression: at 1 Hz with 50+ fields per node, full snapshots flood downstream nodes and dashboards. A typical delta carries 0–5 fields per tick.
|
|
|
|
Example output, one rotatingMachine tick:
|
|
|
|
```json
|
|
{
|
|
"topic": "rotatingMachine#pump-A",
|
|
"payload": {
|
|
"flow.predicted.downstream.default": 12.4,
|
|
"predictionConfidence": 0.87
|
|
}
|
|
}
|
|
```
|
|
|
|
Most other fields (state, pressure, mode, ...) didn't change this tick and are omitted.
|
|
|
|
Trigger: `outputUtils.checkForChanges()` compares the current `getOutput()` against the previous snapshot. No change means no send.
|
|
|
|
---
|
|
|
|
## Port 1 — InfluxDB line protocol
|
|
|
|
Purpose: time-series storage for trending, regulatory reporting, ML training.
|
|
|
|
Shape: `msg.payload` is a string (or array of strings) in InfluxDB line protocol:
|
|
|
|
```
|
|
<measurement>,<tag-set> <field-set> <timestamp-ns>
|
|
```
|
|
|
|
Example, rotatingMachine telemetry:
|
|
|
|
```
|
|
rotatingMachine,id=pump-A,softwareType=rotatingMachine flow_predicted_downstream=12.4,power_measured_atequipment=18.2 1714752000000000000
|
|
```
|
|
|
|
### Conventions
|
|
|
|
| Element | Rule |
|
|
|:---|:---|
|
|
| Measurement (table name) | The node's `softwareType`, lowercase |
|
|
| Tag set | Low-cardinality identity: `id`, `softwareType`, location-style tags. Never raw measurement values |
|
|
| Field set | Numeric values only. Keys flatten `<type>_<variant>_<position>` (underscore, not dot — InfluxDB constraint) |
|
|
| Timestamp | Nanoseconds. Set by `outputUtils` from the node's clock |
|
|
|
|
See [InfluxDB Schema Design](Concept-InfluxDB-Schema-Design) for cardinality discipline.
|
|
|
|
---
|
|
|
|
## Port 2 — Registration / control
|
|
|
|
Purpose: upward `child.register` at startup; later, internal control msgs.
|
|
|
|
Shape:
|
|
|
|
```json
|
|
{
|
|
"topic": "child.register",
|
|
"payload": {
|
|
"ref": "<node reference>",
|
|
"softwareType": "machine",
|
|
"config": { }
|
|
}
|
|
}
|
|
```
|
|
|
|
Trigger: the nodeClass adapter emits `child.register` on init if a parent is configured. Parent's `commandRegistry` dispatches into `ChildRouter.onRegister(...)`.
|
|
|
|
The legacy alias `registerChild` is still accepted; it logs a one-time deprecation warning on first use.
|
|
|
|
---
|
|
|
|
## The output composition pipeline
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
autonumber
|
|
participant tick as Tick (1 Hz) or event source
|
|
participant sc as specificClass
|
|
participant mc as MeasurementContainer
|
|
participant ou as outputUtils
|
|
participant ports as Ports 0 / 1
|
|
|
|
tick->>sc: tick() OR emit('output-changed')
|
|
sc->>sc: concern modules update mc + state
|
|
sc->>ou: getOutput() snapshot
|
|
ou->>ou: diff vs last snapshot
|
|
alt no change
|
|
ou-->>sc: skip
|
|
else change
|
|
ou->>ports: Port 0 — JSON delta
|
|
ou->>ports: Port 1 — line protocol
|
|
end
|
|
```
|
|
|
|
> [!CAUTION]
|
|
> Never write directly to `node.send` from specificClass. Go through `outputUtils.formatMsg`. Direct sends bypass delta compression and flood downstream nodes.
|
|
|
|
---
|
|
|
|
## InfluxDB schema layout (recommended)
|
|
|
|
| InfluxDB element | Maps to |
|
|
|:---|:---|
|
|
| Database / bucket | One per plant, or per environment: `evolv_dev`, `evolv_prod` |
|
|
| Measurement (table) | Node `softwareType` |
|
|
| Tags | `id`, `softwareType`, `area`, `processCell`, `unit` (for hierarchical drill-down) |
|
|
| Fields | Numeric series — every numeric key from `getOutput()`, flattened with `_` |
|
|
| Retention — hot | 7 days at 1 s |
|
|
| Retention — cold | 1 year at 1 min downsample |
|
|
|
|
> [!WARNING]
|
|
> Cardinality discipline. Keep tag sets stable. Do not put `state` (string) as a tag — emit it as a field with a code (`state_code=2`). High-cardinality tags fragment InfluxDB's index and degrade query performance dramatically.
|
|
|
|
See [InfluxDB Schema Design](Concept-InfluxDB-Schema-Design) for full guidance.
|
|
|
|
---
|
|
|
|
## FlowFuse dashboard wiring
|
|
|
|
Port 0 is the natural source for FlowFuse `ui-chart` widgets — the delta-compressed JSON maps cleanly to `msg.topic` (series label) plus `msg.payload` (y-value).
|
|
|
|
Layout rule (from `.claude/rules/node-red-flow-layout.md` §4):
|
|
|
|
- One chart per metric type (one for flow, one for power, one for level).
|
|
- A trend-feeder function splits Port-0 deltas into per-series outputs.
|
|
- Each output wires to one chart. The chart's `category: "topic"` and `categoryType: "msg"` plot one series per unique `msg.topic`.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
p0[("Port 0")]
|
|
split["trend-feeder — function (N outputs)"]
|
|
chart1["ui-chart: flow"]
|
|
chart2["ui-chart: power"]
|
|
|
|
p0 --> split
|
|
split --> chart1
|
|
split --> chart2
|
|
|
|
class p0 p0c
|
|
class split tier2
|
|
class chart1,chart2 neutral
|
|
|
|
classDef p0c fill:#0c99d9,color:#fff
|
|
classDef tier2 fill:#86bbdd,color:#000
|
|
classDef neutral fill:#dddddd
|
|
```
|
|
|
|
See [FlowFuse ui-chart manual](Manual-NodeRED-Flowfuse-Ui-Chart-Manual) for the required chart properties.
|
|
|
|
---
|
|
|
|
## Grafana dashboard provisioning
|
|
|
|
`dashboardAPI` consumes registrations and emits Grafana dashboard JSON via HTTP.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
evolv["EVOLV node (any softwareType)"]
|
|
dash[dashboardAPI]
|
|
grafana[("Grafana HTTP API — POST /api/dashboards/db")]
|
|
|
|
evolv -- child.register --> dash
|
|
dash -- composed JSON --> grafana
|
|
|
|
class evolv tier3
|
|
class dash util
|
|
class grafana ext
|
|
|
|
classDef tier3 fill:#50a8d9,color:#000
|
|
classDef util fill:#dddddd
|
|
classDef ext fill:#fff2cc,color:#000
|
|
```
|
|
|
|
dashboardAPI looks up a template per softwareType (in `nodes/dashboardAPI/src/config/templates/`), substitutes the node's id + tags, and POSTs an upsert. Bearer-token auth is supported via `config.grafanaConnector.bearerToken`.
|
|
|
|
---
|
|
|
|
## Debug recipes
|
|
|
|
| Symptom | First thing to check |
|
|
|:---|:---|
|
|
| InfluxDB rows missing for a node | Port 1 wired to an `influxdb out` node? Tap Port 1 with a debug node to verify line-protocol output |
|
|
| Dashboard widgets stuck on `n/a` | Port 0 reaching the trend-feeder? Many widgets need `msg.topic` set for series labelling |
|
|
| `child.register` not arriving | Tap Port 2 with debug. Confirm parent's `commandRegistry` accepts `child.register` (or `registerChild` alias) |
|
|
| Too many InfluxDB writes | Likely a tick-driven debug node bypassed the delta filter. Confirm `outputUtils.checkForChanges()` is firing |
|
|
| Grafana dashboard not created on boot | Inspect dashboardAPI's HTTP response. Check bearer token + base URL in its config |
|
|
| High cardinality alarm in InfluxDB | A string value is being written as a tag (probably `state` or similar). Move it to a field |
|
|
|
|
> [!CAUTION]
|
|
> Never ship `enableLog: 'debug'` in a demo. Fills the container log within seconds and obscures real errors. Use only for live debugging.
|
|
|
|
---
|
|
|
|
## Related pages
|
|
|
|
| Page | Why |
|
|
|:---|:---|
|
|
| [Architecture](Architecture) | Output port wiring in the three-tier model |
|
|
| [Topic Conventions](Topic-Conventions) | What topics map to what fields |
|
|
| [Topology Patterns](Topology-Patterns) | Typical telemetry flows |
|
|
| [InfluxDB Schema Design](Concept-InfluxDB-Schema-Design) | Cardinality discipline |
|
|
| [FlowFuse ui-chart manual](Manual-NodeRED-Flowfuse-Ui-Chart-Manual) | Required chart properties |
|