Add vertical branch handles and seed architecture documents from diagrams

Branch handles: Added all 6 directions (→ ← ↑ ↓ ↘ ↗) so an entire
metro map can grow from a single starting node. Cardinal directions
(0/90/180/270°) extend the same line, diagonals (45/315°) fork to
new tracks.

Documents seeded from 6 architecture diagrams:
- EVOLV Digital Twin Hierarchy (ISA-88 tracks) → Gemaal 3.0
- Pumping Station dependency chain → Gemaal 3.0
- PLC/EDGE VLAN network architecture → BRIDGE
- R&D Stack topology (Cloud/EDGE/OT) → BRIDGE
- CoreSync network setup → BRIDGE
- R&D Timeline gitflow tracks → Governance

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-04-08 12:01:30 +02:00
parent 22cbc26b75
commit 6fdeb1b5e2
8 changed files with 121 additions and 5 deletions

View File

@@ -456,6 +456,119 @@ class DatabaseSeeder extends Seeder
'auteur_id' => $rene->id, 'auteur_id' => $rene->id,
]); ]);
// --- Documents extracted from architecture diagrams (raw/*.drawio.png) ---
// EVOLV-Tracks-Maintracks: full ISA-88 node hierarchy
Document::create([
'project_id' => $gemaal->id,
'titel' => 'EVOLV Digital Twin Hiërarchie (ISA-88 Tracks)',
'type' => 'technisch_ontwerp',
'inhoud' => "EVOLV node-hiërarchie conform ISA-88 (S88) batch control standaard.\n\n"
. "**Control Module**: measurement, diffuser, healthCompact, filterController\n"
. "**Equipment**: Rotating Machine, sump (gepland)\n"
. "**Unit**: machineGroupControl, valve, hydraulic network (gepland)\n"
. "**Process Cell**: Pumping Station\n"
. "**Area**: Sewage Pumping Group Control\n"
. "**Complex models**: Influent Prediction\n"
. "**Utilities**: generalFunctions, coreSync, convert, dashboardAPI\n"
. "**Data manipulation**: onderste laag\n\n"
. "Verticale tracks per asset-keten. Horizontale vertakkingen per ISA-88 niveau. "
. "Stippellijncirkels = geplande maar nog niet gebouwde nodes. "
. "Bron: EVOLV-Tracks-Maintracks.drawio.png",
'versie' => 1,
'auteur_id' => $rene->id,
]);
// EVOLV-Tracks-PumpingStation: vertical dependency chain
Document::create([
'project_id' => $gemaal->id,
'titel' => 'Pumping Station Afhankelijkheidsketen',
'type' => 'technisch_ontwerp',
'inhoud' => "Verticale afhankelijkheidsketen voor de Pumping Station track:\n\n"
. "measurement (Control Module) → Rotating Machine (Equipment) → "
. "machineGroupControl (Unit) → Sewage Pumping Station (Process Cell) → "
. "Sewage Pumping Group Control (Area) → Influent Prediction (Complex) → "
. "generalFunctions + coreSync + convert + dashboardAPI (Utilities)\n\n"
. "Elke laag is afhankelijk van de laag erboven. Equipment-nodes die als stippellijn staan "
. "(sump) zijn gepland maar nog niet ontwikkeld.\n"
. "Bron: EVOLV-Tracks-PumpingStation.drawio.png",
'versie' => 1,
'auteur_id' => $rene->id,
]);
// PLCEdge: network architecture VLAN separation
Document::create([
'project_id' => $bridge->id,
'titel' => 'PLC/EDGE Netwerk Architectuur (VLAN-scheiding)',
'type' => 'technisch_ontwerp',
'inhoud' => "Netwerkarchitectuur voor de BRIDGE pilot met VLAN-scheiding:\n\n"
. "**Extern Serverpark** (Internet): Digital Twin + Integratielaag op Edem expert system\n"
. "**Bouvigne Serverpark** (VLAN 1): Linux Server met Digital Twin + Integratielaag\n"
. "**On-site Gemaal/RWZI** (VLAN 2 + 3):\n"
. " - VLAN 3: Linux EDGE met Digital Twin + Integratielaag\n"
. " - VLAN 2: Siemens 1500 PLC (Typical) + Remote IO modules → Pomp\n\n"
. "Communicatie: HTTPS tussen extern en Bouvigne, tunnel naar EDGE, OPC UA naar PLC.\n"
. "Bron: PLCEdge-Page-1.drawio.png",
'versie' => 2,
'auteur_id' => $pim->id,
]);
// Stack: full R&D software stack
Document::create([
'project_id' => $bridge->id,
'titel' => 'R&D Stack Topologie (Cloud / EDGE / OT)',
'type' => 'technisch_ontwerp',
'inhoud' => "Drielaagse R&D stack architectuur:\n\n"
. "**Cloud/Central Layer**: nginx-proxy, Nodered, Portainer, SSL CertResolver, "
. "jenkins, influx-db, Grafana, Mosquitto, MQTT Broker, Fluffle (MQTT OUT ONLY broker), "
. "ESL dit (single port only)\n\n"
. "**EDGE Layer**: Mosquitto/VPN bridge, Nodered, Portainer, influx-db, "
. "Grafana, CoreSync, Mosquitto, MQTT SX\n\n"
. "**OT Layer**: OPCua Server → PLC\n\n"
. "Tunnel-verbinding tussen Cloud en EDGE. OPC UA van EDGE naar OT.\n"
. "Bron: Stack.drawio (2).png",
'versie' => 1,
'auteur_id' => $rene->id,
]);
// CoreSync: local/central sync topology
Document::create([
'project_id' => $bridge->id,
'titel' => 'CoreSync Netwerk Setup (Single Level)',
'type' => 'technisch_ontwerp',
'inhoud' => "CoreSync single-level configuratie:\n\n"
. "**R&D lokaal ZRG (LVL 1)**: Apparaat 1 + Apparaat 2 met measurement nodes, "
. "verbonden via Apparaat 3 (Node-Red container) met GR Bridge flows\n\n"
. "**R&D Centraal (LVL 1)**: Apparaat 4 (Node-Red container) met GR Bridge flows, "
. "verbonden via HTTPS naar lokaal\n\n"
. "Notitie: Elk netwerk dat UR, DR en 1RG bijv de edges zijn dit simpel. "
. "Ga through AZURE. Per net tekent voor edge = 1 à 1 b/u to net. "
. "PI flow at Bouvigne.\n"
. "Bron: CoreSync-Single level.drawio.png",
'versie' => 1,
'auteur_id' => $pim->id,
]);
// Complex gitflow: R&D timeline with branching tracks
Document::create([
'project_id' => $governance->id,
'titel' => 'R&D Tijdlijn & Releasebeleid (Gitflow)',
'type' => 'projectplan',
'inhoud' => "R&D release-tijdlijn als metro-tracks (gitflow model):\n\n"
. "**Tracks**: Bestuurlijk, Hotfix, Release, Release Fixes, Studenten, Yellowchess, Budget\n\n"
. "**Tijdlijn**:\n"
. "- 7/'21: R&D start PRO\n"
. "- 8/'21: Introductie met minor avans\n"
. "- 9/'21: Start minor PA avans, Opbouw partytent\n"
. "- 8/'21: Budget 40k\n"
. "- Pilot v0.1 → Verhuizing loods hardenberg → Pilot v0.2\n"
. "- Release 2.0 → 2.1\n\n"
. "Vertakkingen tonen hoe R&D-werk splitst en samenkomt.\n"
. "Bron: EVOLV-Tracks-complex gitflow.drawio.png",
'versie' => 1,
'auteur_id' => $rene->id,
]);
// ────────────────────────────────────────────────────────────── // ──────────────────────────────────────────────────────────────
// 9. Dependencies between projects // 9. Dependencies between projects
// ────────────────────────────────────────────────────────────── // ──────────────────────────────────────────────────────────────

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

