#!/usr/bin/env node /** * Check the deployed Node-RED flow for correctness after changes. */ const http = require('http'); function fetch(url) { return new Promise((resolve, reject) => { http.get(url, res => { const chunks = []; res.on('data', c => chunks.push(c)); res.on('end', () => resolve(JSON.parse(Buffer.concat(chunks)))); }).on('error', reject); }); } (async () => { let errors = 0; // 1. Check deployed flow structure console.log('=== Checking deployed flow structure ==='); const flow = await fetch('http://localhost:1880/flows'); console.log('Total deployed nodes:', flow.length); // Check MGC exists const mgc = flow.find(n => n.id === 'demo_mgc_west'); if (mgc) { console.log('PASS: MGC West exists, position:', mgc.positionVsParent); } else { console.log('FAIL: MGC West missing from deployed flow'); errors++; } // Check reactor speedUpFactor const reactor = flow.find(n => n.id === 'demo_reactor'); if (reactor && reactor.speedUpFactor === 1) { console.log('PASS: Reactor speedUpFactor = 1'); } else { console.log('FAIL: Reactor speedUpFactor =', reactor?.speedUpFactor); errors++; } // Check sim mode on measurements const simMeasIds = [ 'demo_meas_flow', 'demo_meas_do', 'demo_meas_nh4', 'demo_meas_ft_n1', 'demo_meas_eff_flow', 'demo_meas_eff_do', 'demo_meas_eff_nh4', 'demo_meas_eff_no3', 'demo_meas_eff_tss' ]; let simOk = 0; simMeasIds.forEach(id => { const n = flow.find(x => x.id === id); if (n && n.simulator === true) simOk++; else { console.log('FAIL: simulator not true on', id); errors++; } }); console.log(`PASS: ${simOk}/9 measurement nodes have simulator=true`); // Check pressure nodes exist const ptIds = ['demo_meas_pt_w_up','demo_meas_pt_w_down','demo_meas_pt_n_up','demo_meas_pt_n_down','demo_meas_pt_s_up','demo_meas_pt_s_down']; let ptOk = 0; ptIds.forEach(id => { const n = flow.find(x => x.id === id); if (n && n.type === 'measurement') ptOk++; else { console.log('FAIL: pressure node missing:', id); errors++; } }); console.log(`PASS: ${ptOk}/6 pressure measurement nodes present`); // Check removed nodes are gone const removedIds = [ 'demo_inj_meas_flow', 'demo_fn_sim_flow', 'demo_inj_meas_do', 'demo_fn_sim_do', 'demo_inj_meas_nh4', 'demo_fn_sim_nh4', 'demo_inj_ft_n1', 'demo_fn_sim_ft_n1', 'demo_inj_eff_flow', 'demo_fn_sim_eff_flow', 'demo_inj_eff_do', 'demo_fn_sim_eff_do', 'demo_inj_eff_nh4', 'demo_fn_sim_eff_nh4', 'demo_inj_eff_no3', 'demo_fn_sim_eff_no3', 'demo_inj_eff_tss', 'demo_fn_sim_eff_tss', 'demo_inj_w1_startup', 'demo_inj_w1_setpoint', 'demo_inj_w2_startup', 'demo_inj_w2_setpoint', 'demo_inj_n1_startup', 'demo_inj_s1_startup' ]; const stillPresent = removedIds.filter(id => flow.find(x => x.id === id)); if (stillPresent.length === 0) { console.log('PASS: All 24 removed nodes are gone'); } else { console.log('FAIL: These removed nodes are still present:', stillPresent); errors++; } // Check kept nodes still exist const keptIds = [ 'demo_inj_west_flow', 'demo_fn_west_flow_sim', 'demo_inj_north_flow', 'demo_fn_north_flow_sim', 'demo_inj_south_flow', 'demo_fn_south_flow_sim', 'demo_inj_w1_mode', 'demo_inj_w2_mode', 'demo_inj_n1_mode', 'demo_inj_s1_mode', 'demo_inj_west_mode', 'demo_inj_north_mode', 'demo_inj_south_mode' ]; const keptMissing = keptIds.filter(id => !flow.find(x => x.id === id)); if (keptMissing.length === 0) { console.log('PASS: All kept nodes still present'); } else { console.log('FAIL: These nodes should exist but are missing:', keptMissing); errors++; } // Check wiring: W1/W2 register to MGC, MGC registers to PS West const w1 = flow.find(n => n.id === 'demo_pump_w1'); const w2 = flow.find(n => n.id === 'demo_pump_w2'); if (w1 && w1.wires[2] && w1.wires[2].includes('demo_mgc_west')) { console.log('PASS: W1 port 2 wired to MGC'); } else { console.log('FAIL: W1 port 2 not wired to MGC, got:', w1?.wires?.[2]); errors++; } if (w2 && w2.wires[2] && w2.wires[2].includes('demo_mgc_west')) { console.log('PASS: W2 port 2 wired to MGC'); } else { console.log('FAIL: W2 port 2 not wired to MGC, got:', w2?.wires?.[2]); errors++; } if (mgc && mgc.wires[2] && mgc.wires[2].includes('demo_ps_west')) { console.log('PASS: MGC port 2 wired to PS West'); } else { console.log('FAIL: MGC port 2 not wired to PS West'); errors++; } // Check PS outputs wire to level-to-pressure functions const psWest = flow.find(n => n.id === 'demo_ps_west'); if (psWest && psWest.wires[0] && psWest.wires[0].includes('demo_fn_level_to_pressure_w')) { console.log('PASS: PS West port 0 wired to level-to-pressure function'); } else { console.log('FAIL: PS West port 0 missing level-to-pressure wire'); errors++; } console.log('\n=== RESULT ==='); if (errors === 0) { console.log('ALL CHECKS PASSED'); } else { console.log(`${errors} FAILURE(S)`); process.exit(1); } })().catch(err => { console.error('Failed to connect to Node-RED:', err.message); process.exit(1); });