Migrate to new Gitea instance (gitea.wbd-rd.nl)
- Update all submodule URLs from gitea.centraal.wbd-rd.nl to gitea.wbd-rd.nl - Add settler as proper submodule in .gitmodules - Add agent skills, function anchors, decisions, and improvements - Add Docker configuration and scripts - Add manuals and third_party docs - Update .gitignore with secrets and build artifacts - Remove stale .tgz build artifact Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
36
.agents/function-anchors/README.md
Normal file
36
.agents/function-anchors/README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# Function Anchors
|
||||
|
||||
This folder stores class-level anchor documents that define EVOLV logic truth for long-term maintainability.
|
||||
|
||||
## Standard
|
||||
1. Start each anchor with a **Connection Map (At a Glance)**.
|
||||
2. Then provide a **Unit Table** as the first data section.
|
||||
3. Cover the class end-to-end: config, I/O contracts, mode/state logic, full function inventory, calculations, safeguards, tests, invariants, and known gaps.
|
||||
4. Keep references tied to file/line evidence.
|
||||
|
||||
## Mandatory Architecture Rule
|
||||
All EVOLV node anchors must use the same folder and artifact structure as `rotatingMachine`.
|
||||
|
||||
Required per node:
|
||||
- `.agents/function-anchors/<nodeName>/ANCHOR-<nodeName>.md`
|
||||
- `.agents/function-anchors/<nodeName>/ANCHOR-<nodeName>.html`
|
||||
- `.agents/function-anchors/<nodeName>/EVIDENCE-<nodeName>-tests.md`
|
||||
- `nodes/<nodeName>/test/basic/*.test.js`
|
||||
- `nodes/<nodeName>/test/integration/*.test.js`
|
||||
- `nodes/<nodeName>/test/edge/*.test.js`
|
||||
|
||||
Enforcement policy:
|
||||
- Do not ship behavioral changes in `nodes/<nodeName>/` without updating the matching anchor and evidence files.
|
||||
- New EVOLV nodes must be created with this structure from day one.
|
||||
- Existing nodes missing this structure are considered incomplete and must be brought to parity.
|
||||
|
||||
## Files
|
||||
- `TEMPLATE.md`: reusable format for all future anchor points.
|
||||
- `rotatingMachine/ANCHOR-rotatingMachine.md`: current rotatingMachine anchor.
|
||||
- `rotatingMachine/EVIDENCE-rotatingMachine-tests.md`: test-evidence companion.
|
||||
- `pumpingStation/ANCHOR-pumpingStation.md`: pumpingStation anchor preparation baseline.
|
||||
- `pumpingStation/ANCHOR-pumpingStation.html`: pumpingStation visual topology anchor baseline.
|
||||
- `pumpingStation/EVIDENCE-pumpingStation-tests.md`: pumpingStation test plan/evidence baseline.
|
||||
- `monster/ANCHOR-monster.md`: monster node anchor baseline with API/report integration context.
|
||||
- `monster/ANCHOR-monster.html`: monster visual topology anchor baseline.
|
||||
- `monster/EVIDENCE-monster-tests.md`: monster test evidence baseline.
|
||||
94
.agents/function-anchors/TEMPLATE.md
Normal file
94
.agents/function-anchors/TEMPLATE.md
Normal file
@@ -0,0 +1,94 @@
|
||||
# Function Anchor Template
|
||||
|
||||
Use this template to document any EVOLV class as a stable "logic truth" anchor.
|
||||
|
||||
## Mandatory File Layout (Required For Every Node)
|
||||
- `.agents/function-anchors/<nodeName>/ANCHOR-<nodeName>.md`
|
||||
- `.agents/function-anchors/<nodeName>/ANCHOR-<nodeName>.html`
|
||||
- `.agents/function-anchors/<nodeName>/EVIDENCE-<nodeName>-tests.md`
|
||||
- `nodes/<nodeName>/test/basic/*.test.js`
|
||||
- `nodes/<nodeName>/test/integration/*.test.js`
|
||||
- `nodes/<nodeName>/test/edge/*.test.js`
|
||||
|
||||
Any deviation from this layout must be treated as technical debt and resolved before closing the work item.
|
||||
|
||||
## 1) Connection Map (At a Glance)
|
||||
- **Node type**:
|
||||
- **Consumes from EVOLV nodes/topics**:
|
||||
- **Publishes to EVOLV nodes/topics**:
|
||||
- **Registers as child to**:
|
||||
- **Accepts child registration from**:
|
||||
- **Admin/UI endpoints**:
|
||||
|
||||
## 2) Unit Table (Always First Data Section)
|
||||
| Signal/Field | Represents | Asset Type | Default Unit | Accepted Units | Source of Truth (file:line) | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
|
||||
## 3) Class Identity
|
||||
- **Class**:
|
||||
- **Primary files**:
|
||||
- **Runtime responsibility**:
|
||||
- **Editor responsibility**:
|
||||
|
||||
## 4) Configuration Contract
|
||||
| UI Field | Runtime Path | Default | Validation/Coercion | Behavior Impact | Source |
|
||||
|---|---|---|---|---|---|
|
||||
|
||||
## 5) Input/Output Contract
|
||||
### Input topics
|
||||
| Topic | Payload schema | Handler | Side effects | Source |
|
||||
|---|---|---|---|---|
|
||||
|
||||
### Output ports
|
||||
| Port | Message type | Producer method | Typical consumers | Source |
|
||||
|---|---|---|---|---|
|
||||
|
||||
### Admin endpoints
|
||||
| Endpoint | Method | Purpose | Source |
|
||||
|---|---|---|---|
|
||||
|
||||
## 6) Mode, State, and Control Model
|
||||
- **Modes**:
|
||||
- **Allowed actions by mode**:
|
||||
- **Allowed sources by mode**:
|
||||
- **Operational states for prediction**:
|
||||
- **Sequence definitions**:
|
||||
|
||||
## 7) End-to-End Execution Flow
|
||||
1. Constructor and initialization flow.
|
||||
2. Registration and child wiring flow.
|
||||
3. Input routing flow.
|
||||
4. Tick/output emission flow.
|
||||
5. Status update flow.
|
||||
|
||||
## 8) Full Function Inventory
|
||||
| Function | Purpose | Reads | Writes | Calls | Emits/Returns | Failure/Fallback | Source | Covered by tests |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
|
||||
## 9) Calculations and Physical Semantics
|
||||
- **Prediction paths** (flow, power, control).
|
||||
- **Pressure selection order**.
|
||||
- **Efficiency, CoG, and BEP distance calculations**.
|
||||
- **Assumptions and plausibility constraints**.
|
||||
|
||||
## 10) Error Handling and Safeguards
|
||||
- Validation guards.
|
||||
- Warning/error paths.
|
||||
- Availability-first behavior.
|
||||
|
||||
## 11) Test Evidence Matrix
|
||||
| Test file | What is covered | Methods/contracts anchored |
|
||||
|---|---|---|
|
||||
|
||||
## 12) Invariants (Anchor Truth)
|
||||
- Non-negotiable behaviors this class must preserve.
|
||||
|
||||
## 13) Known Gaps / Risks
|
||||
- Mismatches, TODOs, or technical debt observed in current implementation.
|
||||
|
||||
## 14) Change Checklist
|
||||
- Required updates when logic changes:
|
||||
- Code sections
|
||||
- Contract docs
|
||||
- Tests
|
||||
- Example flows
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>dashboardAPI Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>dashboardAPI Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
29
.agents/function-anchors/dashboardAPI/ANCHOR-dashboardAPI.md
Normal file
29
.agents/function-anchors/dashboardAPI/ANCHOR-dashboardAPI.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# dashboardAPI Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: dashboardAPI
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/dashboardAPI/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/dashboardAPI
|
||||
- Node-RED wrapper: nodes/dashboardAPI/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/dashboardAPI/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/dashboardAPI/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
@@ -0,0 +1,15 @@
|
||||
# dashboardAPI Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/dashboardAPI/test/basic/*.test.js
|
||||
- nodes/dashboardAPI/test/integration/*.test.js
|
||||
- nodes/dashboardAPI/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/dashboardAPI/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/dashboardAPI/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/dashboardAPI/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
16
.agents/function-anchors/diffuser/ANCHOR-diffuser.html
Normal file
16
.agents/function-anchors/diffuser/ANCHOR-diffuser.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>diffuser Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>diffuser Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
29
.agents/function-anchors/diffuser/ANCHOR-diffuser.md
Normal file
29
.agents/function-anchors/diffuser/ANCHOR-diffuser.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# diffuser Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: diffuser
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/diffuser/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/diffuser
|
||||
- Node-RED wrapper: nodes/diffuser/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/diffuser/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/diffuser/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
15
.agents/function-anchors/diffuser/EVIDENCE-diffuser-tests.md
Normal file
15
.agents/function-anchors/diffuser/EVIDENCE-diffuser-tests.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# diffuser Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/diffuser/test/basic/*.test.js
|
||||
- nodes/diffuser/test/integration/*.test.js
|
||||
- nodes/diffuser/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/diffuser/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/diffuser/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/diffuser/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>machineGroupControl Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>machineGroupControl Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
# machineGroupControl Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: machineGroupControl
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/machineGroupControl/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/machineGroupControl
|
||||
- Node-RED wrapper: nodes/machineGroupControl/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/machineGroupControl/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/machineGroupControl/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
@@ -0,0 +1,15 @@
|
||||
# machineGroupControl Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/machineGroupControl/test/basic/*.test.js
|
||||
- nodes/machineGroupControl/test/integration/*.test.js
|
||||
- nodes/machineGroupControl/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/machineGroupControl/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/machineGroupControl/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/machineGroupControl/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
45
.agents/function-anchors/measurement/ANCHOR-measurement.html
Normal file
45
.agents/function-anchors/measurement/ANCHOR-measurement.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Measurement Anchor Topology</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; color: #1f2937; background: #f7f8fa; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; margin-bottom: 12px; }
|
||||
.topic { font-family: monospace; color: #0f52a5; }
|
||||
ul { margin: 8px 0 0 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Measurement Function Anchor</h1>
|
||||
<div class="card">
|
||||
<h2>Topology</h2>
|
||||
<ul>
|
||||
<li><strong>Node:</strong> <code>measurement</code></li>
|
||||
<li><strong>Runtime:</strong> <code>nodes/measurement/src/nodeClass.js</code></li>
|
||||
<li><strong>Domain:</strong> <code>nodes/measurement/src/specificClass.js</code></li>
|
||||
<li><strong>Admin endpoints:</strong> <code>/measurement/menu.js</code>, <code>/measurement/configData.js</code>, <code>/measurement/asset-reg</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Input Topics</h2>
|
||||
<ul>
|
||||
<li><span class="topic">measurement</span> -> set input value when payload is numeric</li>
|
||||
<li><span class="topic">simulator</span> -> toggle simulation mode</li>
|
||||
<li><span class="topic">outlierDetection</span> -> toggle outlier mode flag</li>
|
||||
<li><span class="topic">calibrate</span> -> run calibration logic</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h2>Output Ports</h2>
|
||||
<ul>
|
||||
<li>Port 0: process message</li>
|
||||
<li>Port 1: influx message</li>
|
||||
<li>Port 2: parent registration (<code>registerChild</code>)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
53
.agents/function-anchors/measurement/ANCHOR-measurement.md
Normal file
53
.agents/function-anchors/measurement/ANCHOR-measurement.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Measurement Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- **Node type**: `measurement` (`nodes/measurement/measurement.js:1`, `nodes/measurement/measurement.html:14`)
|
||||
- **Consumes topics**: `measurement`, `simulator`, `outlierDetection`, `calibrate` (`nodes/measurement/src/nodeClass.js:147`)
|
||||
- **Publishes periodic outputs**:
|
||||
- Output `0`: process payload (`nodes/measurement/src/nodeClass.js:137`)
|
||||
- Output `1`: influx payload (`nodes/measurement/src/nodeClass.js:138`)
|
||||
- Output `2`: parent registration (`registerChild`) (`nodes/measurement/src/nodeClass.js:118`)
|
||||
- **Cross-node integrations (direct observed)**:
|
||||
- Registers as child to parent with `positionVsParent` and optional `distance` (`nodes/measurement/src/nodeClass.js:118`)
|
||||
- Emits measurement updates through `MeasurementContainer` in `specificClass` (`nodes/measurement/src/specificClass.js:479`)
|
||||
- **Admin/UI endpoints**:
|
||||
- `GET /measurement/menu.js` (`nodes/measurement/measurement.js:23`)
|
||||
- `GET /measurement/configData.js` (`nodes/measurement/measurement.js:33`)
|
||||
- `POST /measurement/asset-reg` (`nodes/measurement/measurement.js:43`)
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `inputValue` | raw measurement input | asset-dependent | `nodes/measurement/src/specificClass.js:30` | `measurement` topic or simulator | scaling/smoothing pipeline | defaults to `0` |
|
||||
| `outputAbs` (`mAbs`) | processed absolute output | `config.asset.unit` | `nodes/measurement/src/specificClass.js:472` | `updateOutputAbs()` | process/influx outputs + event emitter | constrained to configured abs range |
|
||||
| `outputPercent` (`mPercent`) | normalized percent-like output | `%` semantic | `nodes/measurement/src/specificClass.js:483` | `updateOutputPercent()` | process/influx outputs | interpolated from abs range or observed min/max |
|
||||
| `storedValues` | smoothing window values | same as processed value | `nodes/measurement/src/specificClass.js:24` | `applySmoothing()` | smoothing and repeatability checks | capped to `smoothWindow` length |
|
||||
| `simulation.enabled` | internal simulated signal mode | boolean | `nodes/measurement/src/specificClass.js:52` | config/topic toggle | `tick()` behavior | off by default |
|
||||
|
||||
## 2) Class Identity
|
||||
- **Runtime registration + endpoints**: `nodes/measurement/measurement.js`
|
||||
- **Node-RED wrapper/routing**: `nodes/measurement/src/nodeClass.js`
|
||||
- **Domain measurement logic**: `nodes/measurement/src/specificClass.js`
|
||||
- **Editor UI/defaults**: `nodes/measurement/measurement.html`
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
1. Node label precedence can hide fallback text due to expression order:
|
||||
- `return this.positionIcon + " " + this.assetType || "Measurement";` (`nodes/measurement/measurement.html:63`)
|
||||
2. `success` variable is assigned without declaration in editor save path:
|
||||
- `success = window.EVOLV.nodes.measurement.assetMenu.saveEditor(this);` (`nodes/measurement/measurement.html:131`)
|
||||
3. `toggleOutlierDetection()` mutates config object to boolean:
|
||||
- `this.config.outlierDetection = !this.config.outlierDetection;` (`nodes/measurement/src/specificClass.js:503`)
|
||||
4. Input handler ignores numeric strings for `measurement` topic:
|
||||
- accepts only `typeof msg.payload === 'number'` (`nodes/measurement/src/nodeClass.js:152`)
|
||||
|
||||
## 4) Standardization Plan (Mirror RotatingMachine)
|
||||
1. Keep this anchor pair (`.md` + `.html`) and evidence file maintained with behavior changes.
|
||||
2. Maintain test layout under `nodes/measurement/test/`:
|
||||
- `basic/`, `integration/`, `edge/`, `helpers/`
|
||||
3. Maintain examples package under `nodes/measurement/examples/`:
|
||||
- `README.md`, `basic.flow.json`, `integration.flow.json`, `edge.flow.json`
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Required anchor artifacts exist and map to current behavior.
|
||||
- Test suite runs with node-level command.
|
||||
- Example flow files exist and pass flow-structure tests.
|
||||
@@ -0,0 +1,32 @@
|
||||
# Measurement Test Evidence
|
||||
|
||||
Status: baseline suite created and executed.
|
||||
|
||||
## Required Test Layout
|
||||
- `nodes/measurement/test/basic/*.test.js`
|
||||
- `nodes/measurement/test/integration/*.test.js`
|
||||
- `nodes/measurement/test/edge/*.test.js`
|
||||
- `nodes/measurement/test/helpers/*.js`
|
||||
|
||||
## Test-to-Contract Mapping
|
||||
| Test file | Scope | Primary contracts anchored |
|
||||
|---|---|---|
|
||||
| `nodes/measurement/test/basic/specific-constructor.basic.test.js` | constructor baseline and range derivation | `Measurement` constructor |
|
||||
| `nodes/measurement/test/basic/scaling-and-output.basic.test.js` | scaling constraint and output update path | `calculateInput`, `updateOutputAbs`, `getOutput` |
|
||||
| `nodes/measurement/test/basic/nodeclass-routing.basic.test.js` | topic routing and registration output shape | `nodeClass._attachInputHandler`, `_registerChild` |
|
||||
| `nodes/measurement/test/integration/examples-flows.integration.test.js` | example package integrity and expected topic drivers | `nodes/measurement/examples/*.flow.json` |
|
||||
| `nodes/measurement/test/integration/measurement-event.integration.test.js` | measurement container event emission contract | `updateOutputAbs`, measurement emitter wiring |
|
||||
| `nodes/measurement/test/edge/invalid-payload.edge.test.js` | non-numeric input payload ignored behavior | `nodeClass._attachInputHandler` measurement branch |
|
||||
| `nodes/measurement/test/edge/outlier-toggle.edge.test.js` | current outlier toggle behavior capture | `toggleOutlierDetection` |
|
||||
|
||||
## Executed
|
||||
- Command:
|
||||
- `cd nodes/measurement && npm test`
|
||||
- Result:
|
||||
- `pass: baseline suite` (see latest run in session)
|
||||
- Date:
|
||||
- February 19, 2026
|
||||
|
||||
## Known Gaps Captured by Tests
|
||||
- Outlier toggle currently converts config object to boolean.
|
||||
- Measurement topic currently ignores numeric strings.
|
||||
74
.agents/function-anchors/monster/ANCHOR-monster.html
Normal file
74
.agents/function-anchors/monster/ANCHOR-monster.html
Normal file
@@ -0,0 +1,74 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Monster Anchor Map</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f5f7fb;
|
||||
--panel: #ffffff;
|
||||
--line: #9ab0d9;
|
||||
--text: #14233d;
|
||||
--muted: #4d6084;
|
||||
--monster: #4f8582;
|
||||
--sensor: #eef8e8;
|
||||
--api: #fff5e5;
|
||||
--ops: #e9f1ff;
|
||||
}
|
||||
body { margin: 0; font-family: "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }
|
||||
.wrap { max-width: 1100px; margin: 24px auto; padding: 0 16px 24px; }
|
||||
.panel { background: var(--panel); border: 1px solid #dde5f5; border-radius: 12px; padding: 16px; }
|
||||
h1 { margin: 0 0 8px; font-size: 24px; }
|
||||
p { margin: 0 0 12px; color: var(--muted); }
|
||||
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.chip { border: 1px solid #d1ddf5; border-radius: 999px; padding: 4px 10px; background: #f7faff; font-size: 12px; }
|
||||
svg { width: 100%; height: auto; border-radius: 10px; background: #f9fbff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="panel">
|
||||
<h1>Monster Function Anchor</h1>
|
||||
<p>External APIs are orchestrated by surrounding flows; the `monster` node computes sampling state and report fields.</p>
|
||||
<div class="chips">
|
||||
<span class="chip">input: input_q / i_start / monsternametijden / rain_data</span>
|
||||
<span class="chip">output: pulse, m3Total, m3PerPuls, bucketVol, running</span>
|
||||
<span class="chip">report path: Z-Info import + operator m3/pulse reference</span>
|
||||
</div>
|
||||
<svg viewBox="0 0 980 380" role="img" aria-label="Monster integration topology">
|
||||
<defs>
|
||||
<marker id="arr" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#6f85aa"></path>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect x="390" y="150" width="190" height="80" rx="10" fill="var(--monster)"></rect>
|
||||
<text x="485" y="192" text-anchor="middle" fill="#fff" font-size="16">monster</text>
|
||||
|
||||
<rect x="40" y="45" width="220" height="56" rx="9" fill="var(--sensor)" stroke="#b7d89e"></rect>
|
||||
<text x="150" y="79" text-anchor="middle" fill="#2e5a22" font-size="13">PLC/measurement flow input</text>
|
||||
|
||||
<rect x="40" y="290" width="220" height="56" rx="9" fill="var(--api)" stroke="#e9c589"></rect>
|
||||
<text x="150" y="324" text-anchor="middle" fill="#634319" font-size="13">Open-Meteo + Aquon schedule</text>
|
||||
|
||||
<rect x="720" y="45" width="220" height="56" rx="9" fill="var(--ops)" stroke="#aac0ef"></rect>
|
||||
<text x="830" y="79" text-anchor="middle" fill="#244271" font-size="13">Dashboard / Influx / Grafana</text>
|
||||
|
||||
<rect x="720" y="290" width="220" height="56" rx="9" fill="#e7faf5" stroke="#9edcca"></rect>
|
||||
<text x="830" y="324" text-anchor="middle" fill="#1e5244" font-size="13">PLC pulse + Z-Info report tooling</text>
|
||||
|
||||
<line x1="260" y1="73" x2="390" y2="160" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="260" y1="318" x2="390" y2="220" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="580" y1="160" x2="720" y2="75" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="580" y1="220" x2="720" y2="318" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
|
||||
<text x="294" y="125" fill="var(--muted)" font-size="12">input_q / i_start / registerChild</text>
|
||||
<text x="285" y="277" fill="var(--muted)" font-size="12">rain_data / monsternametijden</text>
|
||||
<text x="610" y="124" fill="var(--muted)" font-size="12">process + influx streams</text>
|
||||
<text x="607" y="278" fill="var(--muted)" font-size="12">pulse + m3Total + m3PerPuls</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
85
.agents/function-anchors/monster/ANCHOR-monster.md
Normal file
85
.agents/function-anchors/monster/ANCHOR-monster.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Monster Function Anchor (Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- **Node type**: `monster` (`nodes/monster/monster.js:1`, `nodes/monster/monster.html:5`)
|
||||
- **Consumes control/input topics**: `input_q`, `i_start`, `monsternametijden`, `rain_data`, `registerChild` (`nodes/monster/src/nodeClass.js:202`)
|
||||
- **Publishes periodic outputs**:
|
||||
- Output `0`: process payload (`nodes/monster/src/nodeClass.js:185`)
|
||||
- Output `1`: influx payload (`nodes/monster/src/nodeClass.js:186`)
|
||||
- Output `2`: parent registration (`registerChild`) (`nodes/monster/src/nodeClass.js:158`)
|
||||
- **Cross-node integrations**:
|
||||
- Accepts measurement children of type `flow` (`nodes/monster/src/specificClass.js:300`)
|
||||
- Common external orchestration pattern around this node:
|
||||
- Open-Meteo -> `rain_data`
|
||||
- Aquon schedule feed -> `monsternametijden`
|
||||
- PLC/MQTT pulse sink fed by `output.pulse`
|
||||
- Z-Info/report tooling fed by `m3Total` + `m3PerPuls`
|
||||
- Dashboard API/Grafana and Influx consumers
|
||||
- **Admin/UI endpoints**:
|
||||
- `GET /monster/menu.js`
|
||||
- `GET /monster/configData.js` (`nodes/monster/monster.js:17`, `nodes/monster/monster.js:27`)
|
||||
|
||||
## 1) Unit Table (Always First Data Section)
|
||||
| Signal/Field | Represents | Asset Type | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
| `input_q.payload.value` | influent flow command | manual/control input | `m3/h` (normalized in wrapper) | `nodes/monster/src/nodeClass.js:216` | upstream control flow | sampling calculation loop | invalid/unit conversion failure is warned and ignored |
|
||||
| `flow.measured.*` | measured flow from child sensors | measurement child | `m3/h` | `nodes/monster/src/specificClass.js:300` | measurement nodes | effective flow selection | if missing, manual flow or 0 is used |
|
||||
| `q` | effective flow used by model | derived | `m3/h` | `nodes/monster/src/specificClass.js:775` | `tick()` | pulse and volume progression | defaults to `0` if no measured/manual flow |
|
||||
| `m3PerPuls` | reporting conversion factor for sampler pulse | derived/report field | `m3/pulse` | `nodes/monster/src/specificClass.js:660` | `sampling_program()` | Z-Info/report tooling, operations | `0` when not running |
|
||||
| `m3Total` | accumulated volume during active run | derived/report field | `m3` | `nodes/monster/src/specificClass.js:687` | `sampling_program()` | Z-Info/report tooling | reset to `0` when sampling window ends |
|
||||
| `pulse` | pulse command signal | control output | boolean | `nodes/monster/src/specificClass.js:707` | `sampling_program()` | PLC/MQTT pulse output paths | forced `false` under cooldown/capacity/end-of-run |
|
||||
| `bucketVol` | sampled bucket fill volume | derived/state | `L` | `nodes/monster/src/specificClass.js:712` | pulse accumulation | dashboard/operator checks | reset to `0` after run |
|
||||
| `predictedRateM3h` | rain-scaled prediction reference | derived | `m3/h` | `nodes/monster/src/specificClass.js:367` | `getOutput()` | dashboards/diagnostics | falls back to measured/manual effective rate |
|
||||
|
||||
## 2) Class Identity
|
||||
- **Runtime registration + endpoints**: `nodes/monster/monster.js`
|
||||
- **Node-RED wrapper/routing**: `nodes/monster/src/nodeClass.js`
|
||||
- **Domain sampling logic**: `nodes/monster/src/specificClass.js`
|
||||
- **Editor UI/defaults**: `nodes/monster/monster.html`
|
||||
- **Default config schema**: `nodes/generalFunctions/src/configs/monster.json`
|
||||
|
||||
## 3) Configuration Contract (Key)
|
||||
| UI Field | Runtime Path | Default | Behavior Impact | Source |
|
||||
|---|---|---|---|---|
|
||||
| `samplingtime` | `constraints.samplingtime` | `0` | sampling window hours | `nodes/monster/monster.html:16`, `nodes/monster/src/nodeClass.js:68` |
|
||||
| `minvolume` | `constraints.minVolume` | `5` | min valid sample volume | `nodes/monster/monster.html:17`, `nodes/monster/src/nodeClass.js:69` |
|
||||
| `maxweight` | `constraints.maxWeight` | `22` | max bucket load before invalid sample | `nodes/monster/monster.html:18`, `nodes/monster/src/nodeClass.js:70` |
|
||||
| `nominalFlowMin` / `flowMax` | `constraints.nominalFlowMin` / `constraints.flowMax` | `0` / `0` | prediction bounds and start guard | `nodes/monster/monster.html:19`, `nodes/monster/src/specificClass.js:226` |
|
||||
| `minSampleIntervalSec` | `constraints.minSampleIntervalSec` | `60` | pulse cooldown protection | `nodes/monster/monster.html:22`, `nodes/monster/src/specificClass.js:693` |
|
||||
| `emptyWeightBucket` | `asset.emptyWeightBucket` | `3` | max bucket volume derivation | `nodes/monster/monster.html:23`, `nodes/monster/src/specificClass.js:378` |
|
||||
| `aquon_sample_name` | `aquonSampleName` | `"112100"` internal default | schedule selector key | `nodes/monster/monster.html:24`, `nodes/monster/src/nodeClass.js:96` |
|
||||
|
||||
## 4) I/O and Integration Notes
|
||||
- Node-level output is process/influx/parent only.
|
||||
- External APIs are normally handled by surrounding flows, not by the node class itself.
|
||||
- Report tooling integration should read from process payload fields:
|
||||
- `m3Total`
|
||||
- `m3PerPuls`
|
||||
- `running`
|
||||
- `pulse`
|
||||
- Reference examples:
|
||||
- dashboard baseline: `nodes/monster/examples/monster-dashboard.flow.json`
|
||||
- full API + dashboard template: `nodes/monster/examples/monster-api-dashboard.flow.json`
|
||||
|
||||
## 5) Current Gaps / Risks
|
||||
1. Wrapper exposes topics (`setMode`, `execSequence`, `execMovement`, `flowMovement`, `emergencystop`) that are not implemented in `Monster.handleInput` contract.
|
||||
2. `showWorkingCurves`/`CoG` routes in wrapper call methods that are not present in `Monster`.
|
||||
3. Existing legacy tests were not organized in required `basic/integration/edge` folders before this update.
|
||||
4. External API credentials/tokens must remain outside committed example flows.
|
||||
|
||||
## 6) Test Evidence Matrix (Current Baseline)
|
||||
| Test file | What is covered | Methods/contracts anchored |
|
||||
|---|---|---|
|
||||
| `nodes/monster/test/basic/constructor.basic.test.js` | constructor + output field contract | `constructor`, `set_boundries_and_targets`, `getOutput` |
|
||||
| `nodes/monster/test/integration/flow-and-schedule.integration.test.js` | flow averaging, rain/schedule ingestion | `registerChild`, `handleInput`, `tick`, `updateRainData`, `regNextDate` |
|
||||
| `nodes/monster/test/edge/sampling-guards.edge.test.js` | invalid-bound guard + cooldown behavior | `validateFlowBounds`, `sampling_program`, cooldown gate |
|
||||
|
||||
## 7) Change Checklist
|
||||
- When `monster` behavior changes, update:
|
||||
- `nodes/monster/src/nodeClass.js`
|
||||
- `nodes/monster/src/specificClass.js`
|
||||
- `nodes/monster/monster.html`
|
||||
- `nodes/monster/test/basic|integration|edge/*`
|
||||
- `.agents/function-anchors/monster/ANCHOR-monster.md`
|
||||
- `.agents/function-anchors/monster/ANCHOR-monster.html`
|
||||
- `.agents/function-anchors/monster/EVIDENCE-monster-tests.md`
|
||||
30
.agents/function-anchors/monster/EVIDENCE-monster-tests.md
Normal file
30
.agents/function-anchors/monster/EVIDENCE-monster-tests.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Monster Test Evidence
|
||||
|
||||
## Executed Baseline
|
||||
- Command:
|
||||
- `node --test test/basic/*.test.js test/integration/*.test.js test/edge/*.test.js`
|
||||
- Working directory:
|
||||
- `nodes/monster`
|
||||
- Result:
|
||||
- `pass: 6`, `fail: 0`
|
||||
|
||||
## Test Matrix
|
||||
| Test file | Scope | Contracts anchored |
|
||||
|---|---|---|
|
||||
| `nodes/monster/test/basic/constructor.basic.test.js` | initialization and output field contract | constructor, boundary setup, report output fields |
|
||||
| `nodes/monster/test/basic/structure-module-load.basic.test.js` | required structure/module load guard | baseline architecture compliance |
|
||||
| `nodes/monster/test/integration/flow-and-schedule.integration.test.js` | flow aggregation + rain/schedule ingestion | measured/manual flow merge, `handleInput`, schedule update path |
|
||||
| `nodes/monster/test/integration/structure-examples.integration.test.js` | required examples contract guard | example flow presence/shape |
|
||||
| `nodes/monster/test/edge/sampling-guards.edge.test.js` | sampling safety guards | invalid flow bounds guard, cooldown pulse throttling |
|
||||
| `nodes/monster/test/edge/structure-examples-node-type.edge.test.js` | node-type structure guard | example includes `monster` node usage |
|
||||
|
||||
## Coverage Notes
|
||||
- Structure guards now require both dashboard examples:
|
||||
- `nodes/monster/examples/monster-dashboard.flow.json`
|
||||
- `nodes/monster/examples/monster-api-dashboard.flow.json`
|
||||
- Focused on the most operationally critical report fields:
|
||||
- `m3Total`
|
||||
- `m3PerPuls`
|
||||
- `pulse`
|
||||
- `running`
|
||||
- Additional contract tests are still recommended for wrapper topic routes that currently map to unsupported domain handlers.
|
||||
@@ -0,0 +1,75 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PumpingStation Anchor Map</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f5f7fb;
|
||||
--panel: #ffffff;
|
||||
--line: #9ab0d9;
|
||||
--text: #14233d;
|
||||
--muted: #4d6084;
|
||||
--pump: #0c99d9;
|
||||
--child: #e7faf5;
|
||||
--sensor: #eef8e8;
|
||||
--dash: #fff5e5;
|
||||
}
|
||||
body { margin: 0; font-family: "Segoe UI", sans-serif; background: var(--bg); color: var(--text); }
|
||||
.wrap { max-width: 1100px; margin: 24px auto; padding: 0 16px 24px; }
|
||||
.panel { background: var(--panel); border: 1px solid #dde5f5; border-radius: 12px; padding: 16px; }
|
||||
h1 { margin: 0 0 8px; font-size: 24px; }
|
||||
p { margin: 0 0 12px; color: var(--muted); }
|
||||
.chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
|
||||
.chip { border: 1px solid #d1ddf5; border-radius: 999px; padding: 4px 10px; background: #f7faff; font-size: 12px; }
|
||||
svg { width: 100%; height: auto; border-radius: 10px; background: #f9fbff; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap">
|
||||
<div class="panel">
|
||||
<h1>PumpingStation Function Anchor</h1>
|
||||
<p>Preparation baseline map. Keep this topology in sync with `ANCHOR-pumpingStation.md` and runtime contracts.</p>
|
||||
<div class="chips">
|
||||
<span class="chip">input: registerChild / calibrate* / q_in / changemode</span>
|
||||
<span class="chip">output[0]: process</span>
|
||||
<span class="chip">output[1]: influx</span>
|
||||
<span class="chip">output[2]: registerChild</span>
|
||||
</div>
|
||||
<svg viewBox="0 0 900 360" role="img" aria-label="PumpingStation integration map">
|
||||
<defs>
|
||||
<marker id="arr" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#6f85aa"></path>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<rect x="360" y="140" width="180" height="72" rx="10" fill="var(--pump)"></rect>
|
||||
<text x="450" y="182" text-anchor="middle" fill="#fff" font-size="16">pumpingStation</text>
|
||||
|
||||
<rect x="40" y="40" width="210" height="56" rx="9" fill="var(--child)" stroke="#9edcca"></rect>
|
||||
<text x="145" y="74" text-anchor="middle" fill="#1e5244" font-size="13">machine / machineGroupControl</text>
|
||||
|
||||
<rect x="40" y="250" width="210" height="56" rx="9" fill="var(--sensor)" stroke="#b7d89e"></rect>
|
||||
<text x="145" y="284" text-anchor="middle" fill="#2e5a22" font-size="13">measurement (level/flow/pressure)</text>
|
||||
|
||||
<rect x="650" y="40" width="210" height="56" rx="9" fill="var(--dash)" stroke="#e9c589"></rect>
|
||||
<text x="755" y="74" text-anchor="middle" fill="#634319" font-size="13">dashboard / manual control</text>
|
||||
|
||||
<rect x="650" y="250" width="210" height="56" rx="9" fill="#e9f1ff" stroke="#aac0ef"></rect>
|
||||
<text x="755" y="284" text-anchor="middle" fill="#244271" font-size="13">parent process / orchestrator</text>
|
||||
|
||||
<line x1="250" y1="68" x2="360" y2="152" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="250" y1="278" x2="360" y2="198" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="650" y1="68" x2="540" y2="150" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
<line x1="540" y1="202" x2="650" y2="278" stroke="var(--line)" stroke-width="2" marker-end="url(#arr)"></line>
|
||||
|
||||
<text x="268" y="128" fill="var(--muted)" font-size="12">flow.predicted.* / control handoff</text>
|
||||
<text x="260" y="240" fill="var(--muted)" font-size="12">*.measured.<position></text>
|
||||
<text x="566" y="128" fill="var(--muted)" font-size="12">q_in / calibrate / mode</text>
|
||||
<text x="560" y="240" fill="var(--muted)" font-size="12">registerChild + process/influx consumers</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,69 @@
|
||||
# Pumping Station Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- **Node type**: `pumpingStation` (`nodes/pumpingStation/pumpingStation.js:1`, `nodes/pumpingStation/pumpingStation.html:15`)
|
||||
- **Consumes parent/control topics**: `changemode`, `registerChild`, `calibratePredictedVolume`, `calibratePredictedLevel`, `q_in` (`nodes/pumpingStation/src/nodeClass.js:209`)
|
||||
- **Publishes periodic outputs**:
|
||||
- Output `0`: process payload (`nodes/pumpingStation/src/nodeClass.js:197`)
|
||||
- Output `1`: influx payload (`nodes/pumpingStation/src/nodeClass.js:198`)
|
||||
- Output `2`: parent registration/control plumbing (`registerChild`) (`nodes/pumpingStation/src/nodeClass.js:114`)
|
||||
- **Cross-node integrations (direct observed)**:
|
||||
- Registers `measurement` children and listens for `*.measured.<position>` events (`nodes/pumpingStation/src/specificClass.js:73`)
|
||||
- Registers `machine`, `machinegroup`, `pumpingstation` children and listens for predicted flow (`nodes/pumpingStation/src/specificClass.js:59`)
|
||||
- Commands child machines/stations/groups during control/safety transitions (`nodes/pumpingStation/src/specificClass.js:258`, `nodes/pumpingStation/src/specificClass.js:528`)
|
||||
- **Admin/UI endpoints**:
|
||||
- `GET /pumpingStation/menu.js`
|
||||
- `GET /pumpingStation/configData.js` (`nodes/pumpingStation/pumpingStation.js:22`, `nodes/pumpingStation/pumpingStation.js:33`)
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| `flow.measured.*` / `flow.predicted.*` | inflow/outflow streams | `m3/s` preferred | `nodes/pumpingStation/src/specificClass.js:24` | measurement/machine/machinegroup children | net-flow selection + predicted volume integration | falls back to level-rate estimate when unavailable (`nodes/pumpingStation/src/specificClass.js:458`) |
|
||||
| `level.measured.*` / `level.predicted.*` | wet well level | `m` | `nodes/pumpingStation/src/specificClass.js:24` | measurement or pressure conversion path | control decisions + remaining-time estimate | if no level available, remaining time becomes null (`nodes/pumpingStation/src/specificClass.js:487`) |
|
||||
| `volume.predicted.atequipment` | integrated basin volume | `m3` | `nodes/pumpingStation/src/specificClass.js:393` | tick-based integration | safety + status + output | if volume unreadable, station shuts down machines availability-first (`nodes/pumpingStation/src/specificClass.js:503`) |
|
||||
| `volumePercent.*.atequipment` | normalized fill percentage | `%` | `nodes/pumpingStation/src/specificClass.js:424` | level/volume conversion | status + dashboards | not emitted until level/volume is known |
|
||||
| `netFlowRate.*.atequipment` | selected net flow | measured unit or `m3/s` | `nodes/pumpingStation/src/specificClass.js:454` | `_selectBestNetFlow()` | status + remaining-time + safety | defaults to `0` with `steady` direction (`nodes/pumpingStation/src/specificClass.js:466`) |
|
||||
| `timeleft` | estimated seconds to empty/full limit | `s` | `nodes/pumpingStation/src/specificClass.js:470` | `_computeRemainingTime()` | safety logic + output | null if insufficient data |
|
||||
|
||||
## 2) Class Identity
|
||||
- **Runtime registration + endpoints**: `nodes/pumpingStation/pumpingStation.js`
|
||||
- **Node-RED wrapper/routing**: `nodes/pumpingStation/src/nodeClass.js`
|
||||
- **Domain/station logic**: `nodes/pumpingStation/src/specificClass.js`
|
||||
- **Editor UI/defaults**: `nodes/pumpingStation/pumpingStation.html`
|
||||
- **Default config schema/validation rules**: `nodes/generalFunctions/src/configs/pumpingStation.json`
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
1. Topic/mode mismatch:
|
||||
- UI default uses `controlMode: "none"` (`nodes/pumpingStation/pumpingStation.html:59`)
|
||||
- runtime switch expects `manual` not `none` (`nodes/pumpingStation/src/specificClass.js:234`)
|
||||
2. Position token mismatch risk:
|
||||
- code mixes `atEquipment` and `atequipment` variants (`nodes/pumpingStation/src/nodeClass.js:122`, `nodes/pumpingStation/src/specificClass.js:103`)
|
||||
3. Child softwareType mismatch risk:
|
||||
- checks for `'pumpingstation'`/`'machinegroup'` lowercase (`nodes/pumpingStation/src/specificClass.js:61`, `nodes/pumpingStation/src/specificClass.js:63`)
|
||||
- other configs generally use camelCase (`nodes/generalFunctions/src/configs/pumpingStation.json:48`)
|
||||
4. Missing guards in input registration path:
|
||||
- no null check after `RED.nodes.getNode` (`nodes/pumpingStation/src/nodeClass.js:217`)
|
||||
5. Test baseline exists but is not yet full parity:
|
||||
- basic/edge/integration scaffolding is present; additional safety/control math coverage is still pending.
|
||||
|
||||
## 4) Standardization Plan (Mirror RotatingMachine)
|
||||
1. Create `ANCHOR-pumpingStation.html` with:
|
||||
- always-visible topology map
|
||||
- unit/signal catalog table
|
||||
- control and safety flow diagram
|
||||
- known invariants and risk list
|
||||
2. Expand the current unit/integration/edge test suite under `nodes/pumpingStation/test/`:
|
||||
- config defaults/overrides
|
||||
- topic routing and child registration
|
||||
- predicted volume integration and remaining-time math
|
||||
- safety triggers and control actions
|
||||
- regression for string casing mismatches and missing child node IDs
|
||||
3. Add evidence companion doc:
|
||||
- `EVIDENCE-pumpingStation-tests.md` with fail-before/pass-after references.
|
||||
4. Keep this anchor and tests updated on every pumpingStation behavior change.
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor markdown complete to template parity with rotatingMachine.
|
||||
- Anchor HTML visualization added and aligned with actual contracts.
|
||||
- Test suite runnable with `node --test nodes/pumpingStation/test/**/*.test.js`.
|
||||
- Evidence file links each test file to anchored behavior.
|
||||
@@ -0,0 +1,34 @@
|
||||
# PumpingStation Test Evidence (Preparation)
|
||||
|
||||
Status: baseline suite created and executed.
|
||||
|
||||
## Executed
|
||||
- Command:
|
||||
- `node --test test/basic/*.test.js test/edge/*.test.js test/integration/*.test.js`
|
||||
- Working directory:
|
||||
- `nodes/pumpingStation`
|
||||
- Result:
|
||||
- `pass: 4`, `fail: 0`
|
||||
|
||||
## Planned Test Matrix
|
||||
| Planned test file | Scope | Primary contracts anchored |
|
||||
|---|---|---|
|
||||
| `nodes/pumpingStation/test/basic/constructor.basic.test.js` | config initialization, basin property derivation | constructor, `initBasinProperties`, config defaults |
|
||||
| `nodes/pumpingStation/test/basic/nodeClass-routing.basic.test.js` | topic routing and registration handling | `nodeClass._attachInputHandler`, `registerChild`, calibration topics, `q_in` parsing |
|
||||
| `nodes/pumpingStation/test/integration/registration-normalization.integration.test.js` | softwareType/position normalization and listener dedupe | `registerChild`, `_registerPredictedFlowChild`, `_registerMeasurementChild` |
|
||||
| `nodes/pumpingStation/test/edge/mode-alias.edge.test.js` | mode alias normalization | `_normalizeMode`, `changeMode` compatibility path |
|
||||
| `nodes/pumpingStation/test/integration/flow-balance.integration.test.js` | inflow/outflow aggregation and predicted volume update | `_updatePredictedVolume`, `_selectBestNetFlow`, `_computeRemainingTime` |
|
||||
| `nodes/pumpingStation/test/integration/measurement.integration.test.js` | level/pressure measurement handling and conversions | `_onLevelMeasurement`, `_onPressureMeasurement` |
|
||||
| `nodes/pumpingStation/test/integration/safety.integration.test.js` | dry-run/overfill/time threshold behavior | `_safetyController` |
|
||||
| `nodes/pumpingStation/test/integration/control-levelbased.integration.test.js` | level-based machine command dispatch behavior | `_controlLevelBased`, `_applyMachineLevelControl` |
|
||||
| `nodes/pumpingStation/test/edge/status.edge.test.js` | status output formatting under sparse data | `_updateNodeStatus` |
|
||||
|
||||
## Execution Target
|
||||
- Preferred command (after suite exists): `node --test nodes/pumpingStation/test/**/*.test.js`
|
||||
|
||||
## Coverage Goal
|
||||
- Match rotatingMachine discipline:
|
||||
- config contract coverage
|
||||
- topic routing coverage
|
||||
- control/safety path coverage
|
||||
- regression cases for known risk patterns
|
||||
16
.agents/function-anchors/reactor/ANCHOR-reactor.html
Normal file
16
.agents/function-anchors/reactor/ANCHOR-reactor.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>reactor Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>reactor Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
29
.agents/function-anchors/reactor/ANCHOR-reactor.md
Normal file
29
.agents/function-anchors/reactor/ANCHOR-reactor.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# reactor Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: reactor
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/reactor/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/reactor
|
||||
- Node-RED wrapper: nodes/reactor/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/reactor/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/reactor/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
15
.agents/function-anchors/reactor/EVIDENCE-reactor-tests.md
Normal file
15
.agents/function-anchors/reactor/EVIDENCE-reactor-tests.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# reactor Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/reactor/test/basic/*.test.js
|
||||
- nodes/reactor/test/integration/*.test.js
|
||||
- nodes/reactor/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/reactor/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/reactor/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/reactor/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
@@ -0,0 +1,810 @@
|
||||
<!doctype html>
|
||||
<html lang="en" data-theme="light">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>EVOLV RotatingMachine Anchor</title>
|
||||
<style>
|
||||
:root {
|
||||
--bg: #f3f6fb;
|
||||
--bg-grad: radial-gradient(circle at 0% 0%, #e8efff 0%, #f3f6fb 42%);
|
||||
--panel: #ffffff;
|
||||
--text: #172435;
|
||||
--muted: #5c6a7c;
|
||||
--line: #d7e0ee;
|
||||
--line-strong: #a9bad7;
|
||||
--blue: #2059d8;
|
||||
--teal: #0d9f9e;
|
||||
--green: #0fa57d;
|
||||
--amber: #cf8a11;
|
||||
--red: #d63f50;
|
||||
--chip: #f7faff;
|
||||
--chip-border: #d4deef;
|
||||
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
||||
--sans: "Segoe UI", "Avenir Next", "Helvetica Neue", Arial, sans-serif;
|
||||
--shadow: 0 8px 24px rgba(14, 25, 42, 0.08);
|
||||
--radius: 14px;
|
||||
}
|
||||
|
||||
html[data-theme="dark"] {
|
||||
--bg: #0d1420;
|
||||
--bg-grad: radial-gradient(circle at 0% 0%, #1a2538 0%, #0d1420 44%);
|
||||
--panel: #131c2a;
|
||||
--text: #e6edf9;
|
||||
--muted: #9eb0cb;
|
||||
--line: #26344a;
|
||||
--line-strong: #355079;
|
||||
--blue: #4b86ff;
|
||||
--teal: #25c9c4;
|
||||
--green: #24c794;
|
||||
--amber: #e9ad42;
|
||||
--red: #ff6f7b;
|
||||
--chip: #172234;
|
||||
--chip-border: #324968;
|
||||
--shadow: 0 8px 24px rgba(0, 0, 0, 0.34);
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--bg-grad), var(--bg);
|
||||
color: var(--text);
|
||||
font-family: var(--sans);
|
||||
line-height: 1.42;
|
||||
}
|
||||
|
||||
.wrap { max-width: 1240px; margin: 0 auto; padding: 18px; }
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border: 1px solid var(--line-strong);
|
||||
background: var(--panel);
|
||||
color: var(--text);
|
||||
padding: 7px 11px;
|
||||
border-radius: 9px;
|
||||
font-size: 0.82rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.btn:hover { border-color: var(--blue); }
|
||||
.btn.active { background: var(--blue); color: #fff; border-color: var(--blue); }
|
||||
|
||||
.hero {
|
||||
background: linear-gradient(128deg, #1e47ac 0%, #1f5ad6 48%, #1a8eb9 100%);
|
||||
color: #fff;
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow);
|
||||
padding: 20px;
|
||||
display: grid;
|
||||
grid-template-columns: 1.4fr 1fr;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.hero h1 { margin: 0 0 7px; font-size: 1.58rem; letter-spacing: 0.2px; }
|
||||
.hero p { margin: 0; opacity: 0.94; font-size: 0.93rem; }
|
||||
|
||||
.badge-row { margin-top: 12px; display: flex; flex-wrap: wrap; gap: 7px; }
|
||||
.badge {
|
||||
padding: 4px 9px;
|
||||
border: 1px solid rgba(255,255,255,0.3);
|
||||
background: rgba(255,255,255,0.12);
|
||||
border-radius: 999px;
|
||||
font-size: 0.77rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.metric-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0,1fr));
|
||||
gap: 8px;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.metric {
|
||||
border: 1px solid rgba(255,255,255,0.28);
|
||||
background: rgba(255,255,255,0.13);
|
||||
border-radius: 10px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.metric .k { font-size: 0.7rem; text-transform: uppercase; opacity: 0.86; letter-spacing: 0.45px; }
|
||||
.metric .v { font-size: 1rem; font-weight: 700; margin-top: 2px; }
|
||||
|
||||
.grid { margin-top: 14px; display: grid; gap: 12px; }
|
||||
|
||||
.panel {
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 12px;
|
||||
box-shadow: var(--shadow);
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
.panel h2 { margin: 0 0 10px; font-size: 1.03rem; }
|
||||
.muted { color: var(--muted); font-size: 0.84rem; }
|
||||
|
||||
.split { display: grid; grid-template-columns: 1.2fr 1fr; gap: 12px; }
|
||||
|
||||
.chip-list { display: flex; flex-wrap: wrap; gap: 7px; }
|
||||
.chip {
|
||||
border: 1px solid var(--chip-border);
|
||||
background: var(--chip);
|
||||
color: var(--text);
|
||||
border-radius: 999px;
|
||||
font-size: 0.77rem;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.kpi-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0,1fr));
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.kpi {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
background: var(--panel);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.kpi .label { color: var(--muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.38px; }
|
||||
.kpi .value { font-size: 1.15rem; font-weight: 700; margin-top: 4px; }
|
||||
|
||||
.table-tools { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
|
||||
|
||||
table { width: 100%; border-collapse: collapse; font-size: 0.82rem; }
|
||||
th, td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
border-bottom: 1px solid var(--line);
|
||||
border-right: 1px solid color-mix(in srgb, var(--line) 82%, transparent);
|
||||
padding: 7px 6px;
|
||||
}
|
||||
th:last-child, td:last-child { border-right: 0; }
|
||||
th {
|
||||
color: var(--muted);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.35px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
tr.group-row td {
|
||||
background: color-mix(in srgb, var(--panel) 80%, var(--line));
|
||||
color: var(--text);
|
||||
font-weight: 700;
|
||||
border-bottom: 1px solid var(--line-strong);
|
||||
border-top: 1px solid var(--line-strong);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.72rem;
|
||||
letter-spacing: 0.45px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: var(--mono);
|
||||
background: color-mix(in srgb, var(--blue) 10%, transparent);
|
||||
color: var(--text);
|
||||
border-radius: 6px;
|
||||
padding: 1px 5px;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.svg-box {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
background: color-mix(in srgb, var(--panel) 92%, var(--line));
|
||||
}
|
||||
|
||||
.graph-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
|
||||
.timeline { display: grid; gap: 8px; }
|
||||
.step { display: grid; grid-template-columns: 28px 1fr; gap: 8px; align-items: start; }
|
||||
.dot {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 999px;
|
||||
color: #fff;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.d1 { background: var(--blue); }
|
||||
.d2 { background: var(--teal); }
|
||||
.d3 { background: var(--amber); }
|
||||
.d4 { background: var(--green); }
|
||||
|
||||
details {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 10px;
|
||||
padding: 8px 10px;
|
||||
background: color-mix(in srgb, var(--panel) 96%, var(--line));
|
||||
}
|
||||
|
||||
details + details { margin-top: 8px; }
|
||||
summary {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
color: var(--text);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.risk {
|
||||
border-left: 4px solid var(--amber);
|
||||
background: color-mix(in srgb, var(--amber) 10%, transparent);
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
margin-top: 7px;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.risk.ok { border-left-color: var(--green); background: color-mix(in srgb, var(--green) 10%, transparent); }
|
||||
.risk.bad { border-left-color: var(--red); background: color-mix(in srgb, var(--red) 10%, transparent); }
|
||||
|
||||
.foot {
|
||||
font-size: 0.76rem;
|
||||
color: var(--muted);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 1040px) {
|
||||
.hero, .split, .graph-grid { grid-template-columns: 1fr; }
|
||||
.kpi-cards { grid-template-columns: repeat(2, minmax(0,1fr)); }
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.kpi-cards { grid-template-columns: 1fr; }
|
||||
.table-tools { flex-wrap: wrap; }
|
||||
table { font-size: 0.78rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main class="wrap">
|
||||
<div class="toolbar">
|
||||
<button id="themeToggle" class="btn" type="button">Toggle Dark Mode</button>
|
||||
</div>
|
||||
|
||||
<section class="hero">
|
||||
<div>
|
||||
<h1>RotatingMachine Engineering Anchor</h1>
|
||||
<p>Function-design truth source for runtime behavior, control contracts, units/signals, and integration boundaries.</p>
|
||||
<div class="badge-row">
|
||||
<span class="badge">Node Type: <code>rotatingMachine</code></span>
|
||||
<span class="badge">Domain Class: <code>specificClass.js</code></span>
|
||||
<span class="badge">Wrapper: <code>nodeClass.js</code></span>
|
||||
<span class="badge">Ports: <code>3</code></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-grid">
|
||||
<div class="metric"><div class="k">Control Topics In</div><div class="v">9</div></div>
|
||||
<div class="metric"><div class="k">Signal Rows Catalogued</div><div class="v">31</div></div>
|
||||
<div class="metric"><div class="k">Anchored Tests</div><div class="v">9</div></div>
|
||||
<div class="metric"><div class="k">Known Risks</div><div class="v">4</div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="grid">
|
||||
<section class="panel">
|
||||
<h2>Connection Map (Always Visible)</h2>
|
||||
<div class="split">
|
||||
<div>
|
||||
<div class="muted">Primary integrations and contracts</div>
|
||||
<div class="chip-list" style="margin-top:8px; margin-bottom:10px">
|
||||
<span class="chip">machineGroupControl -> parent command source</span>
|
||||
<span class="chip">pumpingStation -> orchestration source</span>
|
||||
<span class="chip">measurement -> real pressure child via registerChild</span>
|
||||
<span class="chip">dashboard flows -> simulateMeasurement + chart consumers</span>
|
||||
<span class="chip">output[2] -> registerChild to parent</span>
|
||||
<span class="chip">/rotatingMachine/menu.js</span>
|
||||
<span class="chip">/rotatingMachine/configData.js</span>
|
||||
</div>
|
||||
<div class="kpi-cards">
|
||||
<div class="kpi"><div class="label">Pressure Policy</div><div class="value">Real > Virtual > 0</div></div>
|
||||
<div class="kpi"><div class="label">Mode Gate</div><div class="value">Action + Source</div></div>
|
||||
<div class="kpi"><div class="label">Tick Rate</div><div class="value">1s</div></div>
|
||||
<div class="kpi"><div class="label">Operational States</div><div class="value">4 Active</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="svg-box">
|
||||
<svg viewBox="0 0 540 230" width="100%" role="img" aria-label="integration topology">
|
||||
<defs>
|
||||
<marker id="arr" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#6f85aa"></path>
|
||||
</marker>
|
||||
</defs>
|
||||
<rect x="200" y="86" width="140" height="52" rx="10" fill="#2059d8"/>
|
||||
<text x="270" y="117" text-anchor="middle" fill="#fff" font-size="13" font-family="Segoe UI">rotatingMachine</text>
|
||||
|
||||
<rect x="18" y="20" width="160" height="44" rx="9" fill="#e9f1ff" stroke="#aac0ef"/>
|
||||
<text x="98" y="46" text-anchor="middle" fill="#244271" font-size="12">machineGroupControl</text>
|
||||
|
||||
<rect x="18" y="164" width="160" height="44" rx="9" fill="#e7faf5" stroke="#9edcca"/>
|
||||
<text x="98" y="190" text-anchor="middle" fill="#1e5244" font-size="12">pumpingStation</text>
|
||||
|
||||
<rect x="360" y="24" width="160" height="44" rx="9" fill="#fff5e5" stroke="#e9c589"/>
|
||||
<text x="440" y="50" text-anchor="middle" fill="#634319" font-size="12">dashboard / examples</text>
|
||||
|
||||
<rect x="360" y="156" width="160" height="44" rx="9" fill="#eef8e8" stroke="#b7d89e"/>
|
||||
<text x="440" y="182" text-anchor="middle" fill="#2e5a22" font-size="12">measurement</text>
|
||||
|
||||
<line x1="178" y1="44" x2="200" y2="96" stroke="#6f85aa" stroke-width="2" marker-end="url(#arr)"/>
|
||||
<line x1="178" y1="186" x2="200" y2="130" stroke="#6f85aa" stroke-width="2" marker-end="url(#arr)"/>
|
||||
<line x1="360" y1="45" x2="338" y2="97" stroke="#6f85aa" stroke-width="2" marker-end="url(#arr)"/>
|
||||
<line x1="341" y1="130" x2="360" y2="50" stroke="#8ea4c8" stroke-dasharray="4 4" stroke-width="2" marker-end="url(#arr)"/>
|
||||
<line x1="360" y1="178" x2="340" y2="126" stroke="#6f85aa" stroke-width="2" marker-end="url(#arr)"/>
|
||||
|
||||
<text x="184" y="66" fill="#5c7091" font-size="11">setMode / exec*</text>
|
||||
<text x="182" y="162" fill="#5c7091" font-size="11">station-level dispatch</text>
|
||||
<text x="334" y="78" fill="#5c7091" font-size="11">simulateMeasurement</text>
|
||||
<text x="350" y="94" fill="#5c7091" font-size="11">process + influx out</text>
|
||||
<text x="342" y="168" fill="#5c7091" font-size="11">registerChild + pressure.measured.*</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Engineering Unit & Signal Catalog</h2>
|
||||
<div class="table-tools">
|
||||
<button id="btnInput" class="btn active" type="button">Input</button>
|
||||
<button id="btnOutput" class="btn" type="button">Output</button>
|
||||
<span class="muted" id="tableMeta">Showing input topics/signals grouped by engineering function.</span>
|
||||
</div>
|
||||
<table id="signalTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Group</th>
|
||||
<th>Signal / Topic</th>
|
||||
<th>Direction</th>
|
||||
<th>Unit</th>
|
||||
<th>Typical Range</th>
|
||||
<th>Criticality</th>
|
||||
<th>Meaning</th>
|
||||
<th>Primary Source</th>
|
||||
<th>Conversion Hints</th>
|
||||
<th>Fallback / Notes</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Functional Parameter Sheet (Template + Example)</h2>
|
||||
<div class="muted">Engineering-oriented parameter table for BEP/curve/mechanical context. Example values are placeholders and scenario-dependent.</div>
|
||||
<table id="paramTable" style="margin-top:10px">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Symbool</th>
|
||||
<th>Eenheid</th>
|
||||
<th>Waarde (+/-)</th>
|
||||
<th>Toelichting</th>
|
||||
<th>Node Mapping</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="paramBody"></tbody>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>True Graphs (Data-Derived)</h2>
|
||||
<div class="muted">Curves below are drawn from repository data in <code>nodes/rotatingMachine/misc/measured_curve.json</code> (pressure slice <code>175</code>), plus derived efficiency ratio.</div>
|
||||
<div class="graph-grid" style="margin-top:10px">
|
||||
<div class="svg-box">
|
||||
<div class="muted" style="margin-bottom:6px">Flow vs Control (pressure=175)</div>
|
||||
<svg id="flowChart" viewBox="0 0 520 280" width="100%" role="img" aria-label="flow vs control"></svg>
|
||||
</div>
|
||||
<div class="svg-box">
|
||||
<div class="muted" style="margin-bottom:6px">Power vs Control (pressure=175)</div>
|
||||
<svg id="powerChart" viewBox="0 0 520 280" width="100%" role="img" aria-label="power vs control"></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="graph-grid" style="margin-top:10px">
|
||||
<div class="svg-box">
|
||||
<div class="muted" style="margin-bottom:6px">Derived Efficiency Index (flow/power) vs Control</div>
|
||||
<svg id="effChart" viewBox="0 0 520 280" width="100%" role="img" aria-label="efficiency index vs control"></svg>
|
||||
</div>
|
||||
<div class="svg-box">
|
||||
<div class="muted" style="margin-bottom:6px">Allowed Actions by Mode (config defaults)</div>
|
||||
<svg id="modeChart" viewBox="0 0 520 280" width="100%" role="img" aria-label="allowed actions by mode"></svg>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Execution Flow (Core Open)</h2>
|
||||
<div class="timeline">
|
||||
<div class="step"><div class="dot d1">1</div><div><strong>Construct</strong><div class="muted">Load defaults and model, initialize predictors/state/measurement containers, attach state listeners.</div></div></div>
|
||||
<div class="step"><div class="dot d2">2</div><div><strong>Connect</strong><div class="muted">Register virtual pressure children, listen to real/virtual measurement streams, register self to parent.</div></div></div>
|
||||
<div class="step"><div class="dot d3">3</div><div><strong>Control</strong><div class="muted">Route topics, validate mode/source/action, execute movement or sequence transitions.</div></div></div>
|
||||
<div class="step"><div class="dot d4">4</div><div><strong>Compute + Emit</strong><div class="muted">Pressure basis selection -> flow/power prediction -> efficiency/CoG/BEP -> output formatting and status update.</div></div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<h2>Extended Sections (Selective Collapse)</h2>
|
||||
<details>
|
||||
<summary>Function Inventory Snapshot</summary>
|
||||
<div class="muted" style="margin-top:8px">Anchored files: <code>rotatingMachine.js</code>, <code>src/nodeClass.js</code>, <code>src/specificClass.js</code>. 44+ callable methods/paths inventoried in markdown anchor.</div>
|
||||
<div class="chip-list" style="margin-top:8px">
|
||||
<span class="chip">constructor + init</span>
|
||||
<span class="chip">registerChild + handler dispatch</span>
|
||||
<span class="chip">handleInput + setMode + sequences</span>
|
||||
<span class="chip">calcFlow/calcPower/calcCtrl</span>
|
||||
<span class="chip">calcEfficiency + calcCog + getOutput</span>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Risks And Invariants</summary>
|
||||
<div class="risk bad"><strong>Risk:</strong> <code>CoG</code> input path calls <code>showCoG()</code> but method is not present in current <code>specificClass.js</code>.</div>
|
||||
<div class="risk bad"><strong>Risk:</strong> emergency sequence key mismatch (<code>emergencyStop</code> vs config <code>emergencystop</code>).</div>
|
||||
<div class="risk"><strong>Risk:</strong> <code>eneableLog</code> typo in state logging mapping.</div>
|
||||
<div class="risk"><strong>Risk:</strong> node label expression precedence may render unexpected labels.</div>
|
||||
<div class="risk ok"><strong>Invariant:</strong> mechanical truth remains in <code>specificClass.js</code>; wrapper remains routing/lifecycle.</div>
|
||||
<div class="risk ok"><strong>Invariant:</strong> pressure selection order stays real sensor > virtual sensor > fallback 0.</div>
|
||||
<div class="risk ok"><strong>Invariant:</strong> output channels remain process/influx/parent registration separation.</div>
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Test Evidence</summary>
|
||||
<div class="chip-list" style="margin-top:8px">
|
||||
<span class="chip">constructor.basic.test.js</span>
|
||||
<span class="chip">mode-and-input.basic.test.js</span>
|
||||
<span class="chip">error-paths.edge.test.js</span>
|
||||
<span class="chip">nodeClass-routing.edge.test.js</span>
|
||||
<span class="chip">sequences.integration.test.js</span>
|
||||
<span class="chip">registration.integration.test.js</span>
|
||||
<span class="chip">pressure-initialization.integration.test.js</span>
|
||||
<span class="chip">coolprop.integration.test.js</span>
|
||||
<span class="chip">basic-flow-dashboard.integration.test.js</span>
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<div class="foot">
|
||||
Iteration 3: engineering-grade signal table (ranges, criticality, conversion hints), functional parameter sheet, input/output filters, dark mode toggle, selective collapsible sections, and data-derived graphs.
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const signalRows = [
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:registerChild", unit: "n/a", meaning: "Attach child node source by node id", source: "nodeClass.js:268", notes: "Warns if child/source not found" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:setMode", unit: "enum", meaning: "Set current command policy mode", source: "nodeClass.js:278", notes: "Validated by setMode()" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:execSequence", unit: "json payload", meaning: "Execute named transition sequence", source: "nodeClass.js:281", notes: "Mode + source gated" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:execMovement", unit: "json payload", meaning: "Move to explicit setpoint", source: "nodeClass.js:285", notes: "Setpoint coerced to Number" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:flowMovement", unit: "json payload", meaning: "Convert desired flow to control setpoint", source: "nodeClass.js:289", notes: "Uses calcCtrl()" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:emergencystop", unit: "json payload", meaning: "Emergency stop command path", source: "nodeClass.js:294", notes: "Sequence-name mismatch risk in logic" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:simulateMeasurement", unit: "json payload", meaning: "Inject measured values from dashboard/test flow", source: "nodeClass.js:298", notes: "Finite numeric value required" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:showWorkingCurves", unit: "n/a", meaning: "Request current curve and CoG diagnostics", source: "nodeClass.js:336", notes: "Immediate response on output[0]" },
|
||||
{ dir: "input", group: "Control Topic", signal: "topic:CoG", unit: "n/a", meaning: "Request CoG diagnostics", source: "nodeClass.js:339", notes: "Calls showCoG() method" },
|
||||
|
||||
{ dir: "input", group: "Measured Signal", signal: "pressure.measured.upstream", unit: "mbar (default)", meaning: "Upstream pressure input", source: "specificClass.js:52,703", notes: "Real child preferred over virtual" },
|
||||
{ dir: "input", group: "Measured Signal", signal: "pressure.measured.downstream", unit: "mbar (default)", meaning: "Downstream pressure input", source: "specificClass.js:52,703", notes: "Combined with upstream for differential" },
|
||||
{ dir: "input", group: "Measured Signal", signal: "flow.measured.upstream", unit: "config general.unit", meaning: "Measured inflow reference", source: "specificClass.js:727", notes: "Used for reconciliation when present" },
|
||||
{ dir: "input", group: "Measured Signal", signal: "flow.measured.downstream", unit: "config general.unit", meaning: "Measured discharge reference", source: "specificClass.js:727", notes: "Accepted only in operational states" },
|
||||
{ dir: "input", group: "Measured Signal", signal: "temperature.measured.atEquipment", unit: "C (init), K used", meaning: "Fluid temperature for density/efficiency path", source: "specificClass.js:149,858", notes: "Starts at 15 C" },
|
||||
{ dir: "input", group: "Measured Signal", signal: "power.measured.atEquipment", unit: "kW", meaning: "Measured power if provided by child", source: "specificClass.js:686", notes: "Read helper exists" },
|
||||
|
||||
{ dir: "output", group: "Port Contract", signal: "output[0]", unit: "process msg", meaning: "Process payload from flattened output", source: "nodeClass.js:250", notes: "Formatted by outputUtils" },
|
||||
{ dir: "output", group: "Port Contract", signal: "output[1]", unit: "influx msg", meaning: "InfluxDB payload", source: "nodeClass.js:251", notes: "Formatted by outputUtils" },
|
||||
{ dir: "output", group: "Port Contract", signal: "output[2]", unit: "registerChild msg", meaning: "Parent registration plumbing", source: "nodeClass.js:222", notes: "Topic registerChild" },
|
||||
|
||||
{ dir: "output", group: "Predicted Signal", signal: "flow.predicted.downstream", unit: "config general.unit", meaning: "Predicted discharge flow", source: "specificClass.js:423", notes: "0 when inactive/no curve" },
|
||||
{ dir: "output", group: "Predicted Signal", signal: "flow.predicted.atEquipment", unit: "config general.unit", meaning: "Predicted flow at equipment", source: "specificClass.js:424", notes: "0 when inactive/no curve" },
|
||||
{ dir: "output", group: "Predicted Signal", signal: "flow.predicted.min", unit: "config general.unit", meaning: "Curve min flow at current pressure", source: "specificClass.js:156", notes: "Seeded in init/fallback paths" },
|
||||
{ dir: "output", group: "Predicted Signal", signal: "flow.predicted.max", unit: "config general.unit", meaning: "Curve max flow at current pressure", source: "specificClass.js:155", notes: "Seeded in init/fallback paths" },
|
||||
{ dir: "output", group: "Predicted Signal", signal: "power.predicted.atEquipment", unit: "kW", meaning: "Predicted power draw", source: "specificClass.js:448", notes: "0 when inactive/no curve" },
|
||||
{ dir: "output", group: "Predicted Signal", signal: "ctrl.predicted.atEquipment", unit: "unitless (%)", meaning: "Predicted control setpoint", source: "specificClass.js:482", notes: "From requested flow" },
|
||||
|
||||
{ dir: "output", group: "Derived KPI", signal: "efficiency.predicted.atEquipment", unit: "flow/power ratio", meaning: "Specific flow proxy", source: "specificClass.js:881", notes: "Computed when power and flow non-zero" },
|
||||
{ dir: "output", group: "Derived KPI", signal: "specificEnergyConsumption.predicted.atEquipment", unit: "power/flow", meaning: "Specific energy proxy", source: "specificClass.js:882", notes: "Computed when power and flow non-zero" },
|
||||
{ dir: "output", group: "Derived KPI", signal: "nHydraulicEfficiency.predicted.atEquipment", unit: "unitless", meaning: "Hydraulic-efficiency-like metric", source: "specificClass.js:887", notes: "Uses pressure diff + density + conversions" },
|
||||
{ dir: "output", group: "Derived KPI", signal: "cog / NCog / NCogPercent", unit: "unitless / %", meaning: "Best efficiency operating index", source: "specificClass.js:796,950", notes: "Used by higher-level optimization" },
|
||||
{ dir: "output", group: "Derived KPI", signal: "effDistFromPeak / effRelDistFromPeak", unit: "unitless", meaning: "Distance from best-efficiency point", source: "specificClass.js:924,962", notes: "Absolute + relative" },
|
||||
|
||||
{ dir: "output", group: "Runtime State", signal: "state", unit: "enum", meaning: "Current movement/state-machine state", source: "specificClass.js:943", notes: "Exposed in output object" },
|
||||
{ dir: "output", group: "Runtime State", signal: "mode", unit: "enum", meaning: "Current command mode", source: "specificClass.js:947", notes: "auto/virtualControl/fysicalControl" },
|
||||
{ dir: "output", group: "Runtime State", signal: "ctrl", unit: "%", meaning: "Current position setpoint", source: "specificClass.js:945", notes: "From state module" },
|
||||
{ dir: "output", group: "Runtime State", signal: "runtime", unit: "h", meaning: "Accumulated runtime", source: "specificClass.js:944", notes: "From state module" },
|
||||
{ dir: "output", group: "Runtime State", signal: "moveTimeleft", unit: "s", meaning: "Time remaining for movement", source: "specificClass.js:946", notes: "From state module" },
|
||||
{ dir: "output", group: "Runtime State", signal: "maintenanceTime", unit: "h", meaning: "Maintenance counter", source: "specificClass.js:951", notes: "From state module" },
|
||||
{ dir: "output", group: "Runtime State", signal: "flowNrmse / flowLongterNRMSD / flowImmediateLevel / flowLongTermLevel", unit: "mixed", meaning: "Drift diagnostics when available", source: "specificClass.js:953", notes: "Present only when flowDrift exists" }
|
||||
];
|
||||
|
||||
const tbody = document.querySelector("#signalTable tbody");
|
||||
const btnInput = document.getElementById("btnInput");
|
||||
const btnOutput = document.getElementById("btnOutput");
|
||||
const tableMeta = document.getElementById("tableMeta");
|
||||
const paramBody = document.getElementById("paramBody");
|
||||
let activeDir = "input";
|
||||
|
||||
function inferTypicalRange(r) {
|
||||
if (r.signal.indexOf("pressure.measured.upstream") >= 0) return "200-1500 mbar";
|
||||
if (r.signal.indexOf("pressure.measured.downstream") >= 0) return "400-2500 mbar";
|
||||
if (r.signal.indexOf("flow.measured") >= 0) return "0-2.5 m3/s or project unit";
|
||||
if (r.signal.indexOf("flow.predicted") >= 0) return "Curve-bound min-max";
|
||||
if (r.signal.indexOf("power.measured") >= 0) return "0-250 kW";
|
||||
if (r.signal.indexOf("power.predicted") >= 0) return "Curve-bound min-max";
|
||||
if (r.signal.indexOf("temperature.measured") >= 0) return "5-40 C (278-313 K)";
|
||||
if (r.signal.indexOf("ctrl") >= 0) return "0-100% (project may scale)";
|
||||
if (r.signal.indexOf("runtime") >= 0 || r.signal.indexOf("maintenanceTime") >= 0) return "0+ h";
|
||||
if (r.signal.indexOf("moveTimeleft") >= 0) return "0+ s";
|
||||
if (r.signal.indexOf("efficiency") >= 0 || r.signal.indexOf("nHydraulicEfficiency") >= 0) return "0-1+ (index)";
|
||||
if (r.signal.indexOf("NCog") >= 0 || r.signal.indexOf("cog") >= 0) return "0-1 (NCog), 0-100% (NCogPercent)";
|
||||
if (r.group === "Control Topic") return "Discrete commands";
|
||||
if (r.group === "Port Contract") return "Per tick / event message";
|
||||
return "Project-specific";
|
||||
}
|
||||
|
||||
function inferCriticality(r) {
|
||||
if (r.signal.indexOf("emergencystop") >= 0) return "High";
|
||||
if (r.signal.indexOf("setMode") >= 0 || r.signal.indexOf("execSequence") >= 0 || r.signal.indexOf("execMovement") >= 0 || r.signal.indexOf("flowMovement") >= 0) return "High";
|
||||
if (r.signal.indexOf("pressure.measured") >= 0) return "High";
|
||||
if (r.signal.indexOf("power.predicted") >= 0 || r.signal.indexOf("flow.predicted") >= 0 || r.signal.indexOf("output[") >= 0) return "High";
|
||||
if (r.signal.indexOf("temperature.measured") >= 0 || r.signal.indexOf("power.measured") >= 0) return "Medium";
|
||||
if (r.signal.indexOf("efficiency") >= 0 || r.signal.indexOf("NCog") >= 0 || r.signal.indexOf("flowNrmse") >= 0) return "Medium";
|
||||
if (r.signal.indexOf("showWorkingCurves") >= 0 || r.signal.indexOf("CoG") >= 0) return "Low";
|
||||
return "Medium";
|
||||
}
|
||||
|
||||
function inferConversionHints(r) {
|
||||
if (r.signal.indexOf("pressure") >= 0) return "mbar <-> Pa (1 mbar = 100 Pa)";
|
||||
if (r.signal.indexOf("flow") >= 0) return "m3/h <-> m3/s (divide/multiply by 3600); l/s <-> m3/s (x/1000)";
|
||||
if (r.signal.indexOf("power") >= 0) return "kW <-> W (x1000)";
|
||||
if (r.signal.indexOf("temperature") >= 0) return "C <-> K (+/-273.15)";
|
||||
if (r.signal.indexOf("ctrl") >= 0) return "Unitless ratio <-> % (x100)";
|
||||
if (r.signal.indexOf("runtime") >= 0 || r.signal.indexOf("maintenanceTime") >= 0) return "h <-> s (x3600)";
|
||||
return "No conversion required / message contract";
|
||||
}
|
||||
|
||||
function renderParameterSheet() {
|
||||
const params = [
|
||||
{ p: "Nominaal toerental", s: "n", u: "t/min", v: "1477 (voorbeeld)", t: "50 Hz in bedrijf", m: "Not directly output; machine/asset datasheet parameter" },
|
||||
{ p: "Debiet bij BEP", s: "Q", u: "L/s", v: "86 (voorbeeld)", t: "Beste efficientiepunt", m: "Derivable from curve + CoG/efficiency path" },
|
||||
{ p: "Opvoerhoogte", s: "H", u: "m", v: "11 (voorbeeld)", t: "Bij Q = 86 L/s", m: "Related to pressure differential conversion path" },
|
||||
{ p: "Opgenomen vermogen", s: "P2", u: "kW", v: "13-15 (voorbeeld)", t: "Inclusief mechanische verliezen", m: "output: power.predicted.atEquipment / measured power path" },
|
||||
{ p: "Rendement", s: "η", u: "%", v: "73 (voorbeeld)", t: "Maximaal rendement", m: "output: efficiency.* and nHydraulicEfficiency.*" },
|
||||
{ p: "Benodigde NPSH", s: "NPSHr", u: "m", v: "2-3 (voorbeeld)", t: "Lage cavitatie gevoeligheid", m: "Not explicit in current output; candidate future anchor metric" },
|
||||
{ p: "Persaansluiting", s: "-", u: "DN150 / PN16", v: "-", t: "Horizontale uitlaat", m: "Asset/mechanical metadata (datasheet-level)" },
|
||||
{ p: "Zuigaansluiting", s: "-", u: "DN200 / PN10", v: "-", t: "Instroomzijde pomp", m: "Asset/mechanical metadata (datasheet-level)" },
|
||||
{ p: "Pomphuismateriaal", s: "-", u: "EN-GJL-250", v: "-", t: "Gietijzer", m: "Asset/mechanical metadata (datasheet-level)" },
|
||||
{ p: "Waaiermateriaal", s: "-", u: "1.4122 RVS", v: "-", t: "Schroefcentrifugaalwaaier", m: "Asset/mechanical metadata (datasheet-level)" },
|
||||
{ p: "Pompgewicht", s: "-", u: "kg", v: "183 (voorbeeld)", t: "Droge uitvoering", m: "Asset/mechanical metadata (datasheet-level)" }
|
||||
];
|
||||
|
||||
paramBody.innerHTML = params.map(function (row) {
|
||||
return "<tr>" +
|
||||
"<td>" + row.p + "</td>" +
|
||||
"<td><code>" + row.s + "</code></td>" +
|
||||
"<td>" + row.u + "</td>" +
|
||||
"<td>" + row.v + "</td>" +
|
||||
"<td>" + row.t + "</td>" +
|
||||
"<td>" + row.m + "</td>" +
|
||||
"</tr>";
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function renderRows() {
|
||||
const rows = signalRows.filter((r) => r.dir === activeDir);
|
||||
const groups = [...new Set(rows.map((r) => r.group))];
|
||||
tbody.innerHTML = "";
|
||||
|
||||
groups.forEach((group) => {
|
||||
const gtr = document.createElement("tr");
|
||||
gtr.className = "group-row";
|
||||
const gtd = document.createElement("td");
|
||||
gtd.colSpan = 10;
|
||||
gtd.textContent = group;
|
||||
gtr.appendChild(gtd);
|
||||
tbody.appendChild(gtr);
|
||||
|
||||
rows.filter((r) => r.group === group).forEach((r) => {
|
||||
const typicalRange = inferTypicalRange(r);
|
||||
const criticality = inferCriticality(r);
|
||||
const conversionHints = inferConversionHints(r);
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = "<td>" + r.group + "</td>" +
|
||||
"<td><code>" + r.signal + "</code></td>" +
|
||||
"<td>" + r.dir + "</td>" +
|
||||
"<td>" + r.unit + "</td>" +
|
||||
"<td>" + typicalRange + "</td>" +
|
||||
"<td>" + criticality + "</td>" +
|
||||
"<td>" + r.meaning + "</td>" +
|
||||
"<td><code>" + r.source + "</code></td>" +
|
||||
"<td>" + conversionHints + "</td>" +
|
||||
"<td>" + r.notes + "</td>";
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
});
|
||||
|
||||
tableMeta.textContent = activeDir === "input"
|
||||
? "Showing input topics/signals grouped by engineering function."
|
||||
: "Showing output ports/signals/KPIs grouped by engineering function.";
|
||||
}
|
||||
|
||||
btnInput.addEventListener("click", function () {
|
||||
activeDir = "input";
|
||||
btnInput.classList.add("active");
|
||||
btnOutput.classList.remove("active");
|
||||
renderRows();
|
||||
});
|
||||
|
||||
btnOutput.addEventListener("click", function () {
|
||||
activeDir = "output";
|
||||
btnOutput.classList.add("active");
|
||||
btnInput.classList.remove("active");
|
||||
renderRows();
|
||||
});
|
||||
|
||||
const themeToggle = document.getElementById("themeToggle");
|
||||
const root = document.documentElement;
|
||||
const storedTheme = localStorage.getItem("rm_anchor_theme");
|
||||
if (storedTheme === "dark") {
|
||||
root.setAttribute("data-theme", "dark");
|
||||
}
|
||||
|
||||
themeToggle.addEventListener("click", function () {
|
||||
const isDark = root.getAttribute("data-theme") === "dark";
|
||||
const next = isDark ? "light" : "dark";
|
||||
root.setAttribute("data-theme", next);
|
||||
localStorage.setItem("rm_anchor_theme", next);
|
||||
drawAllCharts();
|
||||
});
|
||||
|
||||
function drawLineChart(svgId, options) {
|
||||
const svg = document.getElementById(svgId);
|
||||
const w = 520;
|
||||
const h = 280;
|
||||
const m = { top: 20, right: 16, bottom: 44, left: 56 };
|
||||
const iw = w - m.left - m.right;
|
||||
const ih = h - m.top - m.bottom;
|
||||
|
||||
const xMin = Math.min.apply(null, options.x);
|
||||
const xMax = Math.max.apply(null, options.x);
|
||||
const yMinRaw = Math.min.apply(null, options.y);
|
||||
const yMaxRaw = Math.max.apply(null, options.y);
|
||||
const yPad = (yMaxRaw - yMinRaw) * 0.12 || 1;
|
||||
const yMin = Math.max(0, yMinRaw - yPad);
|
||||
const yMax = yMaxRaw + yPad;
|
||||
|
||||
function sx(v) { return m.left + ((v - xMin) / (xMax - xMin || 1)) * iw; }
|
||||
function sy(v) { return m.top + ih - ((v - yMin) / (yMax - yMin || 1)) * ih; }
|
||||
|
||||
const textColor = getComputedStyle(document.documentElement).getPropertyValue("--muted").trim() || "#627489";
|
||||
const lineColor = options.color;
|
||||
const axisColor = getComputedStyle(document.documentElement).getPropertyValue("--line-strong").trim() || "#96accb";
|
||||
const gridColor = getComputedStyle(document.documentElement).getPropertyValue("--line").trim() || "#d7e0ee";
|
||||
|
||||
let path = "";
|
||||
options.x.forEach(function (xv, i) {
|
||||
const px = sx(xv);
|
||||
const py = sy(options.y[i]);
|
||||
path += (i === 0 ? "M" : " L") + px + " " + py;
|
||||
});
|
||||
|
||||
const yTicks = 5;
|
||||
let tickEls = "";
|
||||
for (let i = 0; i <= yTicks; i++) {
|
||||
const val = yMin + ((yMax - yMin) * i) / yTicks;
|
||||
const py = sy(val);
|
||||
tickEls += '<line x1="' + m.left + '" y1="' + py + '" x2="' + (w - m.right) + '" y2="' + py + '" stroke="' + gridColor + '" stroke-width="1" />';
|
||||
tickEls += '<text x="' + (m.left - 8) + '" y="' + (py + 4) + '" text-anchor="end" fill="' + textColor + '" font-size="11">' + val.toFixed(2) + '</text>';
|
||||
}
|
||||
|
||||
let pointEls = "";
|
||||
options.x.forEach(function (xv, i) {
|
||||
pointEls += '<circle cx="' + sx(xv) + '" cy="' + sy(options.y[i]) + '" r="3.8" fill="' + lineColor + '" />';
|
||||
});
|
||||
|
||||
let xTickEls = "";
|
||||
options.x.forEach(function (xv) {
|
||||
xTickEls += '<line x1="' + sx(xv) + '" y1="' + (h - m.bottom) + '" x2="' + sx(xv) + '" y2="' + (h - m.bottom + 5) + '" stroke="' + axisColor + '"/>';
|
||||
xTickEls += '<text x="' + sx(xv) + '" y="' + (h - m.bottom + 18) + '" text-anchor="middle" fill="' + textColor + '" font-size="11">' + xv + '</text>';
|
||||
});
|
||||
|
||||
svg.innerHTML = '' +
|
||||
'<rect x="0" y="0" width="520" height="280" fill="transparent" />' +
|
||||
tickEls +
|
||||
'<line x1="' + m.left + '" y1="' + (h - m.bottom) + '" x2="' + (w - m.right) + '" y2="' + (h - m.bottom) + '" stroke="' + axisColor + '" stroke-width="1.4" />' +
|
||||
'<line x1="' + m.left + '" y1="' + m.top + '" x2="' + m.left + '" y2="' + (h - m.bottom) + '" stroke="' + axisColor + '" stroke-width="1.4" />' +
|
||||
'<path d="' + path + '" fill="none" stroke="' + lineColor + '" stroke-width="2.3" />' +
|
||||
pointEls +
|
||||
xTickEls +
|
||||
'<text x="260" y="272" text-anchor="middle" fill="' + textColor + '" font-size="12">' + options.xLabel + '</text>' +
|
||||
'<text x="16" y="140" transform="rotate(-90 16 140)" text-anchor="middle" fill="' + textColor + '" font-size="12">' + options.yLabel + '</text>';
|
||||
}
|
||||
|
||||
function drawModeBarChart(svgId) {
|
||||
const svg = document.getElementById(svgId);
|
||||
const modes = [
|
||||
{ mode: "auto", count: 6 },
|
||||
{ mode: "virtualControl", count: 6 },
|
||||
{ mode: "fysicalControl", count: 4 }
|
||||
];
|
||||
|
||||
const w = 520, h = 280;
|
||||
const m = { top: 20, right: 16, bottom: 52, left: 52 };
|
||||
const iw = w - m.left - m.right;
|
||||
const ih = h - m.top - m.bottom;
|
||||
const max = 6;
|
||||
const barW = iw / modes.length * 0.58;
|
||||
const gap = iw / modes.length * 0.42;
|
||||
|
||||
const textColor = getComputedStyle(document.documentElement).getPropertyValue("--muted").trim() || "#627489";
|
||||
const axisColor = getComputedStyle(document.documentElement).getPropertyValue("--line-strong").trim() || "#96accb";
|
||||
const blue = getComputedStyle(document.documentElement).getPropertyValue("--blue").trim() || "#2059d8";
|
||||
const teal = getComputedStyle(document.documentElement).getPropertyValue("--teal").trim() || "#0d9f9e";
|
||||
const amber = getComputedStyle(document.documentElement).getPropertyValue("--amber").trim() || "#cf8a11";
|
||||
const colors = [blue, teal, amber];
|
||||
|
||||
let bars = "";
|
||||
modes.forEach(function (d, i) {
|
||||
const x = m.left + i * (barW + gap) + gap * 0.5;
|
||||
const bh = (d.count / max) * ih;
|
||||
const y = m.top + ih - bh;
|
||||
bars += '<rect x="' + x + '" y="' + y + '" width="' + barW + '" height="' + bh + '" rx="6" fill="' + colors[i] + '" />';
|
||||
bars += '<text x="' + (x + barW / 2) + '" y="' + (y - 6) + '" text-anchor="middle" fill="' + textColor + '" font-size="12">' + d.count + '</text>';
|
||||
bars += '<text x="' + (x + barW / 2) + '" y="' + (h - m.bottom + 18) + '" text-anchor="middle" fill="' + textColor + '" font-size="11">' + d.mode + '</text>';
|
||||
});
|
||||
|
||||
svg.innerHTML = '' +
|
||||
'<line x1="' + m.left + '" y1="' + (h - m.bottom) + '" x2="' + (w - m.right) + '" y2="' + (h - m.bottom) + '" stroke="' + axisColor + '" stroke-width="1.4" />' +
|
||||
'<line x1="' + m.left + '" y1="' + m.top + '" x2="' + m.left + '" y2="' + (h - m.bottom) + '" stroke="' + axisColor + '" stroke-width="1.4" />' +
|
||||
bars +
|
||||
'<text x="260" y="272" text-anchor="middle" fill="' + textColor + '" font-size="12">Mode</text>' +
|
||||
'<text x="16" y="140" transform="rotate(-90 16 140)" text-anchor="middle" fill="' + textColor + '" font-size="12">Allowed actions (count)</text>';
|
||||
}
|
||||
|
||||
function drawAllCharts() {
|
||||
const ctrl = [0, 350, 550, 600, 1000];
|
||||
const flow = [0, 0.9294287109, 0.941, 1.05, 1.9220000000];
|
||||
const power = [5.0760000000, 39.1178710938, 64.8, 76.4, 169.2000000000];
|
||||
const eff = flow.map(function (q, i) { return q / (power[i] || 1); });
|
||||
|
||||
drawLineChart("flowChart", {
|
||||
x: ctrl,
|
||||
y: flow,
|
||||
xLabel: "Control setpoint",
|
||||
yLabel: "Flow",
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue("--teal").trim() || "#0d9f9e"
|
||||
});
|
||||
|
||||
drawLineChart("powerChart", {
|
||||
x: ctrl,
|
||||
y: power,
|
||||
xLabel: "Control setpoint",
|
||||
yLabel: "Power (kW)",
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue("--blue").trim() || "#2059d8"
|
||||
});
|
||||
|
||||
drawLineChart("effChart", {
|
||||
x: ctrl,
|
||||
y: eff,
|
||||
xLabel: "Control setpoint",
|
||||
yLabel: "Efficiency index (flow/power)",
|
||||
color: getComputedStyle(document.documentElement).getPropertyValue("--green").trim() || "#0fa57d"
|
||||
});
|
||||
|
||||
drawModeBarChart("modeChart");
|
||||
}
|
||||
|
||||
renderRows();
|
||||
renderParameterSheet();
|
||||
drawAllCharts();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,226 @@
|
||||
# Rotating Machine Function Anchor
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- **Node type**: `rotatingMachine` (`nodes/rotatingMachine/rotatingMachine.js:1`, `nodes/rotatingMachine/rotatingMachine.html:16`)
|
||||
- **Consumes parent/control topics**: `setMode`, `execSequence`, `execMovement`, `flowMovement`, `emergencystop`, `simulateMeasurement`, `registerChild`, `showWorkingCurves`, `CoG` (`nodes/rotatingMachine/src/nodeClass.js:267`)
|
||||
- **Publishes periodic outputs**:
|
||||
- Output `0`: process payload (`nodes/rotatingMachine/src/nodeClass.js:249`)
|
||||
- Output `1`: influx payload (`nodes/rotatingMachine/src/nodeClass.js:251`)
|
||||
- Output `2`: registration/control plumbing (`registerChild`) (`nodes/rotatingMachine/src/nodeClass.js:222`)
|
||||
- **Cross-node integrations (direct observed)**:
|
||||
- Registered/managed by `machineGroupControl` as `machine`, which then commands each machine via `handleInput('parent', ...)` (`nodes/machineGroupControl/src/specificClass.js:50`, `nodes/machineGroupControl/src/specificClass.js:711`, `nodes/machineGroupControl/src/specificClass.js:1028`)
|
||||
- Can be orchestrated by `pumpingStation` via `execSequence` and movement commands (`nodes/pumpingStation/src/specificClass.js:296`, `nodes/pumpingStation/src/specificClass.js:297`)
|
||||
- Dashboard/test flows inject `simulateMeasurement` and consume process output topics (`nodes/rotatingMachine/examples/basic.flow.json:380`, `nodes/rotatingMachine/examples/basic.flow.json:412`)
|
||||
- **Admin/UI endpoints**:
|
||||
- `GET /rotatingMachine/menu.js`
|
||||
- `GET /rotatingMachine/configData.js` (`nodes/rotatingMachine/rotatingMachine.js:17`, `nodes/rotatingMachine/rotatingMachine.js:27`)
|
||||
|
||||
## 1) Unit Table (Anchor Starts Here)
|
||||
| Signal/Field | Represents | Asset Type | Default Unit | Accepted Units | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| `pressure.measured.*` | pressure input (upstream/downstream) | measurement child (`pressure`) | `mbar` | any convertible via `MeasurementContainer` | `nodes/rotatingMachine/src/specificClass.js:48`, `nodes/rotatingMachine/src/specificClass.js:708` | real child sensors and virtual dashboard child | pressure dimension for curve selection (`predict*.fDimension`) | if missing, pressure dimension forced to minimum (`0`) (`nodes/rotatingMachine/src/specificClass.js:552`) |
|
||||
| `flow.predicted.downstream` | predicted flow at discharge | rotating machine | `general.unit` from config | convertible (`m3/h`, `l/s`, etc.) | `nodes/rotatingMachine/src/specificClass.js:53`, `nodes/rotatingMachine/src/specificClass.js:423` | `calcFlow()` | output formatting, status text, parent/group logic | forced `0` if non-operational or no curve (`nodes/rotatingMachine/src/specificClass.js:415`, `nodes/rotatingMachine/src/specificClass.js:429`) |
|
||||
| `flow.predicted.atEquipment` | same flow at equipment point | rotating machine | `general.unit` | convertible | `nodes/rotatingMachine/src/specificClass.js:53`, `nodes/rotatingMachine/src/specificClass.js:424` | `calcFlow()` | efficiency calculations | forced `0` if non-operational/no curve |
|
||||
| `power.predicted.atEquipment` | predicted power draw | rotating machine | `kW` | convertible (`W`, `kW`) | `nodes/rotatingMachine/src/specificClass.js:54`, `nodes/rotatingMachine/src/specificClass.js:448` | `calcPower()` | efficiency calculations, status text | forced `0` if non-operational/no curve |
|
||||
| `ctrl.predicted.atEquipment` | predicted control position for requested flow | rotating machine | unitless (%) semantic | numeric | `nodes/rotatingMachine/src/specificClass.js:482` | `calcCtrl()` | `flowmovement` command path | returns `0` if no curve |
|
||||
| `temperature.measured.atEquipment` | process temp for density lookup | machine fluid context | `C` default, converted to `K` when used | convertible | init at `15 C` (`nodes/rotatingMachine/src/specificClass.js:149`) | init + measurement updates | CoolProp density input | if missing conversion, efficiency can degrade silently |
|
||||
| `atmPressure.measured.atEquipment` | atmospheric pressure for density lookup | machine fluid context | `Pa` | convertible | init at `101325 Pa` (`nodes/rotatingMachine/src/specificClass.js:151`) | init | CoolProp density input | fallback density `1000 kg/m3` on CoolProp error (`nodes/rotatingMachine/src/specificClass.js:867`) |
|
||||
| `efficiency.*` | specific flow (`flow/power`) | derived metric | implicit unitless ratio | numeric | `nodes/rotatingMachine/src/specificClass.js:878`, `nodes/rotatingMachine/src/specificClass.js:881` | `calcEfficiency()` | output and BEP distance metrics | unchanged if power/flow are zero |
|
||||
| `specificEnergyConsumption.*` | power per flow | derived metric | implicit | numeric | `nodes/rotatingMachine/src/specificClass.js:879`, `nodes/rotatingMachine/src/specificClass.js:882` | `calcEfficiency()` | output consumers | unchanged if power/flow zero |
|
||||
| `nHydraulicEfficiency.*` | hydraulic-efficiency-like metric | derived metric | unitless | numeric | `nodes/rotatingMachine/src/specificClass.js:884` | `calcEfficiency()` | diagnostic output | skipped if pressure/flow/power conversions unavailable |
|
||||
| `cog`, `NCog`, `NCogPercent` | efficiency-curve peak indicators | derived curve metrics | unitless | numeric | `nodes/rotatingMachine/src/specificClass.js:796`, `nodes/rotatingMachine/src/specificClass.js:948` | `calcCog()`, `getOutput()` | group optimization, dashboards | retains last computed values |
|
||||
| `effDistFromPeak`, `effRelDistFromPeak` | distance from best-efficiency point | derived | unitless | numeric | `nodes/rotatingMachine/src/specificClass.js:924`, `nodes/rotatingMachine/src/specificClass.js:962` | `calcDistanceBEP()` | output consumers | remains last computed value |
|
||||
| `runtime`, `maintenanceTime`, `moveTimeleft`, `state` | movement/state telemetry | state machine | `h`/`s`/enum | numeric/string | `nodes/rotatingMachine/src/specificClass.js:943` | `state` module | output/status/parent control | depends on `state` module behavior |
|
||||
|
||||
## 2) Class Identity
|
||||
- **Runtime registration + endpoints**: `nodes/rotatingMachine/rotatingMachine.js`
|
||||
- **Node-RED wrapper/routing**: `nodes/rotatingMachine/src/nodeClass.js`
|
||||
- **Domain/mechanical logic**: `nodes/rotatingMachine/src/specificClass.js`
|
||||
- **Editor UI/defaults**: `nodes/rotatingMachine/rotatingMachine.html`
|
||||
- **Default config schema/validation rules**: `nodes/generalFunctions/src/configs/rotatingMachine.json`
|
||||
|
||||
## 3) Configuration Contract
|
||||
| UI Field | Runtime Path | Default | Validation/Coercion | Behavior Impact | Source |
|
||||
|---|---|---|---|---|---|
|
||||
| `speed` | `stateConfig.movement.speed` | `1` | `Number(uiConfig.speed)` | movement progression speed | `nodes/rotatingMachine/rotatingMachine.html:22`, `nodes/rotatingMachine/src/nodeClass.js:91` |
|
||||
| `startup/warmup/shutdown/cooldown` | `stateConfig.time.*` | `0` | `Number(...)` | sequence transition durations | `nodes/rotatingMachine/rotatingMachine.html:23`, `nodes/rotatingMachine/src/nodeClass.js:95` |
|
||||
| `movementMode` | `stateConfig.movement.mode` | `staticspeed` | raw string | state movement model selection | `nodes/rotatingMachine/rotatingMachine.html:27`, `nodes/rotatingMachine/src/nodeClass.js:92` |
|
||||
| `unit` | `config.general.unit` + `config.asset.unit` | UI empty, config default `l/s` | direct assign then config init | base flow unit for measurements and outputs | `nodes/rotatingMachine/src/nodeClass.js:50`, `nodes/generalFunctions/src/configs/rotatingMachine.json:18` |
|
||||
| `model` | `config.asset.model` | UI empty, config default `Unknown` | direct assign | curve loading via `loadCurve(model)` | `nodes/rotatingMachine/src/nodeClass.js:62`, `nodes/rotatingMachine/src/specificClass.js:18` |
|
||||
| logging fields | `config.general.logging.*` | `enableLog=false`, `logLevel=error` in UI; config default enabled/info | direct assign | runtime verbosity | `nodes/rotatingMachine/rotatingMachine.html:39`, `nodes/rotatingMachine/src/nodeClass.js:51` |
|
||||
| `positionVsParent` | `config.functionality.positionVsParent` | UI empty, config default `atEquipment` | direct assign + default in schema | registration topology to parent | `nodes/rotatingMachine/src/nodeClass.js:66`, `nodes/generalFunctions/src/configs/rotatingMachine.json:74` |
|
||||
| Mode/action/source rules | `config.mode.*` | schema defaults | configUtils validation into `Set` semantics | command gating | `nodes/generalFunctions/src/configs/rotatingMachine.json:231`, `nodes/rotatingMachine/src/specificClass.js:269` |
|
||||
| Sequences | `config.sequences.*` | schema defaults | configUtils validation | machine state transitions | `nodes/generalFunctions/src/configs/rotatingMachine.json:360`, `nodes/rotatingMachine/src/specificClass.js:363` |
|
||||
|
||||
## 4) Input/Output Contract
|
||||
### 4.1 Input topics (`nodeClass`)
|
||||
| Topic | Payload schema | Handler | Side effects |
|
||||
|---|---|---|---|
|
||||
| `registerChild` | `payload=<nodeId>`, optional `positionVsParent` | registers child source via `childRegistrationUtils` | starts measurement event wiring (`nodes/rotatingMachine/src/nodeClass.js:268`) |
|
||||
| `setMode` | `payload=<mode>` | `setMode()` | updates command policy mode |
|
||||
| `execSequence` | `{source, action, parameter}` | `handleInput()` | executes state sequence |
|
||||
| `execMovement` | `{source, action, setpoint}` | `handleInput()` | moves position |
|
||||
| `flowMovement` | `{source, action, setpoint}` | `handleInput()` | converts flow->ctrl then moves |
|
||||
| `emergencystop` | `{source, action}` | `handleInput()` | emergency sequence attempt |
|
||||
| `simulateMeasurement` | `{type, position, value, unit, timestamp?}` | measurement update handlers | updates virtual pressure or measured values |
|
||||
| `showWorkingCurves` | any | immediate response on output 0 | emits curve/cog debug payload |
|
||||
| `CoG` | any | immediate response on output 0 | calls `m.showCoG()` (method currently not defined in `specificClass`) |
|
||||
|
||||
### 4.2 Output ports
|
||||
| Port | Message type | Source |
|
||||
|---|---|---|
|
||||
| `0` | formatted process message from flattened measurements + state fields | `nodes/rotatingMachine/src/nodeClass.js:250` |
|
||||
| `1` | formatted influxdb message | `nodes/rotatingMachine/src/nodeClass.js:251` |
|
||||
| `2` | registration to parent: `{topic:'registerChild', payload:id, positionVsParent}` | `nodes/rotatingMachine/src/nodeClass.js:222` |
|
||||
|
||||
### 4.3 Admin endpoints
|
||||
| Endpoint | Purpose | Source |
|
||||
|---|---|---|
|
||||
| `/rotatingMachine/menu.js` | dynamic editor menu script (`asset`, `logger`, `position`) | `nodes/rotatingMachine/rotatingMachine.js:17` |
|
||||
| `/rotatingMachine/configData.js` | dynamic default/config script | `nodes/rotatingMachine/rotatingMachine.js:27` |
|
||||
|
||||
## 5) Mode, State, and Control Model
|
||||
- **Modes**: `auto`, `virtualControl`, `fysicalControl` (`nodes/generalFunctions/src/configs/rotatingMachine.json:233`)
|
||||
- **Mode gate enforcement**:
|
||||
- `isValidActionForMode(action, mode)` (`nodes/rotatingMachine/src/specificClass.js:279`)
|
||||
- `isValidSourceForMode(source, mode)` (`nodes/rotatingMachine/src/specificClass.js:269`)
|
||||
- **Actions supported by handler**: `execsequence`, `execmovement`, `flowmovement`, `entermaintenance`, `exitmaintenance`, `emergencystop`, `statuscheck` (`nodes/rotatingMachine/src/specificClass.js:303`)
|
||||
- **Operational states for active prediction**: `operational`, `warmingup`, `accelerating`, `decelerating` (`nodes/rotatingMachine/src/specificClass.js:739`)
|
||||
- **Sequence defaults**: startup/shutdown/emergencystop/maintenance flows defined in config schema (`nodes/generalFunctions/src/configs/rotatingMachine.json:365`)
|
||||
|
||||
## 6) End-to-End Execution Flow
|
||||
1. Node registration instantiates `nodeClass`, then `Specific` (`Machine`).
|
||||
2. `Machine` constructor loads model curve, initializes predictors/state/measurements, creates virtual pressure children, and subscribes to state events.
|
||||
3. `nodeClass` starts delayed child registration (`output 2`) and 1-second tick/status loops.
|
||||
4. Incoming topics route through `switch(msg.topic)` to mode changes, movement/sequence commands, child registration, and simulated measurements.
|
||||
5. Child measurement events update parent measurement container and dispatch typed handlers.
|
||||
6. Pressure updates set predictor dimension, recompute flow/power/efficiency/CoG/BEP metrics.
|
||||
7. Each tick emits formatted process + influx messages.
|
||||
|
||||
## 7) Full Function Inventory
|
||||
### 7.1 `nodes/rotatingMachine/rotatingMachine.js`
|
||||
| Function | Purpose | Source |
|
||||
|---|---|---|
|
||||
| module export init | register Node-RED node type and admin endpoints | `nodes/rotatingMachine/rotatingMachine.js:5` |
|
||||
|
||||
### 7.2 `nodes/rotatingMachine/src/nodeClass.js`
|
||||
| Function | Purpose | Key effects | Source |
|
||||
|---|---|---|---|
|
||||
| `constructor` | boot wrapper lifecycle | load config, create source, start loops/handlers | `nodes/rotatingMachine/src/nodeClass.js:16` |
|
||||
| `_loadConfig` | map UI config to runtime config | builds `general/asset/functionality`; builds `outputUtils` | `nodes/rotatingMachine/src/nodeClass.js:44` |
|
||||
| `_setupSpecificClass` | build `Machine` with movement/time state config | instantiates `Specific`; stores on `node.source` | `nodes/rotatingMachine/src/nodeClass.js:77` |
|
||||
| `_bindEvents` | placeholder | no-op currently | `nodes/rotatingMachine/src/nodeClass.js:112` |
|
||||
| `_updateNodeStatus` | compose Node-RED status icon/text | warns once for missing pressure init; includes flow/power/state | `nodes/rotatingMachine/src/nodeClass.js:116` |
|
||||
| `_registerChild` | announce self to parent | sends `registerChild` on output 2 after 100ms | `nodes/rotatingMachine/src/nodeClass.js:217` |
|
||||
| `_startTickLoop` | periodic work | 1s `_tick`; 1s status refresh | `nodes/rotatingMachine/src/nodeClass.js:230` |
|
||||
| `_tick` | periodic output generation | `formatMsg(process)` + `formatMsg(influxdb)` send to outputs 0/1 | `nodes/rotatingMachine/src/nodeClass.js:246` |
|
||||
| `_attachInputHandler` | route inbound topics | dispatches all command/simulation/register/show topics | `nodes/rotatingMachine/src/nodeClass.js:260` |
|
||||
| `_attachCloseHandler` | shutdown cleanup | clears intervals | `nodes/rotatingMachine/src/nodeClass.js:357` |
|
||||
|
||||
### 7.3 `nodes/rotatingMachine/src/specificClass.js`
|
||||
| Function | Purpose | Key effects | Source |
|
||||
|---|---|---|---|
|
||||
| `constructor` | initialize machine domain object | config/curve/predictors/state/measurement/events/virtual children | `nodes/rotatingMachine/src/specificClass.js:7` |
|
||||
| `_initVirtualPressureChildren` | create simulated upstream/downstream pressure children | registers virtual measurement children | `nodes/rotatingMachine/src/specificClass.js:104` |
|
||||
| `_init` | seed base measurements and min/max flow | sets temperature, atmPressure, curve min/max | `nodes/rotatingMachine/src/specificClass.js:147` |
|
||||
| `_updateState` | enforce non-operational flow = 0 | overwrites predicted flow when inactive | `nodes/rotatingMachine/src/specificClass.js:163` |
|
||||
| `registerChild` | subscribe to child measurement events | stores real pressure child IDs, updates measurements, calls handlers | `nodes/rotatingMachine/src/specificClass.js:173` |
|
||||
| `_callMeasurementHandler` | typed measurement dispatch | pressure/flow/temp handlers + fallback | `nodes/rotatingMachine/src/specificClass.js:212` |
|
||||
| `assessDrift` | compare measured vs predicted windows | delegates to `nrmse.assessDrift` | `nodes/rotatingMachine/src/specificClass.js:237` |
|
||||
| `reverseCurve` | flip x/y arrays | used for flow->ctrl predictor | `nodes/rotatingMachine/src/specificClass.js:252` |
|
||||
| `updateConfig` | apply validated config patch | merges via `configUtils` | `nodes/rotatingMachine/src/specificClass.js:264` |
|
||||
| `isValidSourceForMode` | mode source gate | checks configured allowed set | `nodes/rotatingMachine/src/specificClass.js:269` |
|
||||
| `isValidActionForMode` | mode action gate | checks configured allowed set | `nodes/rotatingMachine/src/specificClass.js:279` |
|
||||
| `handleInput` | main command dispatcher | executes sequence/movement/status commands | `nodes/rotatingMachine/src/specificClass.js:289` |
|
||||
| `abortMovement` | cancel current movement | delegates to state abort if available | `nodes/rotatingMachine/src/specificClass.js:345` |
|
||||
| `setMode` | update current mode | validates against schema enum values | `nodes/rotatingMachine/src/specificClass.js:351` |
|
||||
| `executeSequence` | run state sequence | transitions through configured states; updatePosition at end | `nodes/rotatingMachine/src/specificClass.js:363` |
|
||||
| `setpoint` | move to numeric target | validates non-negative number then `state.moveTo` | `nodes/rotatingMachine/src/specificClass.js:394` |
|
||||
| `calcFlow` | predict flow from current curve and ctrl position | writes predicted flow measurements | `nodes/rotatingMachine/src/specificClass.js:413` |
|
||||
| `calcPower` | predict power from current curve and ctrl position | writes predicted power measurement | `nodes/rotatingMachine/src/specificClass.js:438` |
|
||||
| `inputFlowCalcPower` | estimate power from requested flow | flow->ctrl->power chained prediction | `nodes/rotatingMachine/src/specificClass.js:460` |
|
||||
| `calcCtrl` | estimate ctrl for desired flow | writes predicted ctrl | `nodes/rotatingMachine/src/specificClass.js:478` |
|
||||
| `getMeasuredPressure` | choose pressure basis for prediction | differential preferred; then downstream; then upstream; else 0 | `nodes/rotatingMachine/src/specificClass.js:496` |
|
||||
| `_getPreferredPressureValue` | pressure source priority resolver | real child > virtual child > aggregated position value | `nodes/rotatingMachine/src/specificClass.js:570` |
|
||||
| `getPressureInitializationStatus` | pressure readiness status model | upstream/downstream/differential flags | `nodes/rotatingMachine/src/specificClass.js:600` |
|
||||
| `updateSimulatedMeasurement` | write dashboard-sim values | pressure route via virtual child; others dispatch typed handler | `nodes/rotatingMachine/src/specificClass.js:617` |
|
||||
| `handleMeasuredFlow` | reconcile measured flow availability/consistency | returns matched/single measurement or null | `nodes/rotatingMachine/src/specificClass.js:644` |
|
||||
| `handleMeasuredPower` | read measured power | returns value or null with error | `nodes/rotatingMachine/src/specificClass.js:685` |
|
||||
| `updateMeasuredTemperature` | temp update hook | currently log-only | `nodes/rotatingMachine/src/specificClass.js:698` |
|
||||
| `updateMeasuredPressure` | pressure update hook | stores pressure, recomputes pressure basis and position metrics | `nodes/rotatingMachine/src/specificClass.js:703` |
|
||||
| `updateMeasuredFlow` | flow update hook | stores measured flow if operational; mirrors predicted flow value | `nodes/rotatingMachine/src/specificClass.js:718` |
|
||||
| `_isOperationalState` | operational predicate | active states used by prediction guards | `nodes/rotatingMachine/src/specificClass.js:737` |
|
||||
| `updatePosition` | core recompute pipeline on movement/state changes | calc flow/power -> efficiency -> cog -> BEP distance | `nodes/rotatingMachine/src/specificClass.js:745` |
|
||||
| `calcDistanceFromPeak` | abs distance metric | absolute efficiency delta | `nodes/rotatingMachine/src/specificClass.js:767` |
|
||||
| `calcRelativeDistanceFromPeak` | normalized distance metric | interpolation to [0,1] | `nodes/rotatingMachine/src/specificClass.js:771` |
|
||||
| `showWorkingCurves` | debugging snapshot of current curve context | returns current curves + metrics | `nodes/rotatingMachine/src/specificClass.js:779` |
|
||||
| `calcCog` | compute peak efficiency point on current curve | updates `cog`, `NCog`, indexes, minEfficiency | `nodes/rotatingMachine/src/specificClass.js:796` |
|
||||
| `calcEfficiencyCurve` | derive efficiency curve and peak/min | from aligned power/flow arrays | `nodes/rotatingMachine/src/specificClass.js:817` |
|
||||
| `calcFlowPower` | convenience combined prediction | calls `calcFlow` and `calcPower` | `nodes/rotatingMachine/src/specificClass.js:845` |
|
||||
| `calcEfficiency` | compute efficiency family metrics | CoolProp density path + fallback + writes derived metrics | `nodes/rotatingMachine/src/specificClass.js:854` |
|
||||
| `updateCurve` | replace machine curve at runtime | validates config and updates predictors | `nodes/rotatingMachine/src/specificClass.js:897` |
|
||||
| `getCompleteCurve` | return full loaded curves | power+flow input curves | `nodes/rotatingMachine/src/specificClass.js:910` |
|
||||
| `getCurrentCurves` | return currently selected pressure curve slices | current flow/power curves | `nodes/rotatingMachine/src/specificClass.js:916` |
|
||||
| `calcDistanceBEP` | write BEP distance metrics | updates `absDistFromPeak`, `relDistFromPeak` | `nodes/rotatingMachine/src/specificClass.js:924` |
|
||||
| `getOutput` | flatten and enrich output object | adds state/runtime/ctrl/mode/cog/drift/eff-distance | `nodes/rotatingMachine/src/specificClass.js:936` |
|
||||
|
||||
## 8) Calculations and Capability Matrix
|
||||
- **Curve-backed capabilities**:
|
||||
- flow prediction (`nq`) via `predictFlow`
|
||||
- power prediction (`np`) via `predictPower`
|
||||
- control inversion (flow->ctrl) via reversed `nq`
|
||||
- **Pressure basis selection order**:
|
||||
1. real differential (`downstream - upstream`)
|
||||
2. real/virtual downstream only
|
||||
3. real/virtual upstream only
|
||||
4. fallback `0` (minimum pressure behavior)
|
||||
- **Availability-first behavior**:
|
||||
- missing pressure does not stop operation; it degrades predictions and warns.
|
||||
- CoolProp failure does not stop operation; density fallback is used.
|
||||
- non-operational states force predicted flow/power to zero.
|
||||
- **BEP indicators**:
|
||||
- computes peak efficiency index and normalized CoG (`NCog`) for optimization usage by parent/group controllers.
|
||||
|
||||
## 9) Error Handling and Safeguards
|
||||
- Invalid actions/sources are rejected by mode gates, with warnings (`nodes/rotatingMachine/src/specificClass.js:297`).
|
||||
- Invalid setpoint (`<0` or non-number) is rejected in `setpoint()` (`nodes/rotatingMachine/src/specificClass.js:398`).
|
||||
- Missing curve model disables predictor objects but keeps class alive (`nodes/rotatingMachine/src/specificClass.js:27`).
|
||||
- Missing pressure initialization surfaces Node-RED warning ring status (`nodes/rotatingMachine/src/nodeClass.js:126`).
|
||||
- `simulateMeasurement` rejects non-finite values and unsupported types (`nodes/rotatingMachine/src/nodeClass.js:312`).
|
||||
|
||||
## 10) Test Evidence Matrix
|
||||
| Test file | Covered behavior |
|
||||
|---|---|
|
||||
| `nodes/rotatingMachine/test/basic/constructor.basic.test.js` | constructor curve/no-curve behavior and output shape |
|
||||
| `nodes/rotatingMachine/test/basic/mode-and-input.basic.test.js` | `setMode`, `handleInput` validation, operational-state predicate |
|
||||
| `nodes/rotatingMachine/test/edge/error-paths.edge.test.js` | negative setpoint resilience, status failure path |
|
||||
| `nodes/rotatingMachine/test/edge/nodeClass-routing.edge.test.js` | input-topic routing, pressure initialization status warning, curve/CoG reply routing |
|
||||
| `nodes/rotatingMachine/test/integration/sequences.integration.test.js` | startup sequence and movement execution |
|
||||
| `nodes/rotatingMachine/test/integration/registration.integration.test.js` | child registration and pressure event propagation |
|
||||
| `nodes/rotatingMachine/test/integration/pressure-initialization.integration.test.js` | explicit pressure init combinations and real-vs-virtual pressure priority |
|
||||
| `nodes/rotatingMachine/test/integration/coolprop.integration.test.js` | efficiency path with CoolProp and medium-pressure initialization behavior |
|
||||
| `nodes/rotatingMachine/test/integration/basic-flow-dashboard.integration.test.js` | example-flow parser/wiring contracts for dashboard topics |
|
||||
|
||||
## 11) Invariants (Anchor Truth)
|
||||
- `specificClass` is the mechanical/logic source of truth; `nodeClass` is routing/lifecycle only.
|
||||
- Prediction calculations must be curve-backed when curve exists, and availability-first fallback when it does not.
|
||||
- Pressure selection priority is **real sensor > virtual dashboard > aggregated fallback**.
|
||||
- Command execution must remain mode-gated by both action and source.
|
||||
- Output shape must keep process/influx separation and parent registration on output port 2.
|
||||
- Operational-state gating must continue to prevent active prediction outputs in inactive states.
|
||||
|
||||
## 12) Known Gaps / Risks (Current Implementation)
|
||||
- `nodeClass` routes topic `CoG` to `m.showCoG()`, but `showCoG` is not present in `specificClass` (runtime risk on that topic): `nodes/rotatingMachine/src/nodeClass.js:340`.
|
||||
- `handleInput('emergencystop')` calls sequence `"emergencyStop"`, but config default key is `"emergencystop"` (case/name mismatch risk): `nodes/rotatingMachine/src/specificClass.js:327`, `nodes/generalFunctions/src/configs/rotatingMachine.json:381`.
|
||||
- `_setupSpecificClass` uses `machineConfig.eneableLog` (typo) for state logging config; likely not intended: `nodes/rotatingMachine/src/nodeClass.js:86`.
|
||||
- Label expression can evaluate unexpectedly because `+` and `||` precedence are mixed: `nodes/rotatingMachine/rotatingMachine.html:58`.
|
||||
|
||||
## 13) Change Checklist
|
||||
When changing rotatingMachine logic, update all of:
|
||||
1. Runtime logic in `nodes/rotatingMachine/src/specificClass.js`.
|
||||
2. Node-RED routing/lifecycle in `nodes/rotatingMachine/src/nodeClass.js`.
|
||||
3. UI defaults/fields in `nodes/rotatingMachine/rotatingMachine.html`.
|
||||
4. Config schema and mode/action/source/sequence defaults in `nodes/generalFunctions/src/configs/rotatingMachine.json`.
|
||||
5. Example flow contracts in `nodes/rotatingMachine/examples/*.flow.json`.
|
||||
6. Tests under `nodes/rotatingMachine/test/` (basic, edge, integration).
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Rotating Machine Test Evidence
|
||||
|
||||
## Scope
|
||||
Evidence source for `ANCHOR-rotatingMachine.md`.
|
||||
|
||||
## Test-to-Contract Mapping
|
||||
| Test file | Contract/Behavior Anchored |
|
||||
|---|---|
|
||||
| `nodes/rotatingMachine/test/basic/constructor.basic.test.js` | Constructor should tolerate missing model curve and still return output object with core fields. |
|
||||
| `nodes/rotatingMachine/test/basic/mode-and-input.basic.test.js` | Mode validation, source/action gating behavior, and active-state definition (`warmingup` active). |
|
||||
| `nodes/rotatingMachine/test/edge/error-paths.edge.test.js` | Error path resilience in `setpoint()` and status update exception fallback (`Status Error`). |
|
||||
| `nodes/rotatingMachine/test/edge/nodeClass-routing.edge.test.js` | Topic routing for control and simulation commands, pressure init warning behavior, and debug topic reply routing. |
|
||||
| `nodes/rotatingMachine/test/integration/sequences.integration.test.js` | End-to-end state transitions for startup and movement command paths. |
|
||||
| `nodes/rotatingMachine/test/integration/registration.integration.test.js` | Child measurement registration pipeline stores measured pressure in parent container. |
|
||||
| `nodes/rotatingMachine/test/integration/pressure-initialization.integration.test.js` | Pressure initialization matrix (none/upstream/downstream/both) and preference for real child pressure over virtual dashboard pressure. |
|
||||
| `nodes/rotatingMachine/test/integration/coolprop.integration.test.js` | Efficiency calculation path passes through CoolProp logic and verifies pressure dimension initialization behavior. |
|
||||
| `nodes/rotatingMachine/test/integration/basic-flow-dashboard.integration.test.js` | Example dashboard parser wiring and topic/index contracts for flow/power/pressure charts. |
|
||||
|
||||
## Remaining Coverage Gaps
|
||||
- No direct test proves `handleInput('emergencystop')` sequence-name alignment with config key.
|
||||
- No direct test for `CoG` input topic when `showCoG` is absent.
|
||||
- No direct test for UI label precedence behavior in `rotatingMachine.html`.
|
||||
- No direct test for typo path `machineConfig.eneableLog` in `_setupSpecificClass`.
|
||||
16
.agents/function-anchors/settler/ANCHOR-settler.html
Normal file
16
.agents/function-anchors/settler/ANCHOR-settler.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>settler Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>settler Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
29
.agents/function-anchors/settler/ANCHOR-settler.md
Normal file
29
.agents/function-anchors/settler/ANCHOR-settler.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# settler Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: settler
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/settler/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/settler
|
||||
- Node-RED wrapper: nodes/settler/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/settler/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/settler/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
15
.agents/function-anchors/settler/EVIDENCE-settler-tests.md
Normal file
15
.agents/function-anchors/settler/EVIDENCE-settler-tests.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# settler Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/settler/test/basic/*.test.js
|
||||
- nodes/settler/test/integration/*.test.js
|
||||
- nodes/settler/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/settler/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/settler/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/settler/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
16
.agents/function-anchors/valve/ANCHOR-valve.html
Normal file
16
.agents/function-anchors/valve/ANCHOR-valve.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>valve Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>valve Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
29
.agents/function-anchors/valve/ANCHOR-valve.md
Normal file
29
.agents/function-anchors/valve/ANCHOR-valve.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# valve Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: valve
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/valve/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/valve
|
||||
- Node-RED wrapper: nodes/valve/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/valve/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/valve/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
15
.agents/function-anchors/valve/EVIDENCE-valve-tests.md
Normal file
15
.agents/function-anchors/valve/EVIDENCE-valve-tests.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# valve Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/valve/test/basic/*.test.js
|
||||
- nodes/valve/test/integration/*.test.js
|
||||
- nodes/valve/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/valve/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/valve/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/valve/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
@@ -0,0 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>valveGroupControl Anchor</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 24px; background: #f7f8fa; color: #1f2937; }
|
||||
.card { background: #fff; border: 1px solid #d1d5db; border-radius: 8px; padding: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>valveGroupControl Function Anchor</h1>
|
||||
<div class="card">Baseline topology placeholder. Expand during functional changes.</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
# valveGroupControl Function Anchor (Preparation Baseline)
|
||||
|
||||
## 0) Connection Map (At a Glance)
|
||||
- Node type: valveGroupControl
|
||||
- Scope: baseline anchor scaffold to satisfy EVOLV required architecture.
|
||||
|
||||
## 1) Unit Table (Initial Baseline)
|
||||
| Signal/Field | Represents | Default Unit | Source of Truth | Produced By | Consumed By | Fallback/Degraded Behavior |
|
||||
|---|---|---|---|---|---|---|
|
||||
| TBD | TBD | TBD | nodes/valveGroupControl/src/* | TBD | TBD | TBD |
|
||||
|
||||
## 2) Class Identity
|
||||
- Runtime registration: nodes/valveGroupControl
|
||||
- Node-RED wrapper: nodes/valveGroupControl/src/nodeClass.js (when present)
|
||||
- Domain logic: nodes/valveGroupControl/src/specificClass.js (when present)
|
||||
- Editor UI: nodes/valveGroupControl/*.html (when present)
|
||||
|
||||
## 3) Current Gaps To Resolve Before Declaring Anchor Complete
|
||||
- Replace placeholder sections with full contract mapping on first functional change.
|
||||
|
||||
## 4) Standardization Plan
|
||||
1. Maintain this anchor and evidence docs with behavior changes.
|
||||
2. Maintain tests under test/basic, test/integration, test/edge.
|
||||
3. Maintain examples package (README, basic.flow.json, integration.flow.json, edge.flow.json).
|
||||
|
||||
## 5) Acceptance Criteria For Completion
|
||||
- Anchor/evidence artifacts exist.
|
||||
- Test structure exists.
|
||||
- Example structure exists.
|
||||
@@ -0,0 +1,15 @@
|
||||
# valveGroupControl Test Evidence
|
||||
|
||||
Status: baseline structure scaffolded.
|
||||
|
||||
## Required Test Layout
|
||||
- nodes/valveGroupControl/test/basic/*.test.js
|
||||
- nodes/valveGroupControl/test/integration/*.test.js
|
||||
- nodes/valveGroupControl/test/edge/*.test.js
|
||||
|
||||
## Baseline Mapping
|
||||
| Test file | Scope |
|
||||
|---|---|
|
||||
| nodes/valveGroupControl/test/basic/structure-module-load.basic.test.js | module load smoke |
|
||||
| nodes/valveGroupControl/test/integration/structure-examples.integration.test.js | examples package integrity |
|
||||
| nodes/valveGroupControl/test/edge/structure-examples-node-type.edge.test.js | node-type presence in basic example |
|
||||
Reference in New Issue
Block a user