Waterschap Brabantse Delta · R&D-lab · uitleg

EVOLV · dashboardAPI — van child.register tot Grafana-dashboard

eigenaar R&D-lab laatst getest 2026-07-17 versie 2.0 concept
dashboardAPI is de utility-node die Grafana-dashboards automatisch genereert uit de geregistreerde parent-child-boom van een flow. Hij luistert naar child.register-handshakes, loopt de subtree recursief af, componeert per node een dashboard uit JSON-templates en emit per dashboard één HTTP-upsert-bericht (POST /api/dashboards/db) voor een downstream http request-node. De node wijkt bewust af van het gangbare EVOLV-patroon: geen BaseNodeAdapter, geen tick-loop, geen telemetrie — een passieve "one-shot HTTP emitter". Deze pagina volgt letterlijk wat de code doet aan de hand van één child.register en een paar interactieve figuren die de compositie getrouw naspelen.

Doel & rol in de keten

dashboardAPI is een utility-node zonder S88-niveau (CLAUDE.md:16-19; palette-kleur slate #7A8BA3, "infrastructuur"; flow-layout-rol neutral). Zijn configrol is 'auto ui generator' (src/nodeClass.js:71). Waar de proces-nodes (pumpingStation, machineGroupControl, rotatingMachine, measurement, …) hun telemetrie via outputUtils naar InfluxDB schrijven, genereert dashboardAPI de Grafana-dashboards die exact díe series queryen. De koppeling loopt via de measurement-naamconventie van outputUtils.formatMsg: het dashboard vult zijn measurement-var met general.name || "<softwareType>_<id>" (src/specificClass.js:552-556), anders queryt elk paneel een niet-bestaande serie. Operators wiren alléén subtree-roots naar de node; de rest van de boom wordt ontdekt via de kindregistraties. De node stáát dus naast de procesketen, niet erin: hij registreert zichzelf nergens als kind (CONTRACT.md:63-65).

Rode draad door de pagina: één binnenkomende child.register van een pumpingStation-root met daaronder een machineGroupControl en twee rotatingMachine-pompen. Alle uid's, layout-getallen en Flux-fragmenten in dit artifact zijn met node tegen de echte dashboardAPI-code doorgerekend (src/specificClass.js); testgevallen staan als commentaar in de pagina-JS. Illustratieve aannames zijn expliciet zo gelabeld.

Praat met deze node

Het I/O-contract in één oogopslag: kies een commando, stel de velden in, kopieer het exacte injecteerbare msg, en klik Injecteer ▸ om te zien wat er uit de ene uitgangspoort komt — met een live doorgerekende SHA-1-uid (dezelfde crypto.createHash('sha1') als de node) en de gevormde POST-envelope. dashboardAPI heeft 1 input en 1 uitgangspoort (label grafana = Port 0); Port 1/2 worden bewust niet gebruikt. Elke bewering draagt een pad:regel-chip.

Stuurinput · 1 poort
Bericht
Poortoutput · 1 poort (grafana)
Volledige poort-referentie
Port-0-bericht per gegenereerd dashboard, gevormd voor een http request-node (src/commands/handlers.js:74-93); het inbound-bericht wordt gespread zodat correlatievelden meereizen. Port 1 (Influx-telemetrie) en Port 2 (child.register) zijn bewust ongebruikt (CONTRACT.md:61-65). Degraded-conventie: ontbrekende keys zijn afwezig, nooit null (test/_output-manifest.md:27).
KeyWaardeBron
topicliteral 'create' handlers.js:76
url <protocol>://<host>:<port>/api/dashboards/db specificClass.js:382-385
method'POST' handlers.js:78
headers Accept/Content-Type: application/json; Authorization: Bearer <token> alléén als bearerToken gezet — anders afwezig, nooit lege string handlers.js:49-51
payload { dashboard, overwrite: true, folderUid? }; folderUid alleen indien geresolvet/geconfigureerd, folderId (number) alleen als expliciete fallback voor oudere Grafana's specificClass.js:586-593, handlers.js:80-84
meta { nodeId, softwareType, uid, title, trigger }; trigger'child.register' | 'manual' handlers.js:85-91

Wie praat met wie

dashboardAPI is een fan-in-knooppunt: elk kind in de boom stuurt na een deploy zijn child.register (poort 2 van het kind → input van dashboardAPI, via draad). Alléén de wortels worden gewired; de rest van de boom ontdekt de node via childRegistrationUtils.registeredChildren. Per node komt er één dashboard uit, en alles gaat via één POST naar Grafana — verstuurd door een aparte http request-node.

pumpingStation subtree-root (gewired) machineGroupControl via registratie ontdekt rotatingMachine ×2 via registratie ontdekt measurement … bladeren dashboardAPI deze node · utility (geen S88) http request method: use Grafana /api/dashboards/db child.register (fan-in, via draad) boom-walk + compose poort 0 · POST-envelope HTTP POST
child.register · fan-in via draad (kind-poort 2) POST-envelope · poort 0 → http request → Grafana boom-walk · in-process (registeredChildren)

De reis van één child.register naar een dashboard

Klik een stap in de lijst (of gebruik ◀ ▶). Boven zie je waar in de pijplijn je bent; rechts wat er gebeurt, het rode-draadvoorbeeld op dat punt (met node tegen de echte code doorgerekend), en het codepad.

Dashboard-layout, interactief

Twee tastbare kanten van het compose-algoritme. machineGroupControl toont de Grafana-paneelgrid (24 kolommen): schuif het aantal pomp-kinderen en zet de emittedFields-dedup aan/uit — het raster herschikt live volgens de echte injectie- en dedup-passes. pumpingStation toont het tankvisual met de vijf drempellijnen die door MIN_LINE_GAP = 28 px uit elkaar worden geduwd. Puur SVG; alle getallen zijn tegen src/specificClass.js geverifieerd.

2

Compositie-regels

Dedup op emittedFields
Een parent-paneel wiens meta.emittedFields volledig gedekt is door de panels van zijn directe kinderen wordt verwijderd — zo staat dezelfde serie niet twee keer op parent én kind. row-panels nooit; panels zonder declaratie (leeg/afwezig) blijven altijd. Geen kinderen ⇒ no-op (src/specificClass.js:700-722, 373-380).
Tankvisual-layout
Alleen voor pumpingStation: 5 drempellijnen in een 400×760-frame, gesorteerd en door MIN_LINE_GAP = 28 px naar beneden geduwd; overshoot voorbij de bodem schuift de hele stapel omhoog. Bewuste vervorming — de tank toont ordening/zone-structuur, niet schaal; labels dragen de echte getallen (src/specificClass.js:262-279).
Flux-regex
Per-pomp series liggen in elk pomp-eigen measurement; de setpoint-serie matcht met regex ^ctrl\.predicted\.atequipment\. omdat de childId per pomp varieert (geen vaste .default). Flow/power idem (^flow\.predicted\.…, ^power\.predicted\.…). De ctrl-serie zelf is exact-match (src/specificClass.js:966-967,1016-1019).
Idempotente POST via SHA-1
stableUid = eerste 12 hex van SHA-1("<softwareType>:<nodeId>") → deterministische Grafana-uid. De upsert POST /api/dashboards/db met overwrite:true is daardoor idempotent: elke deploy overschrijft byte-identiek hetzelfde dashboard i.p.v. een duplicaat te maken (src/specificClass.js:7-10,566; handlers.js:74-84).

Architectuur

De node volgt het EVOLV-lagenpatroon met één bewuste afwijking: er is géén BaseNodeAdapter/BaseDomain en géén tick-loop (CONTRACT.md:3-8, src/nodeClass.js:3-9). Output verschijnt dus nooit op een interval, maar uitsluitend event-driven: na een deploy (child.register van kinderen) of op operator-commando. Eén levensloop-haak die geen andere EVOLV-node heeft: RED.events.on('flows:started') cachet per deploy de diff + timestamp (src/nodeClass.js:37-51).

LaagBestandInhoud
Node-RED wrapperdashboardAPI.js registerType('dashboardapi', …) met credentials-blok voor bearerToken (r.9-16) en twee admin-endpoints voor menu- en editor-config (r.20-44).
Adaptersrc/nodeClass.js Config-opbouw (_buildConfig, r.54-83), instantiatie van DashboardApi, command-dispatch (r.85-101) en de flows:started-hook (r.35-52).
Domeinlogicasrc/specificClass.js Class DashboardApi (1093 regels): templates, uids, graph-walk, dedup, links, pomp-fan-out, overview-graph, folder/datasource-resolvers. Geen RED.*.
Commandssrc/commands/index.js + handlers.js Registry-descriptors en handlers (registerChild, regenerateDashboard).
Lagenindeling van nodes/dashboardAPI. Anders dan de repo-conventie is er géén generalFunctions/src/configs/dashboardapi.json: editor-metadata komt uit dependencies/dashboardapi/dashboardapiConfig.json, de runtime-config wordt inline gebouwd (src/nodeClass.js:68-82, rationale CONTRACT.md:91-95). Het bestand heet dashboardAPI.{js,html}, maar het type-id blijft lowercase 'dashboardapi' zodat gedeployde flows blijven laden (CLAUDE.md:48).

Configuratie

VeldDefaultBetekenis
name'' Node-label; wordt general.name (fallback 'dashboardapi').
protocol / host / port http / localhost / 3000 Opbouw van de Grafana-URL (src/nodeClass.js:74); port met parseInt-fallback 3000.
bearerToken Credential (password): encrypted-at-rest in flow_cred.json (dashboardAPI.js:13-15). Legacy plain-config-token wordt nog gelezen met eenmalige deprecation-warning "re-save to migrate" (src/nodeClass.js:57-67).
folderTitle'' Grafana-folder op naam; uid wordt at-emit geresolvet en de folder aangemaakt indien afwezig — duurzaam over Grafana-rebuilds heen (specificClass.js:142-148,413-433).
folderUid'' Expliciete uid; fallback wanneer folderTitle leeg is of resolutie faalt.
defaultBucket'' Influx-bucket voor de dashboard-var bucket; fallback-keten: uiConfig → env INFLUXDB_BUCKET (src/nodeClass.js:81) → bucketMap[position] → positie-default (upstream→'lvl1', downstream→'lvl3', anders 'lvl2').
enableLog / logLevel true / 'info' Logger aan/uit en niveau (debug|info|warn|error).
Belangrijkste editor-velden (dashboardAPI.html:8-22, verwerking src/nodeClass.js:54-83). grafanaConnector.infinityDatasourceUid is een code-only override voor de overview-datasource (specificClass.js:76-80,827) — geen editor-veld gezien.

Integraties

Aandachtspunten uit de code-analyse

Alle punten zijn code-observaties d.d. 2026-07-17 op de genoemde revisie; niets hiervan is in runtime geverifieerd.

Verder lezen

Bron statische code-analyse van de werkkopie RnD/EVOLV (super-repo 74c4089, 2026-07-03) — submodule dashboardAPI @ 8fb909752b33, plus het feitendossier van deze analyse. Analysedatum 2026-07-17.
Versheids-herverificatie op 2026-07-17 is git fetch gedraaid; de geanalyseerde SHA 8fb909752b33 is nog steeds ancestor van origin/main (tip e0dcfc494a5a) — de beschrijving in dit artifact dekt dus de actuele main.
Methode statische code-analyse (geen runtime-verificatie); elk feit draagt een pad:regel-verwijzing. De console, de stappen-rail en figuur F1 zijn getrouwe herimplementaties, vooraf met node tegen de echte src/specificClass.js doorgerekend (SHA-1-uid, tankvisual-layout, Flux-regex en dedup — testgevallen als commentaar in de pagina-JS); illustratieve aannames zijn expliciet zo gelabeld.
Beperking beschrijft uitsluitend de geanalyseerde revisie; gedrag op andere branches of deployments kan afwijken.
Contact R&D-lab · lab.wbd-rd.nl