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:
145
scripts/capture-process-data.js
Normal file
145
scripts/capture-process-data.js
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Capture live process data from Node-RED WebSocket debug sidebar.
|
||||
* Collects samples over a time window and analyzes trends.
|
||||
*/
|
||||
|
||||
const http = require('http');
|
||||
|
||||
const NR_URL = 'http://localhost:1880';
|
||||
const CAPTURE_SECONDS = 30;
|
||||
|
||||
// Alternative: poll the Node-RED comms endpoint
|
||||
// But let's use a simpler approach - inject a temporary catch-all debug and read context
|
||||
|
||||
async 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: ' + e.message)); }
|
||||
});
|
||||
}).on('error', reject);
|
||||
});
|
||||
}
|
||||
|
||||
async function postJSON(url, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const body = JSON.stringify(data);
|
||||
const parsed = new URL(url);
|
||||
const req = http.request({
|
||||
hostname: parsed.hostname,
|
||||
port: parsed.port,
|
||||
path: parsed.pathname,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(body),
|
||||
},
|
||||
}, res => {
|
||||
const chunks = [];
|
||||
res.on('data', c => chunks.push(c));
|
||||
res.on('end', () => {
|
||||
const text = Buffer.concat(chunks).toString();
|
||||
try { resolve(JSON.parse(text)); } catch { resolve(text); }
|
||||
});
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
console.log('=== Capturing Process Data (' + CAPTURE_SECONDS + 's) ===\n');
|
||||
|
||||
// Use Node-RED inject API to trigger debug output
|
||||
// Instead, let's read node context which stores the current state
|
||||
|
||||
// Get flows to find node IDs
|
||||
const flows = await fetchJSON(NR_URL + '/flows');
|
||||
const wwtp = flows.filter(n => n.z === 'demo_tab_wwtp');
|
||||
|
||||
// Pumping stations store state in node context
|
||||
const pss = wwtp.filter(n => n.type === 'pumpingStation');
|
||||
const pumps = wwtp.filter(n => n.type === 'rotatingMachine');
|
||||
|
||||
const samples = [];
|
||||
const startTime = Date.now();
|
||||
|
||||
console.log('Sampling every 3 seconds for ' + CAPTURE_SECONDS + 's...\n');
|
||||
|
||||
for (let i = 0; i < Math.ceil(CAPTURE_SECONDS / 3); i++) {
|
||||
const t = Date.now();
|
||||
const elapsed = ((t - startTime) / 1000).toFixed(1);
|
||||
|
||||
// Read PS context data via Node-RED context API
|
||||
const sample = { t: elapsed, stations: {} };
|
||||
|
||||
for (const ps of pss) {
|
||||
try {
|
||||
const ctx = await fetchJSON(NR_URL + '/context/node/' + ps.id + '?store=default');
|
||||
sample.stations[ps.id] = ctx;
|
||||
} catch (e) {
|
||||
sample.stations[ps.id] = { error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
for (const pump of pumps) {
|
||||
try {
|
||||
const ctx = await fetchJSON(NR_URL + '/context/node/' + pump.id + '?store=default');
|
||||
sample.stations[pump.id] = ctx;
|
||||
} catch (e) {
|
||||
sample.stations[pump.id] = { error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
samples.push(sample);
|
||||
|
||||
// Print summary for this sample
|
||||
console.log('--- Sample at t=' + elapsed + 's ---');
|
||||
for (const ps of pss) {
|
||||
const ctx = sample.stations[ps.id];
|
||||
if (ctx && ctx.data) {
|
||||
console.log(ps.name + ':');
|
||||
// Print all context keys
|
||||
Object.entries(ctx.data).forEach(([key, val]) => {
|
||||
if (typeof val === 'object') {
|
||||
console.log(' ' + key + ': ' + JSON.stringify(val).substring(0, 200));
|
||||
} else {
|
||||
console.log(' ' + key + ': ' + val);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log(ps.name + ': ' + JSON.stringify(ctx).substring(0, 200));
|
||||
}
|
||||
}
|
||||
|
||||
for (const pump of pumps) {
|
||||
const ctx = sample.stations[pump.id];
|
||||
if (ctx && ctx.data && Object.keys(ctx.data).length > 0) {
|
||||
console.log(pump.name + ':');
|
||||
Object.entries(ctx.data).forEach(([key, val]) => {
|
||||
if (typeof val === 'object') {
|
||||
console.log(' ' + key + ': ' + JSON.stringify(val).substring(0, 200));
|
||||
} else {
|
||||
console.log(' ' + key + ': ' + val);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
|
||||
if (i < Math.ceil(CAPTURE_SECONDS / 3) - 1) {
|
||||
await new Promise(r => setTimeout(r, 3000));
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== Summary ===');
|
||||
console.log('Collected ' + samples.length + ' samples over ' + CAPTURE_SECONDS + 's');
|
||||
})().catch(err => {
|
||||
console.error('Capture failed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user