View File

@@ -505,9 +505,12 @@ const renderBranchHandles = (nodeGroup, node, allNodes) => {
const HANDLE_R = 9 // handle circle radius const HANDLE_R = 9 // handle circle radius
const branches = [ const branches = [
{ deg: 0, dx: GRID_STEP_X, dy: 0, label: '→' }, { deg: 0, dx: GRID_STEP_X, dy: 0, label: '→' }, // extend right
{ deg: 45, dx: GRID_STEP_X, dy: GRID_STEP_Y, label: '' }, { deg: 90, dx: 0, dy: GRID_STEP_Y, label: '' }, // extend down
{ deg: 315, dx: GRID_STEP_X, dy: -GRID_STEP_Y, label: '' }, { deg: 180, dx: -GRID_STEP_X, dy: 0, label: '' }, // extend left
{ deg: 270, dx: 0, dy: -GRID_STEP_Y, label: '↑' }, // extend up
{ deg: 45, dx: GRID_STEP_X, dy: GRID_STEP_Y, label: '↘' }, // fork down-right
{ deg: 315, dx: GRID_STEP_X, dy: -GRID_STEP_Y, label: '↗' }, // fork up-right
] ]
branches.forEach(b => { branches.forEach(b => {
@@ -582,7 +585,7 @@ const renderBranchHandles = (nodeGroup, node, allNodes) => {
.attr('font-family', "'VT323', monospace") .attr('font-family', "'VT323', monospace")
.attr('font-size', '12px') .attr('font-size', '12px')
.attr('opacity', 0.5) .attr('opacity', 0.5)
.text(occupied ? 'interchange' : (b.deg === 0 ? 'extend' : 'fork')) .text(occupied ? 'interchange' : ([0, 90, 180, 270].includes(b.deg) ? 'extend' : 'fork'))
}) })
.on('mouseleave', () => { .on('mouseleave', () => {
handleGroup.selectAll('.ghost-preview').remove() handleGroup.selectAll('.ghost-preview').remove()
@@ -592,7 +595,7 @@ const renderBranchHandles = (nodeGroup, node, allNodes) => {
emit('create-node', { emit('create-node', {
x: targetX, x: targetX,
y: targetY, y: targetY,
lineId: b.deg === 0 ? node.lineId : null, // null = new track lineId: [0, 90, 180, 270].includes(b.deg) ? node.lineId : null, // cardinal = same line, diagonal = new track
afterNodeId: node.id, afterNodeId: node.id,
branchAngle: b.deg, branchAngle: b.deg,
parentNodeId: currentDimensionData.value?.parentNodeId ?? null, parentNodeId: currentDimensionData.value?.parentNodeId ?? null,