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:
znetsixe
2026-03-04 21:07:04 +01:00
parent fbd9e6ec11
commit 6a6c04d34b
169 changed files with 21332 additions and 1512 deletions

View File

@@ -0,0 +1,219 @@
#!/usr/bin/env node
/**
* Step 2: Merge Collection Point
* - Adds link-out from each PS tab to merge on treatment tab
* - Creates link-in, tag, collect, and dashboard link-out nodes on treatment
* - Wires PS outputs through merge to feed reactor
*/
const fs = require('fs');
const path = require('path');
const FLOW_PATH = path.join(__dirname, '..', 'docker', 'demo-flow.json');
const flow = JSON.parse(fs.readFileSync(FLOW_PATH, 'utf8'));
const byId = (id) => flow.find(n => n.id === id);
// =============================================
// 2a. Link-out nodes on each PS tab
// =============================================
flow.push(
{
id: "demo_link_merge_west_out",
type: "link out",
z: "demo_tab_ps_west",
name: "→ Merge (West)",
mode: "link",
links: ["demo_link_merge_west_in"],
x: 1080, y: 360
},
{
id: "demo_link_merge_north_out",
type: "link out",
z: "demo_tab_ps_north",
name: "→ Merge (North)",
mode: "link",
links: ["demo_link_merge_north_in"],
x: 1080, y: 340
},
{
id: "demo_link_merge_south_out",
type: "link out",
z: "demo_tab_ps_south",
name: "→ Merge (South)",
mode: "link",
links: ["demo_link_merge_south_in"],
x: 1080, y: 340
}
);
// Add merge link-outs to each PS node's wires[0]
const psWest = byId("demo_ps_west");
psWest.wires[0].push("demo_link_merge_west_out");
const psNorth = byId("demo_ps_north");
psNorth.wires[0].push("demo_link_merge_north_out");
const psSouth = byId("demo_ps_south");
psSouth.wires[0].push("demo_link_merge_south_out");
// =============================================
// 2b. Merge nodes on Treatment tab
// =============================================
// Link-in nodes
flow.push(
{
id: "demo_link_merge_west_in",
type: "link in",
z: "demo_tab_treatment",
name: "← PS West",
links: ["demo_link_merge_west_out"],
x: 100, y: 920,
wires: [["demo_fn_tag_west"]]
},
{
id: "demo_link_merge_north_in",
type: "link in",
z: "demo_tab_treatment",
name: "← PS North",
links: ["demo_link_merge_north_out"],
x: 100, y: 980,
wires: [["demo_fn_tag_north"]]
},
{
id: "demo_link_merge_south_in",
type: "link in",
z: "demo_tab_treatment",
name: "← PS South",
links: ["demo_link_merge_south_out"],
x: 100, y: 1040,
wires: [["demo_fn_tag_south"]]
}
);
// Tag functions
flow.push(
{
id: "demo_fn_tag_west",
type: "function",
z: "demo_tab_treatment",
name: "Tag: west",
func: "msg._psSource = 'west';\nreturn msg;",
outputs: 1,
x: 280, y: 920,
wires: [["demo_fn_merge_collect"]]
},
{
id: "demo_fn_tag_north",
type: "function",
z: "demo_tab_treatment",
name: "Tag: north",
func: "msg._psSource = 'north';\nreturn msg;",
outputs: 1,
x: 280, y: 980,
wires: [["demo_fn_merge_collect"]]
},
{
id: "demo_fn_tag_south",
type: "function",
z: "demo_tab_treatment",
name: "Tag: south",
func: "msg._psSource = 'south';\nreturn msg;",
outputs: 1,
x: 280, y: 1040,
wires: [["demo_fn_merge_collect"]]
}
);
// Merge collect function
flow.push({
id: "demo_fn_merge_collect",
type: "function",
z: "demo_tab_treatment",
name: "Merge Collector",
func: `// Cache each PS output by _psSource tag, compute totals
const p = msg.payload || {};
const ps = msg._psSource;
const cache = flow.get('merge_cache') || { west: {}, north: {}, south: {} };
const keys = Object.keys(p);
const pick = (prefix) => { const k = keys.find(k => k.startsWith(prefix)); return k ? Number(p[k]) : null; };
if (ps && cache[ps]) {
const nf = pick('netFlowRate.predicted'); if (nf !== null) cache[ps].netFlow = nf;
const fp = pick('volumePercent.predicted'); if (fp !== null) cache[ps].fillPct = fp;
cache[ps].direction = p.direction || cache[ps].direction;
cache[ps].ts = Date.now();
}
flow.set('merge_cache', cache);
const totalFlow = (cache.west.netFlow||0) + (cache.north.netFlow||0) + (cache.south.netFlow||0);
const avgFill = ((cache.west.fillPct||0) + (cache.north.fillPct||0) + (cache.south.fillPct||0)) / 3;
return {
topic: 'merge_combined_influent',
payload: { totalInfluentFlow: +totalFlow.toFixed(1), avgFillPercent: +avgFill.toFixed(1),
west: cache.west, north: cache.north, south: cache.south }
};`,
outputs: 1,
x: 480, y: 980,
wires: [["demo_link_merge_dash"]]
});
// Dashboard link-out for merge data
flow.push({
id: "demo_link_merge_dash",
type: "link out",
z: "demo_tab_treatment",
name: "→ Merge Dashboard",
mode: "link",
links: ["demo_link_merge_dash_in"],
x: 680, y: 980
});
// Create a comment for the merge section
flow.push({
id: "demo_comment_merge",
type: "comment",
z: "demo_tab_treatment",
name: "=== MERGE COLLECTION POINT ===",
info: "Combines output from all 3 pumping stations",
x: 200, y: 880
});
// =============================================
// Validate
// =============================================
const allIds = new Set(flow.map(n => n.id));
let brokenWires = 0;
for (const n of flow) {
if (!n.wires) continue;
for (const port of n.wires) {
for (const target of port) {
if (!allIds.has(target)) {
console.warn(`BROKEN WIRE: ${n.id}${target}`);
brokenWires++;
}
}
}
}
for (const n of flow) {
if (n.type === 'link out' && n.links) {
for (const lt of n.links) {
if (!allIds.has(lt)) console.warn(`BROKEN LINK: ${n.id} links to missing ${lt}`);
}
}
if (n.type === 'link in' && n.links) {
for (const ls of n.links) {
if (!allIds.has(ls)) console.warn(`BROKEN LINK: ${n.id} expects link from missing ${ls}`);
}
}
}
if (brokenWires === 0) console.log('All wire references valid ✓');
console.log('Total nodes:', flow.length);
// Write
fs.writeFileSync(FLOW_PATH, JSON.stringify(flow, null, 2) + '\n');
console.log(`Wrote ${FLOW_PATH}`);