#!/usr/bin/env node /** * Script to update docker/demo-flow.json with Fixes 2-5 from the plan. * Run from project root: node scripts/update-demo-flow.js */ const fs = require('fs'); const path = require('path'); const flowPath = path.join(__dirname, '..', 'docker', 'demo-flow.json'); const flow = JSON.parse(fs.readFileSync(flowPath, 'utf8')); // === Fix 2: Enable simulator on 9 measurement nodes === 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' ]; simMeasIds.forEach(id => { const node = flow.find(n => n.id === id); if (node) { node.simulator = true; console.log('Enabled simulator on', id); } else { console.error('NOT FOUND:', id); } }); // === Fix 2: Remove 18 inject+function sim pairs === const removeSimIds = [ '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' ]; // === Fix 5: Remove manual pump startup/setpoint injectors === const removeManualIds = [ '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 allRemoveIds = new Set([...removeSimIds, ...removeManualIds]); const before = flow.length; const filtered = flow.filter(n => !allRemoveIds.has(n.id)); console.log(`Removed ${before - filtered.length} nodes (expected 24)`); // Remove wires to removed nodes from remaining nodes filtered.forEach(n => { if (n.wires && Array.isArray(n.wires)) { n.wires = n.wires.map(wireGroup => { if (Array.isArray(wireGroup)) { return wireGroup.filter(w => !allRemoveIds.has(w)); } return wireGroup; }); } }); // === Fix 3 (demo part): Add speedUpFactor to reactor === const reactor = filtered.find(n => n.id === 'demo_reactor'); if (reactor) { reactor.speedUpFactor = 1; console.log('Added speedUpFactor=1 to reactor'); } // === Fix 4: Add pressure measurement nodes === const maxY = Math.max(...filtered.filter(n => n.z === 'demo_tab_wwtp').map(n => n.y || 0)); const ptBaseConfig = { scaling: true, i_offset: 0, smooth_method: 'mean', count: 3, category: 'sensor', assetType: 'pressure', enableLog: false, logLevel: 'error', positionIcon: '', hasDistance: false }; // Function to extract level from PS output and convert to hydrostatic pressure const levelExtractFunc = [ '// Extract basin level from PS output and convert to hydrostatic pressure (mbar)', '// P = rho * g * h, rho=1000 kg/m3, g=9.81 m/s2', 'const p = msg.payload || {};', 'const keys = Object.keys(p);', 'const levelKey = keys.find(k => k.startsWith("level.predicted.atequipment") || k.startsWith("level.measured.atequipment"));', 'if (!levelKey) return null;', 'const h = Number(p[levelKey]);', 'if (!Number.isFinite(h)) return null;', 'msg.topic = "measurement";', 'msg.payload = Math.round(h * 98.1 * 10) / 10; // mbar', 'return msg;' ].join('\n'); const newNodes = [ // Comment { id: 'demo_comment_pressure', type: 'comment', z: 'demo_tab_wwtp', name: '=== PRESSURE MEASUREMENTS (per pumping station) ===', info: '', x: 320, y: maxY + 40 }, // --- PS West upstream PT --- { id: 'demo_fn_level_to_pressure_w', type: 'function', z: 'demo_tab_wwtp', name: 'Level\u2192Pressure (West)', func: levelExtractFunc, outputs: 1, x: 370, y: maxY + 80, wires: [['demo_meas_pt_w_up']] }, { id: 'demo_meas_pt_w_up', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-W-UP (West Upstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: false, uuid: 'pt-w-up-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-W-UP', positionVsParent: 'upstream', x: 580, y: maxY + 80, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_w1', 'demo_pump_w2']] }, // PS West downstream PT (simulated) { id: 'demo_meas_pt_w_down', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-W-DN (West Downstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: true, uuid: 'pt-w-dn-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-W-DN', positionVsParent: 'downstream', x: 580, y: maxY + 140, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_w1', 'demo_pump_w2']] }, // --- PS North upstream PT --- { id: 'demo_fn_level_to_pressure_n', type: 'function', z: 'demo_tab_wwtp', name: 'Level\u2192Pressure (North)', func: levelExtractFunc, outputs: 1, x: 370, y: maxY + 220, wires: [['demo_meas_pt_n_up']] }, { id: 'demo_meas_pt_n_up', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-N-UP (North Upstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: false, uuid: 'pt-n-up-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-N-UP', positionVsParent: 'upstream', x: 580, y: maxY + 220, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_n1']] }, { id: 'demo_meas_pt_n_down', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-N-DN (North Downstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: true, uuid: 'pt-n-dn-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-N-DN', positionVsParent: 'downstream', x: 580, y: maxY + 280, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_n1']] }, // --- PS South upstream PT --- { id: 'demo_fn_level_to_pressure_s', type: 'function', z: 'demo_tab_wwtp', name: 'Level\u2192Pressure (South)', func: levelExtractFunc, outputs: 1, x: 370, y: maxY + 360, wires: [['demo_meas_pt_s_up']] }, { id: 'demo_meas_pt_s_up', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-S-UP (South Upstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: false, uuid: 'pt-s-up-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-S-UP', positionVsParent: 'upstream', x: 580, y: maxY + 360, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_s1']] }, { id: 'demo_meas_pt_s_down', type: 'measurement', z: 'demo_tab_wwtp', name: 'PT-S-DN (South Downstream)', ...ptBaseConfig, i_min: 0, i_max: 5000, o_min: 0, o_max: 5000, simulator: true, uuid: 'pt-s-dn-001', supplier: 'Endress+Hauser', model: 'Cerabar-PMC51', unit: 'mbar', assetTagNumber: 'PT-S-DN', positionVsParent: 'downstream', x: 580, y: maxY + 420, wires: [['demo_link_process_out'], ['demo_link_influx_out'], ['demo_pump_s1']] } ]; // Wire PS output port 0 to the level-to-pressure function nodes const psWest = filtered.find(n => n.id === 'demo_ps_west'); const psNorth = filtered.find(n => n.id === 'demo_ps_north'); const psSouth = filtered.find(n => n.id === 'demo_ps_south'); if (psWest && psWest.wires[0]) psWest.wires[0].push('demo_fn_level_to_pressure_w'); if (psNorth && psNorth.wires[0]) psNorth.wires[0].push('demo_fn_level_to_pressure_n'); if (psSouth && psSouth.wires[0]) psSouth.wires[0].push('demo_fn_level_to_pressure_s'); // Combine and write const result = [...filtered, ...newNodes]; console.log(`Final flow has ${result.length} nodes`); fs.writeFileSync(flowPath, JSON.stringify(result, null, 2) + '\n'); console.log('Done! Written to docker/demo-flow.json');