Files
EVOLV/scripts/analyze-runtime.js
znetsixe 6a6c04d34b 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>
2026-03-04 21:07:04 +01:00

139 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Comprehensive runtime analysis of the WWTP demo flow.
* Captures process debug output, pumping station state, measurements,
* and analyzes filling/draining behavior over time.
*/
const http = require('http');
const NR_URL = 'http://localhost:1880';
function fetchJSON(url) {
return new Promise((resolve, reject) => {
http.get(url, res => {
const chunks = [];
res.on('data', c => chunks.push(c));
res.on('end', () => {
try { resolve(JSON.parse(Buffer.concat(chunks))); }
catch (e) { reject(new Error('Parse error from ' + url + ': ' + e.message)); }
});
}).on('error', reject);
});
}
// Inject a debug-capture subflow to intercept process messages
async function injectDebugCapture() {
const flows = await fetchJSON(NR_URL + '/flows');
// Find all nodes on WWTP tab
const wwtp = flows.filter(n => n.z === 'demo_tab_wwtp');
console.log('=== WWTP Node Inventory ===');
const byType = {};
wwtp.forEach(n => {
if (!byType[n.type]) byType[n.type] = [];
byType[n.type].push(n);
});
Object.entries(byType).sort().forEach(([type, nodes]) => {
console.log(type + ' (' + nodes.length + '):');
nodes.forEach(n => {
const extra = [];
if (n.simulator) extra.push('sim=ON');
if (n.model) extra.push('model=' + n.model);
if (n.basinVolume) extra.push('basin=' + n.basinVolume + 'm3');
if (n.basinHeight) extra.push('h=' + n.basinHeight + 'm');
if (n.positionVsParent) extra.push('pos=' + n.positionVsParent);
if (n.control) extra.push('ctrl=' + JSON.stringify(n.control));
console.log(' ' + n.id + ' "' + (n.name || '') + '" ' + (extra.length ? '[' + extra.join(', ') + ']' : ''));
});
});
// Analyze pumping station configurations
console.log('\n=== Pumping Station Configs ===');
const pss = wwtp.filter(n => n.type === 'pumpingStation');
pss.forEach(ps => {
console.log('\n' + ps.id + ' "' + ps.name + '"');
console.log(' Basin: vol=' + ps.basinVolume + 'm3, h=' + ps.basinHeight + 'm');
console.log(' Inlet: h=' + ps.heightInlet + 'm, Outlet: h=' + ps.heightOutlet + 'm');
console.log(' Simulator: ' + ps.simulator);
console.log(' Control mode: ' + (ps.controlMode || 'not set'));
// Check q_in inject wiring
const qinInject = wwtp.find(n => n.id === 'demo_inj_' + ps.id.replace('demo_ps_', '') + '_flow');
if (qinInject) {
console.log(' q_in inject: repeat=' + qinInject.repeat + 's, wired to ' + JSON.stringify(qinInject.wires));
}
// Check what's wired to this PS (port 2 = parent registration)
const children = wwtp.filter(n => {
if (!n.wires) return false;
return n.wires.some(portWires => portWires && portWires.includes(ps.id));
});
console.log(' Children wired to it: ' + children.map(c => c.id + '(' + c.type + ')').join(', '));
});
// Analyze inject timers
console.log('\n=== Active Inject Timers ===');
const injects = wwtp.filter(n => n.type === 'inject');
injects.forEach(inj => {
const targets = (inj.wires || []).flat();
console.log(inj.id + ' "' + (inj.name || '') + '"');
console.log(' topic=' + inj.topic + ' payload=' + inj.payload);
console.log(' once=' + inj.once + ' repeat=' + (inj.repeat || 'none'));
console.log(' → ' + targets.join(', '));
});
// Analyze q_in function nodes
console.log('\n=== q_in Flow Simulation Functions ===');
const fnNodes = wwtp.filter(n => n.type === 'function' && n.name && n.name.includes('Flow'));
fnNodes.forEach(fn => {
console.log(fn.id + ' "' + fn.name + '"');
console.log(' func: ' + (fn.func || '').substring(0, 200));
const targets = (fn.wires || []).flat();
console.log(' → ' + targets.join(', '));
});
// Analyze measurement nodes
console.log('\n=== Measurement Nodes ===');
const meas = wwtp.filter(n => n.type === 'measurement');
meas.forEach(m => {
console.log(m.id + ' "' + (m.name || '') + '"');
console.log(' type=' + m.assetType + ' sim=' + m.simulator + ' range=[' + m.o_min + ',' + m.o_max + '] unit=' + m.unit);
console.log(' pos=' + (m.positionVsParent || 'none'));
// Check port 2 wiring (parent registration)
const port2 = m.wires && m.wires[2] ? m.wires[2] : [];
console.log(' port2→ ' + (port2.length ? port2.join(', ') : 'none'));
});
// Analyze rotating machines
console.log('\n=== Rotating Machine Nodes ===');
const machines = wwtp.filter(n => n.type === 'rotatingMachine');
machines.forEach(m => {
console.log(m.id + ' "' + (m.name || '') + '"');
console.log(' model=' + m.model + ' mode=' + m.movementMode);
console.log(' pos=' + m.positionVsParent + ' supplier=' + m.supplier);
console.log(' speed=' + m.speed + ' startup=' + m.startup + ' shutdown=' + m.shutdown);
const port2 = m.wires && m.wires[2] ? m.wires[2] : [];
console.log(' port2→ ' + (port2.length ? port2.join(', ') : 'none'));
});
// Check wiring integrity
console.log('\n=== Wiring Analysis ===');
pss.forEach(ps => {
const psPort0 = ps.wires && ps.wires[0] ? ps.wires[0] : [];
const psPort1 = ps.wires && ps.wires[1] ? ps.wires[1] : [];
const psPort2 = ps.wires && ps.wires[2] ? ps.wires[2] : [];
console.log(ps.id + ' wiring:');
console.log(' port0 (process): ' + psPort0.join(', '));
console.log(' port1 (influx): ' + psPort1.join(', '));
console.log(' port2 (parent): ' + psPort2.join(', '));
});
}
injectDebugCapture().catch(err => {
console.error('Analysis failed:', err);
process.exit(1);
});