29 Commits

Author SHA1 Message Date
4b90827c2a color update 2025-12-08 08:42:38 +00:00
61277350d8 data update \\ properties unnecessary data removal + translation 2025-12-08 08:27:04 +00:00
7af3fb558b Update branches to include 'main' for workflow triggers 2025-12-08 00:11:32 +00:00
a360dc708e remove testing log file to clean up repository 2025-12-08 00:08:11 +00:00
3250d5a433 Merge pull request #39 from davidalves04/dev
dev merge
2025-12-07 23:58:33 +00:00
65cb0b52f6 Merge branch 'main' into dev 2025-12-07 23:55:33 +00:00
87a8a1bcb6 Merge pull request #38 from davidalves04/cleanup
Cleanup
2025-12-07 23:23:04 +00:00
bce15fe90f Merge branch 'dev' into cleanup 2025-12-07 23:21:44 +00:00
542ce9c8c0 Merge pull request #37 from davidalves04/javadoc
Javadoc
2025-12-07 23:18:41 +00:00
926245986f finish 1st javadoc round 2025-12-07 22:39:21 +00:00
3a3756f701 Translate graph labels and titles to PT 2025-12-07 20:12:43 +00:00
83c3d65e38 more javadoc - dashboard, des, logging 2025-12-07 19:57:40 +00:00
a8ce95e08c Refactor and enhance documentation across multiple classes (Analysis through DashboardStatistics) 2025-12-07 19:33:40 +00:00
Gaa56
b624cfe11e Documentation 2025-12-07 15:51:50 +00:00
David Alves
8fe4e564d3 Update Diagrama de arquitetura - SD.jpg 2025-12-04 22:27:01 +00:00
David Alves
e389c0711e Updated architecture diagram image 2025-12-04 22:25:55 +00:00
1b6ad03057 Merge pull request #35 from davidalves04/cleanup
Refactor Simulation Core & Enhance Dashboard UI
2025-11-27 20:20:34 +00:00
David Alves
24fe1c1d67 Update Diagrama de arquitetura - SD.drawio 2025-11-27 19:54:14 +00:00
David Alves
766eabbbe4 Update .gitignore 2025-11-27 15:49:03 +00:00
David Alves
d7b1de1fe3 Update Diagrama de arquitetura - SD.drawio 2025-11-27 15:47:30 +00:00
David Alves
96b3a66b96 Diagram update
@0x1eo @Gaa56 o que acham do diagrama? Mudariam/acrescentariam algo? As legendas, parecem-vos bem?
2025-11-27 15:46:02 +00:00
David Alves
29848b04a6 Update architecture diagram 2025-11-23 23:16:08 +00:00
043ba7d185 Add workflow_dispatch trigger to Maven CI 2025-11-23 22:12:35 +00:00
25f2876c34 Refactor GitHub Actions workflow for Maven build 2025-11-23 22:10:40 +00:00
7cbecc4fab Update Maven workflow for JDK setup and packaging 2025-11-23 22:00:49 +00:00
72db59415f Add Windows build job to Maven workflow 2025-11-23 21:53:33 +00:00
60b4f0c2b6 Add 'cleanup' branch to Maven CI workflow 2025-11-22 22:53:54 +00:00
81f842e2bb Change CI branch from 'main' to 'dev' 2025-11-19 20:54:47 +00:00
David Alves
108d2e544c Enunciado uploaded 2025-10-27 18:02:24 +00:00
67 changed files with 3892 additions and 3362 deletions

View File

@@ -0,0 +1,110 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.0.3 Chrome/140.0.7339.249 Electron/38.7.0 Safari/537.36" version="29.0.3">
<diagram name="Arquitetura SD" id="QKeTeUWuUs8JeLsq44d-">
<mxGraphModel dx="1426" dy="841" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="0K4eb2koB2xQ8duQ1-_a-1" value="&lt;b&gt;CoordinatorProcess&lt;/b&gt;&lt;br&gt;(Cliente Socket)&lt;hr&gt;• VehicleGenerator&lt;br&gt;• Modelo Poisson (λ=0.5)&lt;br&gt;• Liga a Cr1-Cr5" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="560" y="40" width="240" height="100" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-2" value="&lt;b&gt;Cr1&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8001&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8001)&lt;br&gt;• Thread Semáforo Sul&lt;br&gt;• Thread Semáforo Este&lt;br&gt;• Thread Semáforo Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="280" y="200" width="180" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-3" value="&lt;b&gt;Cr2&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8002&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8002)&lt;br&gt;• Thread Semáforo Sul&lt;br&gt;• Thread Semáforo Este&lt;br&gt;• Thread Semáforo Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="590" y="190" width="180" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-5" value="&lt;b&gt;Cr4&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8004&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8004)&lt;br&gt;• Thread Semáforo Sul&lt;br&gt;• Thread Semáforo Este&lt;br&gt;• Thread Semáforo Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="440" y="530" width="180" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-6" value="&lt;b&gt;Cr5&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8005&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8005)&lt;br&gt;• Thread Semáforo Sul&lt;br&gt;• Thread Semáforo Este&lt;br&gt;• Thread Semáforo Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="910" y="430" width="180" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-7" value="&lt;b&gt;ExitNode (S)&lt;/b&gt;&lt;br&gt;Porta: 9001&lt;br&gt;Servidor Socket&lt;hr&gt;• Recebe veículos finais&lt;br&gt;• Calcula estatísticas:&lt;br&gt; - Tempo no sistema&lt;br&gt; - Tempo de espera&lt;br&gt; - Métricas por tipo&lt;br&gt;• Envia para Dashboard" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="727" y="810" width="200" height="170" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-8" value="&lt;b&gt;DashboardServer&lt;/b&gt;&lt;br&gt;Porta: 9000&lt;br&gt;Servidor Socket&lt;hr&gt;• Thread Pool (10 threads)&lt;br&gt;• ConcurrentHashMap&lt;br&gt;• Agrega estatísticas&lt;br&gt;• Display a cada 5s:&lt;br&gt; - Throughput&lt;br&gt; - Tempos médios&lt;br&gt; - Tamanhos de filas" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="1210" y="585" width="200" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-9" value="VEHICLE_SPAWN&lt;br&gt;(Vehicle)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-2" edge="1">
<mxGeometry x="-0.2105" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-10" value="VEHICLE_SPAWN&lt;br&gt;(Vehicle)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-11" value="VEHICLE_SPAWN&lt;br&gt;(Vehicle)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-12" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-13" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-3" target="0K4eb2koB2xQ8duQ1-_a-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-14" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-5" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-15" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-3" target="0K4eb2koB2xQ8duQ1-_a-5" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-16" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-5" target="0K4eb2koB2xQ8duQ1-_a-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-17" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=none;startFill=0;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-4" target="0K4eb2koB2xQ8duQ1-_a-6" edge="1">
<mxGeometry x="0.3659" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-18" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#FF6600;strokeWidth=2;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-5" target="0K4eb2koB2xQ8duQ1-_a-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-19" value="VEHICLE_TRANSFER" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#FF6600;strokeWidth=2;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-6" target="0K4eb2koB2xQ8duQ1-_a-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-20" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-21" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-4" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-22" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-4" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-23" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-5" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-24" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-6" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-25" value="STATS_UPDATE&lt;br&gt;(periódico 5s)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-26" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-27" value="&lt;b&gt;MessageProtocol&lt;/b&gt;&lt;hr&gt;interface:&lt;br&gt;• getType()&lt;br&gt;• getPayload()&lt;br&gt;• getSourceNode()&lt;br&gt;• getDestinationNode()" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#fff2cc;strokeColor=#d6b656;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="30" y="30" width="180" height="120" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-28" value="&lt;b&gt;Tipos de Mensagens&lt;/b&gt;&lt;hr&gt;• VEHICLE_TRANSFER&lt;br&gt;• VEHICLE_SPAWN&lt;br&gt;• STATS_UPDATE&lt;br&gt;• TRAFFIC_LIGHT_SYNC&lt;br&gt;• HEARTBEAT" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#fff2cc;strokeColor=#d6b656;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="20" y="170" width="200" height="120" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-29" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-3" target="0K4eb2koB2xQ8duQ1-_a-4" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="480" y="280" as="sourcePoint" />
<mxPoint x="990" y="440" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-4" value="&lt;b&gt;Cr3&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8003&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8003)&lt;br&gt;• Thread Semáforo Sul&lt;br&gt;• Thread Semáforo Este&lt;br&gt;• Thread Semáforo Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="910" y="200" width="180" height="160" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-30" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="1" source="0K4eb2koB2xQ8duQ1-_a-7" target="0K4eb2koB2xQ8duQ1-_a-26" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="500" y="710" as="sourcePoint" />
<mxPoint x="1090" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-26" value="&lt;b&gt;LEGENDA&lt;/b&gt;&lt;hr&gt;━━━► Comunicação síncrona&lt;br&gt;╌╌╌► Comunicação periódica&lt;br&gt;&lt;br&gt;&lt;b&gt;Cores:&lt;/b&gt;&lt;br&gt;🔵 Azul = Geração&lt;br&gt;🟢 Verde = Transferência&lt;br&gt;🟠 Laranja = Finalização&lt;br&gt;🟣 Roxo = Monitorização&lt;br&gt;&lt;br&gt;&lt;b&gt;Serialização:&lt;/b&gt; JSON (Gson)&lt;br&gt;&lt;b&gt;Protocolo:&lt;/b&gt; TCP/IP" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;spacing=10;" parent="1" vertex="1">
<mxGeometry x="1210" y="825" width="200" height="220" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@@ -3,7 +3,7 @@ name: Java CI with Maven
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
branches: [ "dev", "cleanup" ] branches: [ "main", "dev", "cleanup" ]
tags: tags:
- 'v*.*.*' - 'v*.*.*'
pull_request: pull_request:
@@ -77,7 +77,7 @@ jobs:
publish-release: publish-release:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build, build-windows] needs: [build, build-windows]
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main'
permissions: permissions:
contents: write contents: write
steps: steps:

View File

@@ -1,24 +1,168 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" version="28.2.7"> <mxfile host="Electron" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.0.3 Chrome/140.0.7339.249 Electron/38.7.0 Safari/537.36" version="29.0.3">
<diagram name="Página-1" id="B1_hHcevBzWlEwI7FSV6"> <diagram name="Arquitetura SD" id="QKeTeUWuUs8JeLsq44d-">
<mxGraphModel dx="778" dy="476" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0"> <mxGraphModel dx="1426" dy="841" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
<root> <root>
<mxCell id="0" /> <mxCell id="0" />
<mxCell id="1" parent="0" /> <mxCell id="1" parent="0" />
<mxCell id="vcp7vux32DhQR4tKQhnF-8" value="Dashboard" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#C73500;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;fillColor=#fa6800;shape=mxgraph.mscae.oms.dashboard;fontColor=#000000;" vertex="1" parent="1"> <mxCell id="0K4eb2koB2xQ8duQ1-_a-27" value="&lt;b&gt;MessageProtocol&lt;/b&gt;&lt;hr&gt;interface:&lt;br&gt;• getType()&lt;br&gt;• getPayload()&lt;br&gt;• getSourceNode()&lt;br&gt;• getDestinationNode()" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#fff2cc;strokeColor=#d6b656;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="389" y="230" width="50" height="41" as="geometry" /> <mxGeometry x="30" y="30" width="180" height="120" as="geometry" />
</mxCell> </mxCell>
<mxCell id="vcp7vux32DhQR4tKQhnF-12" value="Semaforo.java" style="shape=image;html=1;verticalAlign=top;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;imageAspect=0;aspect=fixed;image=https://icons.diagrams.net/icon-cache1/Strabo-2829/traffic_light-1068.png" vertex="1" parent="1"> <mxCell id="0K4eb2koB2xQ8duQ1-_a-28" value="&lt;b&gt;Tipos de Mensagens&lt;/b&gt;&lt;hr&gt;• VEHICLE_TRANSFER&lt;br&gt;• VEHICLE_SPAWN&lt;br&gt;• STATS_UPDATE&lt;br&gt;• TRAFFIC_LIGHT_SYNC&lt;br&gt;• HEARTBEAT" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#fff2cc;strokeColor=#d6b656;spacing=10;fontColor=#000000;" parent="1" vertex="1">
<mxGeometry x="230" y="350" width="53" height="53" as="geometry" /> <mxGeometry x="20" y="170" width="200" height="120" as="geometry" />
</mxCell> </mxCell>
<mxCell id="vcp7vux32DhQR4tKQhnF-13" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" edge="1" parent="1"> <mxCell id="0K4eb2koB2xQ8duQ1-_a-26" value="&lt;b&gt;LEGENDA&lt;/b&gt;&lt;hr&gt;━━━► Comunicação síncrona&lt;br&gt;╌╌╌► Comunicação periódica&lt;br&gt;&lt;br&gt;&lt;b&gt;Cores:&lt;/b&gt;&lt;br&gt;🔵 Azul =&amp;nbsp;&lt;span style=&quot;background-color: transparent;&quot;&gt;Criação do veículo&lt;/span&gt;&lt;div&gt;🟢 Verde = Transferência do veículo&lt;br&gt;🟠 Laranja = Chegada ao destino&lt;br&gt;🟣 Roxo =&amp;nbsp;&lt;span style=&quot;background-color: transparent;&quot;&gt;Envio das estatísticas&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;b&gt;Serialização:&lt;/b&gt; JSON (Gson)&lt;br&gt;&lt;b&gt;Protocolo:&lt;/b&gt; TCP/IP&lt;/div&gt;" style="rounded=0;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;spacing=10;" parent="1" vertex="1">
<mxGeometry width="50" height="50" relative="1" as="geometry"> <mxGeometry x="10" y="320" width="220" height="220" as="geometry" />
<mxPoint x="310" y="330" as="sourcePoint" /> </mxCell>
<mxPoint x="360" y="280" as="targetPoint" /> <mxCell id="L62mICw2ZrYi1D68OOFe-13" value="" style="group" parent="1" vertex="1" connectable="0">
<mxGeometry x="280" y="40" width="850" height="730" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-20" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;exitX=0.616;exitY=-0.011;exitDx=0;exitDy=0;entryX=0.661;entryY=-0.002;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="794" y="530" as="targetPoint" />
<Array as="points">
<mxPoint x="99" y="122" />
<mxPoint x="793" y="122" />
</Array>
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
<mxCell id="vcp7vux32DhQR4tKQhnF-14" value="CruzamentoServer.java" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=1;points=[];movable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" vertex="1" connectable="0" parent="vcp7vux32DhQR4tKQhnF-13"> <mxCell id="0K4eb2koB2xQ8duQ1-_a-1" value="&lt;b&gt;CoordinatorProcess&lt;/b&gt;&lt;br&gt;(Cliente Socket)&lt;hr&gt;• VehicleGenerator&lt;br&gt;• Modelo Poisson&lt;br&gt;• Liga a Cr1-Cr5" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="-0.3933" relative="1" as="geometry"> <mxGeometry x="205.6637168141593" width="176.28318584070794" height="101.38888888888889" as="geometry" />
<mxPoint x="25" y="25" as="offset" /> </mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-2" value="&lt;b&gt;Cr1&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8001&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8001)&lt;br&gt;• Thread Semáforo - Sul&lt;br&gt;• Thread Semáforo - Este&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry y="162.22" width="160" height="162.22" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-3" value="&lt;b&gt;Cr2&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8002&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8002)&lt;br&gt;• Thread Semáforo - Sul&lt;br&gt;• Thread Semáforo - Este&lt;br&gt;• Thread Semáforo - Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="227.7" y="162.22" width="162.3" height="162.22" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-5" value="&lt;b&gt;Cr4&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8004&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8004)&lt;br&gt;• Thread Semáforo - Este&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry y="486.67" width="160" height="133.33" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-6" value="&lt;b&gt;Cr5&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8005&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8005)&lt;br&gt;• Thread Semáforo - Este&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="220.35" y="486.67" width="169.65" height="162.22" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-7" value="&lt;b&gt;ExitNode (S)&lt;/b&gt;&lt;br&gt;Porta: 9001&lt;br&gt;Servidor Socket&lt;hr&gt;• Recebe veículos finais&lt;br&gt;• Calcula estatísticas:&lt;br&gt; - Tempo no sistema&lt;br&gt; - Tempo de espera&lt;br&gt; - Métricas por tipo&lt;br&gt;• Envia para o Dashboard" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="464.07" y="476.53" width="154.6" height="172.36" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-8" value="&lt;b&gt;DashboardServer&lt;/b&gt;&lt;br&gt;Porta: 9000&lt;br&gt;Servidor Socket&lt;hr&gt;• Thread Pool (10 threads)&lt;br&gt;• ConcurrentHashMap&lt;br&gt;• Agrega estatísticas&lt;br&gt;• Display a cada 5s:&lt;br&gt; - Throughput&lt;br&gt; - Tempos médios&lt;br&gt; - Tamanhos de filas" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="683.1" y="540" width="166.9" height="180" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-9" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-2" edge="1">
<mxGeometry x="-0.2105" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-3" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="309" y="120" />
<mxPoint x="309" y="120" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#0000FF;strokeWidth=2;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-1" target="0K4eb2koB2xQ8duQ1-_a-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-13" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=classic;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-3" target="0K4eb2koB2xQ8duQ1-_a-4" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-14" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=none;startFill=0;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-2" target="0K4eb2koB2xQ8duQ1-_a-5" edge="1">
<mxGeometry x="0.125" y="100" relative="1" as="geometry">
<Array as="points">
<mxPoint x="66.10619469026548" y="446.11111111111114" />
<mxPoint x="66.10619469026548" y="446.11111111111114" />
</Array>
<mxPoint y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-16" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=classic;startArrow=none;startFill=0;exitX=1.005;exitY=0.63;exitDx=0;exitDy=0;exitPerimeter=0;align=center;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-5" edge="1">
<mxGeometry x="-0.0178" y="-49" relative="1" as="geometry">
<mxPoint x="139.55752212389382" y="588.0555555555555" as="sourcePoint" />
<mxPoint x="220" y="571" as="targetPoint" />
<Array as="points">
<mxPoint x="220" y="571" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-19" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#b46504;strokeWidth=2;fillColor=#fad7ac;" parent="L62mICw2ZrYi1D68OOFe-13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="390" y="580" as="sourcePoint" />
<mxPoint x="462.74" y="580.22" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-22" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;exitX=0.981;exitY=0.08;exitDx=0;exitDy=0;exitPerimeter=0;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-4" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry x="0.1427" y="-60" relative="1" as="geometry">
<Array as="points">
<mxPoint x="593" y="175" />
<mxPoint x="593" y="140" />
<mxPoint x="764" y="140" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-4" value="&lt;b&gt;Cr3&lt;/b&gt; (IntersectionProcess)&lt;br&gt;Porta: 8003&lt;br&gt;Servidor + Cliente&lt;hr&gt;• ServerSocket (8003)&lt;br&gt;• Thread Semáforo - Sul&lt;br&gt;• Thread Semáforo - Oeste&lt;br&gt;• Fila Eventos (DES)&lt;br&gt;• ReentrantLock" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;align=left;verticalAlign=top;spacing=10;fontColor=#000000;" parent="L62mICw2ZrYi1D68OOFe-13" vertex="1">
<mxGeometry x="462.74" y="162.22" width="167.26" height="162.22" as="geometry" />
</mxCell>
<mxCell id="0K4eb2koB2xQ8duQ1-_a-30" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;entryX=0.186;entryY=0.998;entryDx=0;entryDy=0;entryPerimeter=0;" parent="L62mICw2ZrYi1D68OOFe-13" target="0K4eb2koB2xQ8duQ1-_a-8" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="103" y="620" as="sourcePoint" />
<mxPoint x="710" y="730" as="targetPoint" />
<Array as="points">
<mxPoint x="103" y="730" />
<mxPoint x="714" y="730" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="L62mICw2ZrYi1D68OOFe-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#00AA00;strokeWidth=2;endArrow=none;startArrow=classic;startFill=1;endFill=0;" parent="L62mICw2ZrYi1D68OOFe-13" edge="1">
<mxGeometry x="-0.2214" y="26" relative="1" as="geometry">
<mxPoint x="293.8053097345133" y="486.6666666666666" as="sourcePoint" />
<mxPoint x="293.8053097345133" y="324.44444444444446" as="targetPoint" />
<Array as="points" />
<mxPoint x="-17" y="6" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="L62mICw2ZrYi1D68OOFe-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#b46504;strokeWidth=2;endArrow=classic;startArrow=none;startFill=0;fillColor=#fad7ac;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="L62mICw2ZrYi1D68OOFe-13" target="0K4eb2koB2xQ8duQ1-_a-7" edge="1">
<mxGeometry x="0.3659" relative="1" as="geometry">
<mxPoint as="offset" />
<mxPoint x="541" y="324" as="sourcePoint" />
<mxPoint x="528.8495575221239" y="435.9722222222221" as="targetPoint" />
<Array as="points">
<mxPoint x="541" y="360" />
<mxPoint x="541" y="360" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="L62mICw2ZrYi1D68OOFe-10" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;exitX=0.621;exitY=-0.003;exitDx=0;exitDy=0;exitPerimeter=0;" parent="L62mICw2ZrYi1D68OOFe-13" source="0K4eb2koB2xQ8duQ1-_a-3" edge="1">
<mxGeometry x="0.1427" y="-60" relative="1" as="geometry">
<mxPoint x="330.53097345132744" y="141.94444444444443" as="sourcePoint" />
<mxPoint x="780" y="540" as="targetPoint" />
<Array as="points">
<mxPoint x="329" y="130" />
<mxPoint x="780" y="130" />
</Array>
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="L62mICw2ZrYi1D68OOFe-11" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="L62mICw2ZrYi1D68OOFe-13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="326" y="654" as="sourcePoint" />
<mxPoint x="683.1" y="700" as="targetPoint" />
<Array as="points">
<mxPoint x="326.1" y="700" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="L62mICw2ZrYi1D68OOFe-12" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;strokeColor=#9933FF;strokeWidth=2;dashed=1;" parent="L62mICw2ZrYi1D68OOFe-13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="556" y="650" as="sourcePoint" />
<mxPoint x="683.0973451327434" y="663.0833333333331" as="targetPoint" />
<Array as="points">
<mxPoint x="556" y="663" />
</Array>
</mxGeometry> </mxGeometry>
</mxCell> </mxCell>
</root> </root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
Enunciado.pdf Normal file

Binary file not shown.

712
RELATORIO_FINAL.tex Normal file
View File

@@ -0,0 +1,712 @@
\documentclass[12pt,a4paper]{article}
% Pacotes essenciais
\usepackage[utf8]{inputenc}
\usepackage[portuguese]{babel}
\usepackage[T1]{fontenc}
\usepackage{graphicx}
\usepackage{hyperref}
\usepackage{booktabs}
\usepackage{longtable}
\usepackage{geometry}
\usepackage{fancyhdr}
\usepackage{amsmath}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{float}
\usepackage{caption}
\usepackage{subcaption}
% Configuração da página
\geometry{
left=2.5cm,
right=2.5cm,
top=2.5cm,
bottom=2.5cm
}
% Configuração de hyperlinks
\hypersetup{
colorlinks=true,
linkcolor=blue,
filecolor=magenta,
urlcolor=cyan,
pdftitle={Trabalho Prático - Sistemas Distribuídos},
pdfauthor={David Alves, Leandro Afonso, Gabriel Moreira},
}
% Configuração de código
\lstset{
basicstyle=\ttfamily\footnotesize,
breaklines=true,
frame=single,
numbers=left,
numberstyle=\tiny,
keywordstyle=\color{blue},
commentstyle=\color{green!60!black},
stringstyle=\color{red}
}
% Cabeçalho e rodapé
\pagestyle{fancy}
\fancyhf{}
\rhead{Sistemas Distribuídos}
\lhead{Trabalho Prático}
\rfoot{Página \thepage}
\begin{document}
% ============================================================
% PÁGINA DE TÍTULO
% ============================================================
\begin{titlepage}
\centering
\vspace*{2cm}
{\LARGE\bfseries Licenciatura em Segurança Informática\\e Redes de Computadores\par}
\vspace{1.5cm}
{\Large Unidade Curricular ``Sistemas Distribuídos''\par}
\vspace{0.5cm}
{\large Ano letivo 2024/2025\par}
\vspace{2cm}
{\huge\bfseries Trabalho Prático\par}
\vspace{0.5cm}
{\Large Simulação de Tráfego Urbano Distribuído\par}
\vspace{2cm}
{\large\bfseries Realizado por:\par}
\vspace{0.5cm}
{\large
David Alves, número de aluno 8240231\\
Leandro Afonso, número de aluno [A COMPLETAR]\\
Gabriel Moreira, número de aluno [A COMPLETAR]
\par}
\vspace{1.5cm}
{\large\bfseries Docente da UC:\par}
\vspace{0.3cm}
{\large Ronaldo Moreira Salles\par}
\vfill
{\large 8 de dezembro de 2024\par}
\end{titlepage}
% ============================================================
% ÍNDICE
% ============================================================
\tableofcontents
\newpage
% ============================================================
% INTRODUÇÃO
% ============================================================
\section{Introdução}
O presente trabalho tem como objetivo dotar os alunos com a capacidade de desenvolver uma aplicação distribuída em linguagem Java, simulando um sistema de tráfego urbano. O projeto é desenvolvido de modo a permitir avaliar e gerir as regras de controlo dos semáforos, tendo em conta diferentes cargas de tráfego.
A simulação implementa uma arquitetura de processos distribuídos que comunicam através de sockets TCP/IP, onde cada componente opera de forma autónoma enquanto contribui para o comportamento global do sistema. Este paradigma permite avaliar propriedades como escalabilidade, tolerância a falhas e sincronização distribuída em ambientes concorrentes.
% ============================================================
% O MODELO DE SIMULAÇÃO
% ============================================================
\section{O Modelo de Simulação}
O sistema retrata uma malha três por três, tendo três pontos de entrada (E1, E2 e E3), cinco cruzamentos (de Cr1 a Cr5) e um ponto de saída (S). As arestas representam as ruas e podem ser de sentido único ou duplo. Cada cruzamento é um processo separado, responsável pela gestão local do tráfego através de semáforos independentes que controlam três direções (Sul, Este e Oeste). O ponto de saída recolhe as estatísticas finais dos veículos que terminam o seu percurso.
\subsection{Geração de Veículos}
O processo coordenador (\texttt{CoordinatorProcess}) atua como o gerador de veículos, colocando-os no percurso através dos pontos de entrada seguindo o modelo de \textbf{Poisson} ($\lambda$ configurável). A simulação modela assim a chegada de veículos de forma realista, semelhante ao tráfego real. Esta abordagem significa que o número total de veículos gerados varia entre execuções. Como alternativa, o sistema permite também configurar um modelo de chegadas fixas, onde os veículos chegam em intervalos constantes e previsíveis.
\subsection{Características dos Veículos}
Cada veículo criado tem a probabilidade de ser:
\begin{itemize}
\item \textbf{Mota} (20\%): Tempo de travessia = $0.5 \times$ tempo base
\item \textbf{Carro} (60\%): Tempo de travessia = $1.0 \times$ tempo base
\item \textbf{Camião} (20\%): Tempo de travessia = $2.0 \times$ tempo base
\end{itemize}
Cada veículo possui um identificador único e uma rota predefinida que determina o seu percurso completo até ao nó de saída. As rotas são atribuídas segundo a política de encaminhamento configurada:
\begin{itemize}
\item \textbf{Random}: Escolha aleatória entre todas as rotas possíveis
\item \textbf{Shortest Path}: Seleção da rota com menor número de saltos
\item \textbf{Least Congested}: Escolha da rota com menor congestionamento atual
\end{itemize}
\subsection{Modelo de Eventos Discretos (DES)}
A simulação baseia-se num modelo de eventos discretos, onde cada cruzamento mantém uma fila de prioridade de eventos ordenados cronologicamente por timestamp. Os eventos incluem:
\begin{itemize}
\item Chegadas e partidas de veículos
\item Mudanças de estado dos semáforos
\item Atualizações de estatísticas
\end{itemize}
Cada semáforo opera como uma thread independente, alternando entre os estados \textbf{GREEN} (verde) e \textbf{RED} (vermelho), gerindo uma fila FIFO de veículos que aguardam passagem. Quando um veículo atravessa um cruzamento, é enviado para o processo do próximo cruzamento indicado na sua rota, onde é adicionado à fila do semáforo correspondente à direção necessária.
\subsection{Comunicação Distribuída}
A arquitetura distribuída permite que cada componente seja executado de forma independente. As comunicações são baseadas em sockets TCP/IP, com serialização das mensagens em JSON através da biblioteca \textbf{Gson}. Esta abordagem garante interoperabilidade e facilita a depuração do sistema.
% ============================================================
% ARQUITETURA DO SISTEMA E COMPONENTES
% ============================================================
\section{Arquitetura do Sistema e Componentes}
Foi desenvolvido um diagrama que retrata a arquitetura do projeto, ilustrando a topologia da rede viária e as conexões entre os diversos processos.
\subsection{Componentes Principais}
\subsubsection{Coordenador (\texttt{CoordinatorProcess})}
\begin{itemize}
\item \textbf{Porta}: Coordenação central sem socket servidor próprio (atua como cliente)
\item \textbf{Função}: Geração de veículos, gestão do relógio global de simulação, injeção de carga nos pontos de entrada
\item \textbf{Responsabilidades}:
\begin{itemize}
\item Implementação do modelo de Poisson para chegadas estocásticas
\item Seleção de rotas baseada na política configurada
\item Sincronização temporal da simulação
\end{itemize}
\end{itemize}
\subsubsection{Processos de Cruzamento (\texttt{IntersectionProcess})}
Cada cruzamento mantém um \texttt{ServerSocket} dedicado que aceita ligações de múltiplos clientes simultaneamente, criando uma thread para cada ligação estabelecida:
\begin{itemize}
\item \textbf{Cr1}: Porta 8001
\item \textbf{Cr2}: Porta 8002
\item \textbf{Cr3}: Porta 8003
\item \textbf{Cr4}: Porta 8004
\item \textbf{Cr5}: Porta 8005
\end{itemize}
\textbf{Responsabilidades}:
\begin{itemize}
\item Gestão local de semáforos (3 direções: Sul, Este, Oeste)
\item Processamento de eventos DES locais
\item Encaminhamento de veículos para o próximo destino
\item Reporte periódico de estatísticas ao Dashboard
\end{itemize}
\subsubsection{Nó de Saída (\texttt{ExitNodeProcess})}
\begin{itemize}
\item \textbf{Porta}: 9001
\item \textbf{Função}: Agregação final de métricas
\item \textbf{Responsabilidades}:
\begin{itemize}
\item Cálculo do tempo total de permanência no sistema
\item Throughput global
\item Estatísticas agregadas por tipo de veículo
\end{itemize}
\end{itemize}
\subsubsection{Servidor de Dashboard (\texttt{DashboardServer})}
\begin{itemize}
\item \textbf{Porta}: 9000
\item \textbf{Função}: Monitorização centralizada e visualização em tempo real
\item \textbf{Responsabilidades}:
\begin{itemize}
\item Agregação de estatísticas de todos os processos
\item Deteção de falhas através de heartbeats
\item Renderização da interface gráfica (JavaFX) ou CLI
\end{itemize}
\end{itemize}
\subsection{Sincronização e Concorrência}
O \texttt{DashboardServer} implementa um sistema de agregação com \textbf{10 threads concorrentes} (thread pool) que processam atualizações de estatísticas vindas dos cinco cruzamentos. Utiliza um \texttt{ConcurrentHashMap} para armazenar métricas por cruzamento, permitindo leituras simultâneas enquanto recebe atualizações. O frontend é atualizado a cada \textbf{5 segundos}.
A sincronização entre os processos ocorre através de \textbf{comunicação assíncrona}, onde cada processo continua a sua execução local enquanto envia e recebe mensagens. Os semáforos dentro de cada cruzamento operam de forma autónoma com ciclos independentes. O dashboard identifica a ausência de heartbeats para detetar falhas de processos.
% ============================================================
% CLASSES E MÉTODOS
% ============================================================
\section{Classes e Métodos}
O programa está organizado em diversos packages que contêm classes com responsabilidades distintas. Neste relatório são mencionadas as principais que sustentam o projeto, estando o resto da documentação apoiado em JavaDoc.
\subsection{Package \texttt{sd.model}}
\subsubsection{Classe \texttt{Vehicle}}
Representa cada veículo na simulação com os seguintes atributos:
\begin{itemize}
\item \texttt{vehicleId}: Identificador único
\item \texttt{vehicleType}: Tipo (MOTORCYCLE, CAR, TRUCK)
\item \texttt{entryTime}: Timestamp de entrada no sistema
\item \texttt{route}: Lista de nós que compõem o percurso completo
\end{itemize}
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{advanceRoute()}: Avança para o próximo nó na rota
\item \texttt{getCurrentDestination()}: Obtém o próximo cruzamento
\item \texttt{addWaitingTime(double time)}: Acumula tempo de espera em filas
\item \texttt{addCrossingTime(double time)}: Acumula tempo de travessia
\end{itemize}
\subsubsection{Classe \texttt{Intersection}}
Gere os cruzamentos, mantendo:
\begin{itemize}
\item Um mapa de semáforos por direção (\texttt{Map<Direction, TrafficLight>})
\item Uma tabela de encaminhamento que mapeia destinos para direções específicas
\end{itemize}
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{configureRoute(String destination, Direction direction)}: Define a tabela de routing
\item \texttt{receiveVehicle(Vehicle vehicle)}: Recebe veículos e coloca-os na fila do semáforo correspondente
\item \texttt{getTrafficLight(Direction direction)}: Obtém o semáforo de uma direção específica
\end{itemize}
\subsubsection{Classe \texttt{TrafficLight}}
Controla cada semáforo individualmente, utilizando locks (\texttt{ReentrantLock}) para garantir thread-safety.
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{addVehicle(Vehicle vehicle)}: Adiciona veículo à fila (FIFO)
\item \texttt{removeVehicle()}: Remove veículo da fila (apenas quando verde)
\item \texttt{changeState(TrafficLightState newState)}: Alterna entre GREEN e RED
\item \texttt{getQueueSize()}: Retorna o tamanho atual da fila
\end{itemize}
\subsubsection{Classe \texttt{Event} (no package \texttt{sd.des})}
Representa eventos discretos na simulação. Implementa \texttt{Comparable<Event>} para ordenação automática cronológica na fila de prioridade.
\textbf{Atributos:}
\begin{itemize}
\item \texttt{timestamp}: Momento em que o evento deve ocorrer
\item \texttt{eventType}: Tipo do evento (VEHICLE\_ARRIVAL, TRAFFIC\_LIGHT\_CHANGE, etc.)
\item \texttt{associatedData}: Payload específico do evento
\end{itemize}
\subsubsection{Classe \texttt{Message}}
Define a estrutura das mensagens trocadas entre processos.
\textbf{Atributos:}
\begin{itemize}
\item \texttt{messageId}: Identificador único da mensagem
\item \texttt{messageType}: Tipo da mensagem (VEHICLE\_TRANSFER, STATS\_UPDATE, etc.)
\item \texttt{sourceNode}: Nó de origem
\item \texttt{destinationNode}: Nó de destino
\item \texttt{payload}: Conteúdo serializado em JSON
\end{itemize}
\subsection{Package \texttt{sd.coordinator}}
\subsubsection{Classe \texttt{CoordinatorProcess}}
Conduz a simulação através do paradigma de eventos discretos distribuído.
\textbf{Responsabilidades:}
\begin{itemize}
\item Inicialização da topologia da rede
\item Geração de veículos segundo distribuição de Poisson
\item Injeção de carga nos pontos de entrada (E1, E2, E3)
\item Seleção de rotas com base na política configurada
\end{itemize}
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{initialize()}: Prepara o sistema, estabelece conexões com interseções
\item \texttt{run()}: Loop principal de geração de veículos
\item \texttt{generateAndInjectVehicle()}: Cria um novo veículo e injeta-o num ponto de entrada
\item \texttt{selectRouteForVehicle()}: Determina a rota com base na política ativa
\end{itemize}
\subsubsection{Classe \texttt{SocketClient}}
Facilita a comunicação de rede do lado do cliente.
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{connect(String host, int port)}: Estabelece ligação TCP
\item \texttt{send(Message message)}: Serializa e transmite mensagem
\item \texttt{close()}: Encerra ligação e liberta recursos
\end{itemize}
\subsection{Package \texttt{sd}}
\subsubsection{Classe \texttt{IntersectionProcess}}
Representa um nó de processamento autónomo (Worker Node) na malha distribuída.
\textbf{Arquitetura híbrida:}
\begin{itemize}
\item \textbf{Reativa (Network I/O)}: Threads dedicadas aceitam conexões TCP e injetam veículos nas filas
\item \textbf{Proativa (DES Engine)}: Thread de processamento de eventos gere a lógica temporal
\end{itemize}
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{initialize()}: Carrega configurações, cria semáforos e rotas
\item \texttt{startTrafficLightThreads()}: Inicia threads de controlo dos semáforos
\item \texttt{startServerSocket()}: Aceita conexões de veículos de outros processos
\item \texttt{sendVehicleToNextDestination(Vehicle vehicle)}: Encaminha veículos pela rede
\item \texttt{reportStatistics()}: Envia métricas ao Dashboard periodicamente
\item \texttt{shutdown()}: Encerra serviços e liberta recursos
\end{itemize}
\subsection{Package \texttt{sd.util}}
\subsubsection{Classe \texttt{VehicleGenerator}}
Responsável pela criação de veículos segundo o modelo de Poisson.
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{generateVehicle()}: Cria veículo com tipo e rota selecionados aleatoriamente
\item \texttt{getNextArrivalTime(double lambda)}: Calcula o momento da próxima chegada usando distribuição exponencial
\end{itemize}
\subsubsection{Classe \texttt{StatisticsCollector}}
Agrega métricas do sistema, rastreando veículos em trânsito e mantendo contadores globais.
\textbf{Métodos principais:}
\begin{itemize}
\item \texttt{recordVehicleGeneration(Vehicle vehicle)}: Regista criação de veículo
\item \texttt{recordVehicleArrival(Vehicle vehicle, String intersection)}: Regista chegada a cruzamento
\item \texttt{recordVehicleDeparture(Vehicle vehicle, String intersection)}: Regista partida de cruzamento
\item \texttt{recordVehicleCompletion(Vehicle vehicle)}: Regista conclusão do percurso
\item \texttt{getStatistics()}: Retorna snapshot das métricas atuais
\item \texttt{printStatistics()}: Gera relatórios com throughput, tempos médios, etc.
\end{itemize}
\subsection{Package \texttt{sd.config}}
\subsubsection{Classe \texttt{SimulationConfig}}
Carrega parâmetros do ficheiro \texttt{simulation.properties} (ou variantes \texttt{simulation-low.properties}, \texttt{simulation-medium.properties}, \texttt{simulation-high.properties}).
\textbf{Parâmetros configuráveis:}
\begin{itemize}
\item Duração da simulação
\item Taxa de chegada $\lambda$ (lambda)
\item Probabilidades de tipos de veículos
\item Tempos de travessia base
\item Tempos de ciclo dos semáforos
\item Política de encaminhamento
\end{itemize}
\subsection{Package \texttt{sd.routing}}
\subsubsection{Políticas de Encaminhamento}
O sistema suporta três estratégias de seleção de rotas:
\begin{itemize}
\item \texttt{RandomRouteSelector}: Escolha equiprovável entre todas as rotas disponíveis.
\item \texttt{ShortestPathRouteSelector}: Seleção da rota com menor número de saltos (hops).
\item \texttt{LeastCongestedRouteSelector}: Escolha da rota com menor congestionamento atual, baseada em feedback dos cruzamentos sobre tamanhos de filas.
\end{itemize}
% ============================================================
% AVALIAÇÃO DO DESEMPENHO DO SISTEMA
% ============================================================
\section{Avaliação do Desempenho do Sistema}
A avaliação do sistema baseia-se em métricas recolhidas pela classe \texttt{DashboardStatistics}, que monitoriza:
\begin{itemize}
\item Número de veículos gerados
\item Número de veículos que completaram o percurso
\item Taxa de conclusão (\%)
\item Tempo médio no sistema (segundos)
\item Tempo médio de espera acumulado em filas (segundos)
\item Throughput (veículos/segundo)
\end{itemize}
\subsection{Cenários de Carga}
Foram configurados três cenários de carga de tráfego, cada um executado 5 vezes para obter estatísticas fiáveis.
\subsubsection{Tráfego Leve (LOW LOAD)}
\textbf{Configuração:} \texttt{simulation-low.properties} - $\lambda = 0.2$ veículos/segundo
\begin{table}[H]
\centering
\caption{Métricas de Desempenho - Tráfego Leve}
\begin{tabular}{lrrrrr}
\toprule
\textbf{Métrica} & \textbf{Média} & \textbf{Desvio Padrão} & \textbf{IC 95\%} & \textbf{Mín} & \textbf{Máx} \\
\midrule
Veículos Gerados & 364.60 & 9.34 & [351.30, 377.90] & 350 & 373 \\
Veículos Completados & 219.60 & 31.19 & [175.22, 263.98] & 187 & 263 \\
Taxa de Conclusão (\%) & \textbf{60.38} & 9.71 & [46.57, 74.20] & 50.40 & 72.85 \\
Tempo Médio no Sistema (s) & \textbf{33.04} & 7.41 & [22.50, 43.58] & 23.36 & 42.28 \\
Tempo Médio de Espera (s) & 13.78 & 3.43 & [8.82, 18.73] & 9.72 & 17.53 \\
\bottomrule
\end{tabular}
\end{table}
\textbf{Análise:} Com carga leve, a rede opera abaixo da capacidade sem congestionamento significativo. A taxa de conclusão superior a 60\% e o tempo médio no sistema de apenas 33 segundos demonstram que o sistema consegue processar eficientemente o tráfego quando a chegada de veículos é esparsa.
\subsubsection{Tráfego Moderado (MEDIUM LOAD)}
\textbf{Configuração:} \texttt{simulation-medium.properties} - $\lambda = 0.5$ veículos/segundo
\begin{table}[H]
\centering
\caption{Métricas de Desempenho - Tráfego Moderado}
\begin{tabular}{lrrrrr}
\toprule
\textbf{Métrica} & \textbf{Média} & \textbf{Desvio Padrão} & \textbf{IC 95\%} & \textbf{Mín} & \textbf{Máx} \\
\midrule
Veículos Gerados & 927.20 & 32.48 & [880.97, 973.43] & 886 & 954 \\
Veículos Completados & 419.40 & 90.64 & [290.42, 548.38] & 312 & 535 \\
Taxa de Conclusão (\%) & \textbf{45.23} & 9.64 & [31.50, 58.95] & 34.74 & 56.08 \\
Tempo Médio no Sistema (s) & \textbf{44.48} & 6.81 & [34.79, 54.18] & 35.08 & 52.56 \\
Tempo Médio de Espera (s) & 21.05 & 4.73 & [13.55, 28.56] & 14.60 & 25.60 \\
\bottomrule
\end{tabular}
\end{table}
\textbf{Análise:} Com tráfego moderado, observa-se o início de congestionamento. A taxa de conclusão cai para 45\% e o tempo médio no sistema aumenta 34\% relativamente ao cenário leve. As filas começam a formar-se nos cruzamentos principais (Cr2 e Cr5).
\subsubsection{Tráfego Intenso (HIGH LOAD)}
\textbf{Configuração:} \texttt{simulation-high.properties} - $\lambda = 1.0$ veículos/segundo
\begin{table}[H]
\centering
\caption{Métricas de Desempenho - Tráfego Intenso}
\begin{tabular}{lrrrrr}
\toprule
\textbf{Métrica} & \textbf{Média} & \textbf{Desvio Padrão} & \textbf{IC 95\%} & \textbf{Mín} & \textbf{Máx} \\
\midrule
Veículos Gerados & 1813.80 & 41.93 & [1754.13, 1873.47] & 1782 & 1872 \\
Veículos Completados & 651.00 & 354.20 & [146.96, 1155.04] & 179 & 953 \\
Taxa de Conclusão (\%) & \textbf{35.92} & 19.44 & [8.25, 63.58] & 9.70 & 50.91 \\
Tempo Médio no Sistema (s) & \textbf{60.15} & 6.17 & [51.38, 68.93] & 53.09 & 65.41 \\
Tempo Médio de Espera (s) & 37.82 & 5.59 & [29.67, 45.96] & 31.51 & 44.58 \\
\bottomrule
\end{tabular}
\end{table}
\textbf{Análise:} Com tráfego intenso, a rede aproxima-se da saturação. A taxa de conclusão cai para apenas 36\% com elevada variabilidade ($\sigma = 19.44\%$), indicando comportamento instável. O tempo médio no sistema atinge 60 segundos, mas o aspecto mais crítico é o tempo médio de espera de 37.82 segundos, representando 63\% do tempo total no sistema. A alta variabilidade sugere que o sistema está perto do colapso, com algumas execuções a atingir apenas 9.7\% de taxa de conclusão.
\subsection{Análise Comparativa}
\begin{table}[H]
\centering
\caption{Comparação entre Cenários de Carga}
\begin{tabular}{lrrrr}
\toprule
\textbf{Cenário} & \textbf{$\lambda$ (v/s)} & \textbf{Taxa Conclusão (\%)} & \textbf{Tempo Sistema (s)} & \textbf{Tempo Espera (s)} \\
\midrule
Leve & 0.2 & 60.38 & 33.04 & 13.78 \\
Moderado & 0.5 & 45.23 & 44.48 & 21.05 \\
Intenso & 1.0 & 35.92 & 60.15 & 37.82 \\
\bottomrule
\end{tabular}
\end{table}
\textbf{Observações:}
\begin{enumerate}
\item \textbf{Relação não-linear}: Dobrar a taxa de chegada ($0.2 \rightarrow 0.5$) reduz a taxa de conclusão em 25\%, mas quadruplicar ($0.2 \rightarrow 1.0$) reduz apenas 40\%, sugerindo efeitos de saturação.
\item \textbf{Gargalos identificados}: Os cruzamentos Cr2 e Cr5 funcionam como pontos de convergência, acumulando os maiores tamanhos de fila.
\item \textbf{Tempo de espera dominante}: Em carga intensa, 63\% do tempo no sistema é gasto em filas.
\end{enumerate}
\subsection{Otimizações Testadas}
\subsubsection{Configuração de Ciclos de Semáforos}
Foram testadas diferentes configurações de temporização:
\textbf{Ciclos Longos} (verde 60s, vermelho 5s):
\begin{itemize}
\item Maior vazão (throughput) por ciclo
\item Aumento significativo da espera para direções em vermelho
\item Adequado para tráfego unidirecional dominante
\end{itemize}
\textbf{Ciclos Curtos} (verde 8-10s, vermelho 5s):
\begin{itemize}
\item Distribuição mais equitativa da capacidade
\item Redução da espera máxima
\item Perda de eficiência devido ao overhead de mudanças de estado
\end{itemize}
\textbf{Ciclos Otimizados} (diferenciados por cruzamento):
\begin{itemize}
\item Cr2 e Cr5: verde 30-40s (pontos de convergência)
\item Cr1, Cr3, Cr4: verde 15-20s (tráfego distribuído)
\item Melhor compromisso entre throughput e equidade
\end{itemize}
\subsubsection{Políticas de Encaminhamento}
\textbf{Random}: Baseline - distribuição uniforme da carga mas sem otimização.
\textbf{Shortest Path}: Minimiza latência individual mas pode criar hotspots nos caminhos mais curtos.
\textbf{Least Congested}: Balanceamento dinâmico da carga, requer feedback em tempo real dos cruzamentos. Reduz congestionamento mas pode aumentar latência individual ao escolher caminhos mais longos.
% ============================================================
% DASHBOARD
% ============================================================
\section{Dashboard}
O sistema de monitorização foi implementado utilizando \textbf{JavaFX} para a interface gráfica, fornecendo visualização em tempo real do estado da simulação.
\subsection{Características Principais}
\subsubsection{Interface Gráfica}
\textbf{Estatísticas Globais}: Painel superior com métricas agregadas do sistema
\begin{itemize}
\item Veículos gerados vs. completados
\item Taxa de conclusão em tempo real
\item Throughput atual
\item Tempo médio no sistema
\end{itemize}
\textbf{Tabelas Dinâmicas}:
\begin{itemize}
\item Estatísticas por tipo de veículo (Mota, Carro, Camião)
\item Métricas por interseção (Cr1-Cr5)
\item Tamanhos de fila por direção
\item Estados dos semáforos (cores indicativas)
\end{itemize}
\textbf{Controlos de Simulação}:
\begin{itemize}
\item Botão \textbf{Start}: Inicia todos os processos distribuídos
\item Botão \textbf{Stop}: Termina a simulação graciosamente
\item Indicador de estado do sistema
\item Heartbeat visual para cada processo
\end{itemize}
\subsubsection{Arquitetura de Concorrência}
O \texttt{DashboardServer} utiliza:
\begin{itemize}
\item \textbf{Thread Pool} com 10 threads concorrentes para processar atualizações
\item \texttt{ConcurrentHashMap} para armazenamento thread-safe de métricas
\item \texttt{ScheduledExecutorService} para atualizações periódicas da UI (intervalo de 5 segundos)
\item \texttt{Platform.runLater()} para marshalling seguro de atualizações para a UI thread do JavaFX
\end{itemize}
\subsubsection{Deteção de Falhas}
O sistema implementa monitorização de saúde através de:
\begin{itemize}
\item \textbf{Heartbeats}: Cada processo envia sinais periódicos (intervalo configurável)
\item \textbf{Timeout de conexão}: Identifica processos não-responsivos
\item \textbf{Indicadores visuais}: Marcação a vermelho de nós com falhas
\item \textbf{Logs de eventos}: Registo de todas as anomalias detetadas
\end{itemize}
\subsection{Análise em Batch}
O sistema inclui uma ferramenta de análise estatística (\texttt{MultiRunAnalyzer}) que:
\begin{itemize}
\item Executa múltiplas simulações automaticamente
\item Calcula médias, medianas, desvios padrão e intervalos de confiança (95\%)
\item Gera relatórios em formato texto e CSV
\item Permite comparação entre diferentes configurações
\end{itemize}
% ============================================================
% CONCLUSÃO
% ============================================================
\section{Conclusão}
O trabalho desenvolvido demonstra a implementação bem-sucedida de um sistema distribuído para simulação de tráfego urbano, cumprindo os objetivos pedagógicos da unidade curricular.
\subsection{Principais Conquistas}
\begin{enumerate}
\item \textbf{Arquitetura Distribuída Robusta}: Implementação de comunicação assíncrona entre processos autónomos através de sockets TCP/IP, com serialização JSON garantindo interoperabilidade.
\item \textbf{Modelo de Simulação Realista}: Utilização do modelo de eventos discretos (DES) com distribuição de Poisson para chegadas de veículos, aproximando-se de padrões de tráfego reais.
\item \textbf{Escalabilidade Demonstrada}: O sistema mantém-se funcional desde tráfego leve ($\lambda=0.2$) até tráfego intenso ($\lambda=1.0$), embora com degradação expectável de desempenho em saturação.
\item \textbf{Monitorização Abrangente}: Dashboard com visualização em tempo real e capacidade de análise estatística multi-execução, permitindo avaliação rigorosa do desempenho.
\item \textbf{Políticas de Encaminhamento Adaptativas}: Implementação de três estratégias distintas (Random, Shortest Path, Least Congested) demonstrando flexibilidade arquitetural.
\end{enumerate}
\subsection{Aprendizagens}
\begin{itemize}
\item \textbf{Sincronização Distribuída}: Gestão da complexidade inerente à coordenação de múltiplos processos autónomos sem relógio global centralizado.
\item \textbf{Concorrência e Thread-Safety}: Utilização apropriada de locks, estruturas de dados concorrentes e thread pools para garantir correção em ambiente multi-threaded.
\item \textbf{Trade-offs de Desempenho}: Compreensão das relações não-lineares entre carga de entrada, throughput e latência, especialmente em aproximação à saturação.
\item \textbf{Análise Quantitativa}: Aplicação de métodos estatísticos (intervalos de confiança, análise de variância) para avaliação rigorosa de sistemas estocásticos.
\end{itemize}
\subsection{Limitações e Trabalho Futuro}
\textbf{Limitações identificadas:}
\begin{itemize}
\item Rotas estáticas: Veículos não podem desviar-se em resposta a congestionamento dinâmico
\item Capacidade infinita de filas: Sistema não modela bloqueios físicos por falta de espaço
\item Ausência de prioridades: Todos os veículos são tratados igualmente (sem veículos de emergência)
\item Modelo de semáforos simplificado: Não considera fases de amarelo ou coordenação entre cruzamentos adjacentes
\end{itemize}
\textbf{Melhorias propostas:}
\begin{enumerate}
\item Implementação de encaminhamento adaptativo baseado em aprendizagem por reforço
\item Modelação de capacidades finitas com backpressure entre cruzamentos
\item Coordenação de semáforos através de ``ondas verdes'' para corredores prioritários
\item Integração de eventos externos (acidentes, obras, eventos especiais)
\item Visualização 3D da malha viária com animação de veículos em movimento
\end{enumerate}
\subsection{Conclusão Final}
O projeto cumpre integralmente os requisitos da unidade curricular, demonstrando competências na conceção, implementação e avaliação de sistemas distribuídos. Os resultados quantitativos obtidos através das análises multi-execução fornecem insights valiosos sobre o comportamento do sistema sob diferentes condições de carga, validando a abordagem arquitetural adotada.
A experiência adquirida na resolução de desafios de sincronização, gestão de concorrência e análise de desempenho constitui uma base sólida para o desenvolvimento de sistemas distribuídos de maior complexidade em contextos profissionais futuros.
% ============================================================
% BIBLIOGRAFIA
% ============================================================
\begin{thebibliography}{9}
\bibitem{tanenbaum2017}
Tanenbaum, A. S., \& Van Steen, M. (2017).
\textit{Distributed Systems: Principles and Paradigms} (3rd ed.).
Pearson.
\bibitem{coulouris2011}
Coulouris, G., Dollimore, J., Kindberg, T., \& Blair, G. (2011).
\textit{Distributed Systems: Concepts and Design} (5th ed.).
Addison-Wesley.
\bibitem{banks2009}
Banks, J., Carson, J. S., Nelson, B. L., \& Nicol, D. M. (2009).
\textit{Discrete-Event System Simulation} (5th ed.).
Pearson.
\bibitem{oracle2024java}
Oracle. (2024).
\textit{Java Platform, Standard Edition Documentation} (Version 17).
\url{https://docs.oracle.com/en/java/javase/17/}
\bibitem{goetz2006}
Goetz, B., Peierls, T., Bloch, J., Bowbeer, J., Holmes, D., \& Lea, D. (2006).
\textit{Java Concurrency in Practice}.
Addison-Wesley.
\bibitem{oracle2024javafx}
Oracle. (2024).
\textit{JavaFX Documentation} (Version 17).
\url{https://openjfx.io/javadoc/17/}
\bibitem{google2024gson}
Google. (2024).
\textit{Gson User Guide}.
\url{https://github.com/google/gson/blob/master/UserGuide.md}
\bibitem{law2000}
Law, A. M., \& Kelton, W. D. (2000).
\textit{Simulation Modeling and Analysis} (3rd ed.).
McGraw-Hill.
\end{thebibliography}
\vspace{1cm}
\noindent\textbf{Nota:} Este relatório foi elaborado com base na análise do código-fonte do projeto e nos resultados experimentais obtidos através de múltiplas execuções da simulação. Todos os valores estatísticos apresentados foram extraídos dos ficheiros de análise gerados pelo sistema (\texttt{analysis/LOW\_LOAD\_*.txt}, \texttt{analysis/MEDIUM\_LOAD\_*.txt}, \texttt{analysis/HIGH\_LOAD\_*.txt}).
\end{document}

View File

@@ -1,6 +0,0 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,1784,877,49.16,64.58,61.43,32.29,129.16
2,1782,363,20.37,53.77,51.01,26.88,107.53
3,1786,883,49.44,53.09,50.08,26.54,106.17
4,1845,179,9.70,63.92,60.27,31.96,127.84
5,1872,953,50.91,65.41,62.16,32.70,130.81
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 1784 877 49.16 64.58 61.43 32.29 129.16
3 2 1782 363 20.37 53.77 51.01 26.88 107.53
4 3 1786 883 49.44 53.09 50.08 26.54 106.17
5 4 1845 179 9.70 63.92 60.27 31.96 127.84
6 5 1872 953 50.91 65.41 62.16 32.70 130.81

View File

@@ -1,215 +0,0 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-high.properties
Número de Execuções: 5
Data da Análise: 2025-12-07 00:11:13
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 1813.80 Desvio Padrão: 41.93
Mediana: 1786.00 IC 95%: [1754.13, 1873.47]
Mín: 1782.00 Máx: 1872.00
Veículos Completados:
Média: 651.00 Desvio Padrão: 354.20
Mediana: 877.00 IC 95%: [146.96, 1155.04]
Mín: 179.00 Máx: 953.00
Taxa de Conclusão (%):
Média: 35.92 Desvio Padrão: 19.44
Mediana: 49.16 IC 95%: [8.25, 63.58]
Mín: 9.70 Máx: 50.91
Tempo Médio no Sistema (segundos):
Média: 60.15 Desvio Padrão: 6.17
Mediana: 63.92 IC 95%: [51.38, 68.93]
Mín: 53.09 Máx: 65.41
Tempo Médio de Espera (segundos):
Média: 56.99 Desvio Padrão: 5.93
Mediana: 60.27 IC 95%: [48.55, 65.43]
Mín: 50.08 Máx: 62.16
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 135.40 Desvio Padrão: 77.66
Mediana: 167.00 IC 95%: [24.89, 245.91]
Mín: 37.00 Máx: 211.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 55.15 Desvio Padrão: 12.01
Mediana: 54.23 IC 95%: [38.07, 72.24]
Mín: 43.41 Máx: 74.99
--- LIGHT ---
Contagem de Veículos:
Média: 395.00 Desvio Padrão: 207.62
Mediana: 540.00 IC 95%: [99.55, 690.45]
Mín: 107.00 Máx: 548.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 59.79 Desvio Padrão: 7.28
Mediana: 61.58 IC 95%: [49.43, 70.15]
Mín: 50.81 Máx: 69.26
--- HEAVY ---
Contagem de Veículos:
Média: 120.60 Desvio Padrão: 72.95
Mediana: 142.00 IC 95%: [16.79, 224.41]
Mín: 35.00 Máx: 202.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 49.20 Desvio Padrão: 8.62
Mediana: 50.31 IC 95%: [36.94, 61.46]
Mín: 35.51 Máx: 58.20
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 3.20 Desvio Padrão: 5.54
Mediana: 1.00 IC 95%: [-4.68, 11.08]
Mín: 0.00 Máx: 13.00
Tamanho Médio da Fila:
Média: 3.20 Desvio Padrão: 5.54
Mediana: 1.00 IC 95%: [-4.68, 11.08]
Mín: 0.00 Máx: 13.00
Veículos Processados:
Média: 378.40 Desvio Padrão: 252.94
Mediana: 512.00 IC 95%: [18.46, 738.34]
Mín: 58.00 Máx: 600.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Tamanho Médio da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Veículos Processados:
Média: 390.40 Desvio Padrão: 223.14
Mediana: 409.00 IC 95%: [72.87, 707.93]
Mín: 59.00 Máx: 599.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 6.20 Desvio Padrão: 8.67
Mediana: 0.00 IC 95%: [-6.14, 18.54]
Mín: 0.00 Máx: 18.00
Tamanho Médio da Fila:
Média: 6.20 Desvio Padrão: 8.67
Mediana: 0.00 IC 95%: [-6.14, 18.54]
Mín: 0.00 Máx: 18.00
Veículos Processados:
Média: 339.00 Desvio Padrão: 239.34
Mediana: 416.00 IC 95%: [-1.59, 679.59]
Mín: 57.00 Máx: 622.00
--- Cr4 ---
Tamanho Máximo da Fila:
Média: 0.60 Desvio Padrão: 0.89
Mediana: 0.00 IC 95%: [-0.67, 1.87]
Mín: 0.00 Máx: 2.00
Tamanho Médio da Fila:
Média: 0.60 Desvio Padrão: 0.89
Mediana: 0.00 IC 95%: [-0.67, 1.87]
Mín: 0.00 Máx: 2.00
Veículos Processados:
Média: 123.40 Desvio Padrão: 116.13
Mediana: 109.00 IC 95%: [-41.85, 288.65]
Mín: 21.00 Máx: 316.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 2.40 Desvio Padrão: 1.14
Mediana: 2.00 IC 95%: [0.78, 4.02]
Mín: 1.00 Máx: 4.00
Tamanho Médio da Fila:
Média: 2.40 Desvio Padrão: 1.14
Mediana: 2.00 IC 95%: [0.78, 4.02]
Mín: 1.00 Máx: 4.00
Veículos Processados:
Média: 200.80 Desvio Padrão: 114.19
Mediana: 261.00 IC 95%: [38.31, 363.29]
Mín: 70.00 Máx: 305.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 651.00 Desvio Padrão: 354.20
Mediana: 877.00 IC 95%: [146.96, 1155.04]
Mín: 179.00 Máx: 953.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-high.properties]:
Gerados: 1784, Completados: 877 (49.2%)
Tempo Médio no Sistema: 64.58s
Tempo Médio de Espera: 61.43s
Execução #2 [simulation-high.properties]:
Gerados: 1782, Completados: 363 (20.4%)
Tempo Médio no Sistema: 53.77s
Tempo Médio de Espera: 51.01s
Execução #3 [simulation-high.properties]:
Gerados: 1786, Completados: 883 (49.4%)
Tempo Médio no Sistema: 53.09s
Tempo Médio de Espera: 50.08s
Execução #4 [simulation-high.properties]:
Gerados: 1845, Completados: 179 (9.7%)
Tempo Médio no Sistema: 63.92s
Tempo Médio de Espera: 60.27s
Execução #5 [simulation-high.properties]:
Gerados: 1872, Completados: 953 (50.9%)
Tempo Médio no Sistema: 65.41s
Tempo Médio de Espera: 62.16s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -0,0 +1,6 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,1836,348,18.95,75.91,72.28,37.96,151.82
2,1728,663,38.37,52.10,49.52,26.05,104.21
3,1747,539,30.85,116.39,112.54,58.19,232.78
4,1769,149,8.42,89.64,85.89,44.82,179.29
5,1827,1097,60.04,90.49,86.93,45.25,180.98
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 1836 348 18.95 75.91 72.28 37.96 151.82
3 2 1728 663 38.37 52.10 49.52 26.05 104.21
4 3 1747 539 30.85 116.39 112.54 58.19 232.78
5 4 1769 149 8.42 89.64 85.89 44.82 179.29
6 5 1827 1097 60.04 90.49 86.93 45.25 180.98

View File

@@ -0,0 +1,215 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-high.properties
Número de Execuções: 5
Data da Análise: 2025-12-08 08:20:40
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 1781.40 Desvio Padrão: 48.09
Mediana: 1769.00 IC 95%: [1712.97, 1849.83]
Mín: 1728.00 Máx: 1836.00
Veículos Completados:
Média: 559.20 Desvio Padrão: 358.22
Mediana: 539.00 IC 95%: [49.44, 1068.96]
Mín: 149.00 Máx: 1097.00
Taxa de Conclusão (%):
Média: 31.33 Desvio Padrão: 19.70
Mediana: 30.85 IC 95%: [3.30, 59.36]
Mín: 8.42 Máx: 60.04
Tempo Médio no Sistema (segundos):
Média: 84.91 Desvio Padrão: 23.46
Mediana: 89.64 IC 95%: [51.52, 118.29]
Mín: 52.10 Máx: 116.39
Tempo Médio de Espera (segundos):
Média: 81.43 Desvio Padrão: 23.02
Mediana: 85.89 IC 95%: [48.68, 114.19]
Mín: 49.52 Máx: 112.54
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 111.60 Desvio Padrão: 69.43
Mediana: 105.00 IC 95%: [12.80, 210.40]
Mín: 29.00 Máx: 215.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 78.89 Desvio Padrão: 20.87
Mediana: 89.97 IC 95%: [49.20, 108.59]
Mín: 49.27 Máx: 98.23
--- LIGHT ---
Contagem de Veículos:
Média: 333.80 Desvio Padrão: 221.25
Mediana: 332.00 IC 95%: [18.95, 648.65]
Mín: 90.00 Máx: 669.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 83.83 Desvio Padrão: 24.74
Mediana: 86.14 IC 95%: [48.63, 119.03]
Mín: 51.94 Máx: 120.26
--- HEAVY ---
Contagem de Veículos:
Média: 113.80 Desvio Padrão: 68.36
Mediana: 102.00 IC 95%: [16.53, 211.07]
Mín: 30.00 Máx: 213.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 76.79 Desvio Padrão: 21.46
Mediana: 81.20 IC 95%: [46.26, 107.33]
Mín: 43.10 Máx: 102.14
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Tamanho Médio da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Veículos Processados:
Média: 221.40 Desvio Padrão: 226.21
Mediana: 128.00 IC 95%: [-100.50, 543.30]
Mín: 61.00 Máx: 616.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 3.60 Desvio Padrão: 5.90
Mediana: 2.00 IC 95%: [-4.79, 11.99]
Mín: 0.00 Máx: 14.00
Tamanho Médio da Fila:
Média: 3.60 Desvio Padrão: 5.90
Mediana: 2.00 IC 95%: [-4.79, 11.99]
Mín: 0.00 Máx: 14.00
Veículos Processados:
Média: 228.60 Desvio Padrão: 211.41
Mediana: 126.00 IC 95%: [-72.24, 529.44]
Mín: 93.00 Máx: 593.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 1.20 Desvio Padrão: 2.68
Mediana: 0.00 IC 95%: [-2.62, 5.02]
Mín: 0.00 Máx: 6.00
Tamanho Médio da Fila:
Média: 1.20 Desvio Padrão: 2.68
Mediana: 0.00 IC 95%: [-2.62, 5.02]
Mín: 0.00 Máx: 6.00
Veículos Processados:
Média: 263.80 Desvio Padrão: 240.18
Mediana: 128.00 IC 95%: [-77.98, 605.58]
Mín: 57.00 Máx: 604.00
--- Cr4 ---
Tamanho Máximo da Fila:
Média: 0.60 Desvio Padrão: 0.89
Mediana: 0.00 IC 95%: [-0.67, 1.87]
Mín: 0.00 Máx: 2.00
Tamanho Médio da Fila:
Média: 0.60 Desvio Padrão: 0.89
Mediana: 0.00 IC 95%: [-0.67, 1.87]
Mín: 0.00 Máx: 2.00
Veículos Processados:
Média: 95.00 Desvio Padrão: 78.43
Mediana: 62.00 IC 95%: [-16.60, 206.60]
Mín: 43.00 Máx: 231.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 2.80 Desvio Padrão: 3.63
Mediana: 1.00 IC 95%: [-2.37, 7.97]
Mín: 0.00 Máx: 9.00
Tamanho Médio da Fila:
Média: 2.80 Desvio Padrão: 3.63
Mediana: 1.00 IC 95%: [-2.37, 7.97]
Mín: 0.00 Máx: 9.00
Veículos Processados:
Média: 207.60 Desvio Padrão: 166.31
Mediana: 139.00 IC 95%: [-29.06, 444.26]
Mín: 76.00 Máx: 493.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 559.20 Desvio Padrão: 358.22
Mediana: 539.00 IC 95%: [49.44, 1068.96]
Mín: 149.00 Máx: 1097.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-high.properties]:
Gerados: 1836, Completados: 348 (19.0%)
Tempo Médio no Sistema: 75.91s
Tempo Médio de Espera: 72.28s
Execução #2 [simulation-high.properties]:
Gerados: 1728, Completados: 663 (38.4%)
Tempo Médio no Sistema: 52.10s
Tempo Médio de Espera: 49.52s
Execução #3 [simulation-high.properties]:
Gerados: 1747, Completados: 539 (30.9%)
Tempo Médio no Sistema: 116.39s
Tempo Médio de Espera: 112.54s
Execução #4 [simulation-high.properties]:
Gerados: 1769, Completados: 149 (8.4%)
Tempo Médio no Sistema: 89.64s
Tempo Médio de Espera: 85.89s
Execução #5 [simulation-high.properties]:
Gerados: 1827, Completados: 1097 (60.0%)
Tempo Médio no Sistema: 90.49s
Tempo Médio de Espera: 86.93s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -1,6 +0,0 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,371,187,50.40,42.28,38.65,21.14,84.57
2,361,263,72.85,29.15,25.29,14.57,58.30
3,368,197,53.53,38.02,33.95,19.01,76.04
4,350,239,68.29,32.38,28.36,16.19,64.75
5,373,212,56.84,23.36,19.96,11.68,46.73
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 371 187 50.40 42.28 38.65 21.14 84.57
3 2 361 263 72.85 29.15 25.29 14.57 58.30
4 3 368 197 53.53 38.02 33.95 19.01 76.04
5 4 350 239 68.29 32.38 28.36 16.19 64.75
6 5 373 212 56.84 23.36 19.96 11.68 46.73

View File

@@ -1,209 +0,0 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-low.properties
Número de Execuções: 5
Data da Análise: 2025-12-07 00:09:57
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 364.60 Desvio Padrão: 9.34
Mediana: 368.00 IC 95%: [351.30, 377.90]
Mín: 350.00 Máx: 373.00
Veículos Completados:
Média: 219.60 Desvio Padrão: 31.19
Mediana: 212.00 IC 95%: [175.22, 263.98]
Mín: 187.00 Máx: 263.00
Taxa de Conclusão (%):
Média: 60.38 Desvio Padrão: 9.71
Mediana: 56.84 IC 95%: [46.57, 74.20]
Mín: 50.40 Máx: 72.85
Tempo Médio no Sistema (segundos):
Média: 33.04 Desvio Padrão: 7.41
Mediana: 32.38 IC 95%: [22.50, 43.58]
Mín: 23.36 Máx: 42.28
Tempo Médio de Espera (segundos):
Média: 29.24 Desvio Padrão: 7.30
Mediana: 28.36 IC 95%: [18.85, 39.63]
Mín: 19.96 Máx: 38.65
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 41.00 Desvio Padrão: 6.96
Mediana: 43.00 IC 95%: [31.09, 50.91]
Mín: 33.00 Máx: 50.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 25.91 Desvio Padrão: 3.91
Mediana: 26.98 IC 95%: [20.35, 31.47]
Mín: 19.60 Máx: 30.06
--- LIGHT ---
Contagem de Veículos:
Média: 134.00 Desvio Padrão: 24.07
Mediana: 130.00 IC 95%: [99.74, 168.26]
Mín: 104.00 Máx: 167.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 29.34 Desvio Padrão: 6.83
Mediana: 27.89 IC 95%: [19.62, 39.06]
Mín: 20.73 Máx: 36.42
--- HEAVY ---
Contagem de Veículos:
Média: 44.60 Desvio Padrão: 3.44
Mediana: 46.00 IC 95%: [39.71, 49.49]
Mín: 40.00 Máx: 48.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 32.11 Desvio Padrão: 15.90
Mediana: 30.74 IC 95%: [9.48, 54.74]
Mín: 18.09 Máx: 58.73
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Tamanho Médio da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Veículos Processados:
Média: 63.80 Desvio Padrão: 17.25
Mediana: 57.00 IC 95%: [39.25, 88.35]
Mín: 48.00 Máx: 91.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 0.80 Desvio Padrão: 1.79
Mediana: 0.00 IC 95%: [-1.75, 3.35]
Mín: 0.00 Máx: 4.00
Tamanho Médio da Fila:
Média: 0.80 Desvio Padrão: 1.79
Mediana: 0.00 IC 95%: [-1.75, 3.35]
Mín: 0.00 Máx: 4.00
Veículos Processados:
Média: 56.20 Desvio Padrão: 18.51
Mediana: 50.00 IC 95%: [29.86, 82.54]
Mín: 35.00 Máx: 78.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 1.00 Desvio Padrão: 1.41
Mediana: 0.00 IC 95%: [-1.01, 3.01]
Mín: 0.00 Máx: 3.00
Tamanho Médio da Fila:
Média: 1.00 Desvio Padrão: 1.41
Mediana: 0.00 IC 95%: [-1.01, 3.01]
Mín: 0.00 Máx: 3.00
Veículos Processados:
Média: 63.20 Desvio Padrão: 23.97
Mediana: 56.00 IC 95%: [29.09, 97.31]
Mín: 41.00 Máx: 104.00
--- Cr4 ---
Tamanho Máximo da Fila:
Média: 1.80 Desvio Padrão: 2.49
Mediana: 0.00 IC 95%: [-1.74, 5.34]
Mín: 0.00 Máx: 5.00
Tamanho Médio da Fila:
Média: 1.80 Desvio Padrão: 2.49
Mediana: 0.00 IC 95%: [-1.74, 5.34]
Mín: 0.00 Máx: 5.00
Veículos Processados:
Média: 51.00 Desvio Padrão: 16.05
Mediana: 53.00 IC 95%: [28.16, 73.84]
Mín: 31.00 Máx: 70.00
--- Cr5 ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 86.60 Desvio Padrão: 34.20
Mediana: 65.00 IC 95%: [37.94, 135.26]
Mín: 62.00 Máx: 139.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 219.60 Desvio Padrão: 31.19
Mediana: 212.00 IC 95%: [175.22, 263.98]
Mín: 187.00 Máx: 263.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-low.properties]:
Gerados: 371, Completados: 187 (50.4%)
Tempo Médio no Sistema: 42.28s
Tempo Médio de Espera: 38.65s
Execução #2 [simulation-low.properties]:
Gerados: 361, Completados: 263 (72.9%)
Tempo Médio no Sistema: 29.15s
Tempo Médio de Espera: 25.29s
Execução #3 [simulation-low.properties]:
Gerados: 368, Completados: 197 (53.5%)
Tempo Médio no Sistema: 38.02s
Tempo Médio de Espera: 33.95s
Execução #4 [simulation-low.properties]:
Gerados: 350, Completados: 239 (68.3%)
Tempo Médio no Sistema: 32.38s
Tempo Médio de Espera: 28.36s
Execução #5 [simulation-low.properties]:
Gerados: 373, Completados: 212 (56.8%)
Tempo Médio no Sistema: 23.36s
Tempo Médio de Espera: 19.96s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -0,0 +1,5 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,354,228,64.41,40.36,36.75,20.18,80.72
2,373,261,69.97,40.61,36.87,20.30,81.21
3,353,235,66.57,32.63,29.04,16.32,65.27
4,350,269,76.86,37.39,33.42,18.70,74.78
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 354 228 64.41 40.36 36.75 20.18 80.72
3 2 373 261 69.97 40.61 36.87 20.30 81.21
4 3 353 235 66.57 32.63 29.04 16.32 65.27
5 4 350 269 76.86 37.39 33.42 18.70 74.78

View File

@@ -0,0 +1,204 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-low.properties
Número de Execuções: 4
Data da Análise: 2025-12-08 08:13:57
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 357.50 Desvio Padrão: 10.47
Mediana: 353.50 IC 95%: [340.84, 374.16]
Mín: 350.00 Máx: 373.00
Veículos Completados:
Média: 248.25 Desvio Padrão: 19.82
Mediana: 248.00 IC 95%: [216.71, 279.79]
Mín: 228.00 Máx: 269.00
Taxa de Conclusão (%):
Média: 69.45 Desvio Padrão: 5.44
Mediana: 68.27 IC 95%: [60.79, 78.11]
Mín: 64.41 Máx: 76.86
Tempo Médio no Sistema (segundos):
Média: 37.75 Desvio Padrão: 3.71
Mediana: 38.87 IC 95%: [31.85, 43.65]
Mín: 32.63 Máx: 40.61
Tempo Médio de Espera (segundos):
Média: 34.02 Desvio Padrão: 3.68
Mediana: 35.08 IC 95%: [28.16, 39.88]
Mín: 29.04 Máx: 36.87
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 52.25 Desvio Padrão: 8.58
Mediana: 54.50 IC 95%: [38.60, 65.90]
Mín: 40.00 Máx: 60.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 43.39 Desvio Padrão: 19.65
Mediana: 39.45 IC 95%: [12.12, 74.65]
Mín: 25.04 Máx: 69.63
--- LIGHT ---
Contagem de Veículos:
Média: 151.25 Desvio Padrão: 8.34
Mediana: 152.50 IC 95%: [137.98, 164.52]
Mín: 141.00 Máx: 159.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 28.92 Desvio Padrão: 5.34
Mediana: 28.76 IC 95%: [20.43, 37.42]
Mín: 23.35 Máx: 34.82
--- HEAVY ---
Contagem de Veículos:
Média: 44.75 Desvio Padrão: 6.29
Mediana: 44.00 IC 95%: [34.74, 54.76]
Mín: 39.00 Máx: 52.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 43.02 Desvio Padrão: 13.73
Mediana: 47.91 IC 95%: [21.18, 64.86]
Mín: 22.83 Máx: 53.43
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 0.25 Desvio Padrão: 0.50
Mediana: 0.00 IC 95%: [-0.55, 1.05]
Mín: 0.00 Máx: 1.00
Tamanho Médio da Fila:
Média: 0.25 Desvio Padrão: 0.50
Mediana: 0.00 IC 95%: [-0.55, 1.05]
Mín: 0.00 Máx: 1.00
Veículos Processados:
Média: 105.50 Desvio Padrão: 10.66
Mediana: 103.50 IC 95%: [88.54, 122.46]
Mín: 95.00 Máx: 120.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 1.75 Desvio Padrão: 2.87
Mediana: 0.50 IC 95%: [-2.82, 6.32]
Mín: 0.00 Máx: 6.00
Tamanho Médio da Fila:
Média: 1.75 Desvio Padrão: 2.87
Mediana: 0.50 IC 95%: [-2.82, 6.32]
Mín: 0.00 Máx: 6.00
Veículos Processados:
Média: 119.00 Desvio Padrão: 11.17
Mediana: 122.50 IC 95%: [101.24, 136.76]
Mín: 103.00 Máx: 128.00
--- Cr3 ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 114.75 Desvio Padrão: 15.88
Mediana: 119.00 IC 95%: [89.48, 140.02]
Mín: 93.00 Máx: 128.00
--- Cr4 ---
Tamanho Máximo da Fila:
Média: 1.25 Desvio Padrão: 0.50
Mediana: 1.00 IC 95%: [0.45, 2.05]
Mín: 1.00 Máx: 2.00
Tamanho Médio da Fila:
Média: 1.25 Desvio Padrão: 0.50
Mediana: 1.00 IC 95%: [0.45, 2.05]
Mín: 1.00 Máx: 2.00
Veículos Processados:
Média: 63.00 Desvio Padrão: 11.75
Mediana: 62.00 IC 95%: [44.31, 81.69]
Mín: 50.00 Máx: 78.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 4.50 Desvio Padrão: 2.89
Mediana: 4.50 IC 95%: [-0.09, 9.09]
Mín: 1.00 Máx: 8.00
Tamanho Médio da Fila:
Média: 4.50 Desvio Padrão: 2.89
Mediana: 4.50 IC 95%: [-0.09, 9.09]
Mín: 1.00 Máx: 8.00
Veículos Processados:
Média: 123.00 Desvio Padrão: 24.18
Mediana: 116.50 IC 95%: [84.53, 161.47]
Mín: 103.00 Máx: 156.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 248.25 Desvio Padrão: 19.82
Mediana: 248.00 IC 95%: [216.71, 279.79]
Mín: 228.00 Máx: 269.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-low.properties]:
Gerados: 354, Completados: 228 (64.4%)
Tempo Médio no Sistema: 40.36s
Tempo Médio de Espera: 36.75s
Execução #2 [simulation-low.properties]:
Gerados: 373, Completados: 261 (70.0%)
Tempo Médio no Sistema: 40.61s
Tempo Médio de Espera: 36.87s
Execução #3 [simulation-low.properties]:
Gerados: 353, Completados: 235 (66.6%)
Tempo Médio no Sistema: 32.63s
Tempo Médio de Espera: 29.04s
Execução #4 [simulation-low.properties]:
Gerados: 350, Completados: 269 (76.9%)
Tempo Médio no Sistema: 37.39s
Tempo Médio de Espera: 33.42s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -0,0 +1,6 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,368,329,89.40,78.34,74.19,39.17,156.67
2,368,218,59.24,60.44,56.64,30.22,120.89
3,349,235,67.34,53.51,49.44,26.76,107.03
4,332,243,73.19,69.63,65.50,34.82,139.27
5,322,221,68.63,47.52,43.77,23.76,95.05
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 368 329 89.40 78.34 74.19 39.17 156.67
3 2 368 218 59.24 60.44 56.64 30.22 120.89
4 3 349 235 67.34 53.51 49.44 26.76 107.03
5 4 332 243 73.19 69.63 65.50 34.82 139.27
6 5 322 221 68.63 47.52 43.77 23.76 95.05

View File

@@ -0,0 +1,215 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-low.properties
Número de Execuções: 5
Data da Análise: 2025-12-08 08:19:33
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 347.80 Desvio Padrão: 20.81
Mediana: 349.00 IC 95%: [318.18, 377.42]
Mín: 322.00 Máx: 368.00
Veículos Completados:
Média: 249.20 Desvio Padrão: 45.76
Mediana: 235.00 IC 95%: [184.08, 314.32]
Mín: 218.00 Máx: 329.00
Taxa de Conclusão (%):
Média: 71.56 Desvio Padrão: 11.17
Mediana: 68.63 IC 95%: [55.66, 87.46]
Mín: 59.24 Máx: 89.40
Tempo Médio no Sistema (segundos):
Média: 61.89 Desvio Padrão: 12.34
Mediana: 60.44 IC 95%: [44.33, 79.45]
Mín: 47.52 Máx: 78.34
Tempo Médio de Espera (segundos):
Média: 57.91 Desvio Padrão: 12.21
Mediana: 56.64 IC 95%: [40.54, 75.28]
Mín: 43.77 Máx: 74.19
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 48.20 Desvio Padrão: 12.38
Mediana: 47.00 IC 95%: [30.59, 65.81]
Mín: 36.00 Máx: 68.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 51.22 Desvio Padrão: 16.62
Mediana: 46.02 IC 95%: [27.56, 74.87]
Mín: 40.06 Máx: 80.31
--- LIGHT ---
Contagem de Veículos:
Média: 151.00 Desvio Padrão: 22.64
Mediana: 146.00 IC 95%: [118.78, 183.22]
Mín: 133.00 Máx: 189.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 58.33 Desvio Padrão: 11.58
Mediana: 53.58 IC 95%: [41.85, 74.80]
Mín: 45.31 Máx: 74.17
--- HEAVY ---
Contagem de Veículos:
Média: 50.00 Desvio Padrão: 13.77
Mediana: 47.00 IC 95%: [30.41, 69.59]
Mín: 35.00 Máx: 72.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 60.73 Desvio Padrão: 34.92
Mediana: 44.79 IC 95%: [11.04, 110.42]
Mín: 40.26 Máx: 122.51
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 5.00 Desvio Padrão: 4.47
Mediana: 4.00 IC 95%: [-1.36, 11.36]
Mín: 0.00 Máx: 12.00
Tamanho Médio da Fila:
Média: 5.00 Desvio Padrão: 4.47
Mediana: 4.00 IC 95%: [-1.36, 11.36]
Mín: 0.00 Máx: 12.00
Veículos Processados:
Média: 87.00 Desvio Padrão: 29.01
Mediana: 93.00 IC 95%: [45.72, 128.28]
Mín: 56.00 Máx: 123.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Tamanho Médio da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Veículos Processados:
Média: 95.20 Desvio Padrão: 24.86
Mediana: 100.00 IC 95%: [59.82, 130.58]
Mín: 61.00 Máx: 125.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Tamanho Médio da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Veículos Processados:
Média: 91.40 Desvio Padrão: 28.68
Mediana: 103.00 IC 95%: [50.58, 132.22]
Mín: 56.00 Máx: 126.00
--- Cr4 ---
Tamanho Máximo da Fila:
Média: 0.80 Desvio Padrão: 0.84
Mediana: 1.00 IC 95%: [-0.39, 1.99]
Mín: 0.00 Máx: 2.00
Tamanho Médio da Fila:
Média: 0.80 Desvio Padrão: 0.84
Mediana: 1.00 IC 95%: [-0.39, 1.99]
Mín: 0.00 Máx: 2.00
Veículos Processados:
Média: 63.00 Desvio Padrão: 21.11
Mediana: 62.00 IC 95%: [32.96, 93.04]
Mín: 38.00 Máx: 87.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 2.20 Desvio Padrão: 2.59
Mediana: 1.00 IC 95%: [-1.48, 5.88]
Mín: 0.00 Máx: 5.00
Tamanho Médio da Fila:
Média: 2.20 Desvio Padrão: 2.59
Mediana: 1.00 IC 95%: [-1.48, 5.88]
Mín: 0.00 Máx: 5.00
Veículos Processados:
Média: 126.40 Desvio Padrão: 45.39
Mediana: 111.00 IC 95%: [61.81, 190.99]
Mín: 86.00 Máx: 203.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 249.20 Desvio Padrão: 45.76
Mediana: 235.00 IC 95%: [184.08, 314.32]
Mín: 218.00 Máx: 329.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-low.properties]:
Gerados: 368, Completados: 329 (89.4%)
Tempo Médio no Sistema: 78.34s
Tempo Médio de Espera: 74.19s
Execução #2 [simulation-low.properties]:
Gerados: 368, Completados: 218 (59.2%)
Tempo Médio no Sistema: 60.44s
Tempo Médio de Espera: 56.64s
Execução #3 [simulation-low.properties]:
Gerados: 349, Completados: 235 (67.3%)
Tempo Médio no Sistema: 53.51s
Tempo Médio de Espera: 49.44s
Execução #4 [simulation-low.properties]:
Gerados: 332, Completados: 243 (73.2%)
Tempo Médio no Sistema: 69.63s
Tempo Médio de Espera: 65.50s
Execução #5 [simulation-low.properties]:
Gerados: 322, Completados: 221 (68.6%)
Tempo Médio no Sistema: 47.52s
Tempo Médio de Espera: 43.77s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -1,6 +0,0 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,950,416,43.79,49.34,45.70,24.67,98.68
2,886,480,54.18,35.08,31.69,17.54,70.16
3,954,535,56.08,43.76,40.30,21.88,87.51
4,948,354,37.34,41.68,37.96,20.84,83.37
5,898,312,34.74,52.56,49.26,26.28,105.13
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 950 416 43.79 49.34 45.70 24.67 98.68
3 2 886 480 54.18 35.08 31.69 17.54 70.16
4 3 954 535 56.08 43.76 40.30 21.88 87.51
5 4 948 354 37.34 41.68 37.96 20.84 83.37
6 5 898 312 34.74 52.56 49.26 26.28 105.13

View File

@@ -1,203 +0,0 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-medium.properties
Número de Execuções: 5
Data da Análise: 2025-12-07 00:10:34
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 927.20 Desvio Padrão: 32.48
Mediana: 948.00 IC 95%: [880.97, 973.43]
Mín: 886.00 Máx: 954.00
Veículos Completados:
Média: 419.40 Desvio Padrão: 90.64
Mediana: 416.00 IC 95%: [290.42, 548.38]
Mín: 312.00 Máx: 535.00
Taxa de Conclusão (%):
Média: 45.23 Desvio Padrão: 9.64
Mediana: 43.79 IC 95%: [31.50, 58.95]
Mín: 34.74 Máx: 56.08
Tempo Médio no Sistema (segundos):
Média: 44.48 Desvio Padrão: 6.81
Mediana: 43.76 IC 95%: [34.79, 54.18]
Mín: 35.08 Máx: 52.56
Tempo Médio de Espera (segundos):
Média: 40.98 Desvio Padrão: 6.83
Mediana: 40.30 IC 95%: [31.26, 50.71]
Mín: 31.69 Máx: 49.26
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 75.80 Desvio Padrão: 15.96
Mediana: 71.00 IC 95%: [53.09, 98.51]
Mín: 56.00 Máx: 95.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 42.34 Desvio Padrão: 10.81
Mediana: 39.70 IC 95%: [26.96, 57.72]
Mín: 31.96 Máx: 55.19
--- LIGHT ---
Contagem de Veículos:
Média: 263.20 Desvio Padrão: 58.29
Mediana: 265.00 IC 95%: [180.25, 346.15]
Mín: 204.00 Máx: 344.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 39.13 Desvio Padrão: 6.35
Mediana: 38.08 IC 95%: [30.09, 48.17]
Mín: 30.47 Máx: 47.99
--- HEAVY ---
Contagem de Veículos:
Média: 80.40 Desvio Padrão: 19.11
Mediana: 80.00 IC 95%: [53.20, 107.60]
Mín: 52.00 Máx: 102.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 48.02 Desvio Padrão: 30.99
Mediana: 34.44 IC 95%: [3.92, 92.11]
Mín: 32.46 Máx: 103.40
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 5.60 Desvio Padrão: 11.44
Mediana: 0.00 IC 95%: [-10.67, 21.87]
Mín: 0.00 Máx: 26.00
Tamanho Médio da Fila:
Média: 5.60 Desvio Padrão: 11.44
Mediana: 0.00 IC 95%: [-10.67, 21.87]
Mín: 0.00 Máx: 26.00
Veículos Processados:
Média: 156.00 Desvio Padrão: 122.81
Mediana: 98.00 IC 95%: [-18.76, 330.76]
Mín: 35.00 Máx: 306.00
--- Cr2 ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 172.00 Desvio Padrão: 121.88
Mediana: 116.00 IC 95%: [-1.44, 345.44]
Mín: 66.00 Máx: 322.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Tamanho Médio da Fila:
Média: 0.60 Desvio Padrão: 1.34
Mediana: 0.00 IC 95%: [-1.31, 2.51]
Mín: 0.00 Máx: 3.00
Veículos Processados:
Média: 168.40 Desvio Padrão: 133.38
Mediana: 121.00 IC 95%: [-21.40, 358.20]
Mín: 48.00 Máx: 326.00
--- Cr4 ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 71.80 Desvio Padrão: 20.39
Mediana: 77.00 IC 95%: [42.79, 100.81]
Mín: 38.00 Máx: 92.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 3.60 Desvio Padrão: 3.85
Mediana: 2.00 IC 95%: [-1.87, 9.07]
Mín: 0.00 Máx: 10.00
Tamanho Médio da Fila:
Média: 3.60 Desvio Padrão: 3.85
Mediana: 2.00 IC 95%: [-1.87, 9.07]
Mín: 0.00 Máx: 10.00
Veículos Processados:
Média: 150.60 Desvio Padrão: 43.37
Mediana: 126.00 IC 95%: [88.88, 212.32]
Mín: 116.00 Máx: 209.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 419.40 Desvio Padrão: 90.64
Mediana: 416.00 IC 95%: [290.42, 548.38]
Mín: 312.00 Máx: 535.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-medium.properties]:
Gerados: 950, Completados: 416 (43.8%)
Tempo Médio no Sistema: 49.34s
Tempo Médio de Espera: 45.70s
Execução #2 [simulation-medium.properties]:
Gerados: 886, Completados: 480 (54.2%)
Tempo Médio no Sistema: 35.08s
Tempo Médio de Espera: 31.69s
Execução #3 [simulation-medium.properties]:
Gerados: 954, Completados: 535 (56.1%)
Tempo Médio no Sistema: 43.76s
Tempo Médio de Espera: 40.30s
Execução #4 [simulation-medium.properties]:
Gerados: 948, Completados: 354 (37.3%)
Tempo Médio no Sistema: 41.68s
Tempo Médio de Espera: 37.96s
Execução #5 [simulation-medium.properties]:
Gerados: 898, Completados: 312 (34.7%)
Tempo Médio no Sistema: 52.56s
Tempo Médio de Espera: 49.26s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -0,0 +1,6 @@
Execução,VeículosGerados,VeículosCompletados,TaxaConclusão,TempoMédioSistema,TempoMédioEspera,TempoMínimoSistema,TempoMáximoSistema
1,891,202,22.67,69.75,66.09,34.87,139.50
2,871,340,39.04,68.73,64.73,34.37,137.46
3,953,541,56.77,68.64,65.24,34.32,137.28
4,888,501,56.42,60.85,57.48,30.42,121.69
5,869,387,44.53,58.29,55.37,29.15,116.58
1 Execução VeículosGerados VeículosCompletados TaxaConclusão TempoMédioSistema TempoMédioEspera TempoMínimoSistema TempoMáximoSistema
2 1 891 202 22.67 69.75 66.09 34.87 139.50
3 2 871 340 39.04 68.73 64.73 34.37 137.46
4 3 953 541 56.77 68.64 65.24 34.32 137.28
5 4 888 501 56.42 60.85 57.48 30.42 121.69
6 5 869 387 44.53 58.29 55.37 29.15 116.58

View File

@@ -0,0 +1,209 @@
================================================================================
ANÁLISE ESTATÍSTICA MULTI-EXECUÇÃO
================================================================================
Configuração: simulation-medium.properties
Número de Execuções: 5
Data da Análise: 2025-12-08 08:20:05
--------------------------------------------------------------------------------
MÉTRICAS GLOBAIS
--------------------------------------------------------------------------------
Veículos Gerados:
Média: 894.40 Desvio Padrão: 34.20
Mediana: 888.00 IC 95%: [845.73, 943.07]
Mín: 869.00 Máx: 953.00
Veículos Completados:
Média: 394.20 Desvio Padrão: 134.99
Mediana: 387.00 IC 95%: [202.11, 586.29]
Mín: 202.00 Máx: 541.00
Taxa de Conclusão (%):
Média: 43.89 Desvio Padrão: 14.12
Mediana: 44.53 IC 95%: [23.80, 63.97]
Mín: 22.67 Máx: 56.77
Tempo Médio no Sistema (segundos):
Média: 65.25 Desvio Padrão: 5.28
Mediana: 68.64 IC 95%: [57.73, 72.77]
Mín: 58.29 Máx: 69.75
Tempo Médio de Espera (segundos):
Média: 61.78 Desvio Padrão: 4.97
Mediana: 64.73 IC 95%: [54.71, 68.86]
Mín: 55.37 Máx: 66.09
--------------------------------------------------------------------------------
ANÁLISE POR TIPO DE VEÍCULO
--------------------------------------------------------------------------------
--- BIKE ---
Contagem de Veículos:
Média: 83.60 Desvio Padrão: 28.80
Mediana: 88.00 IC 95%: [42.62, 124.58]
Mín: 42.00 Máx: 112.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 64.62 Desvio Padrão: 9.80
Mediana: 65.07 IC 95%: [50.67, 78.57]
Mín: 53.82 Máx: 77.73
--- LIGHT ---
Contagem de Veículos:
Média: 234.80 Desvio Padrão: 86.82
Mediana: 221.00 IC 95%: [111.26, 358.34]
Mín: 119.00 Máx: 328.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 60.49 Desvio Padrão: 4.15
Mediana: 61.41 IC 95%: [54.58, 66.39]
Mín: 53.78 Máx: 65.19
--- HEAVY ---
Contagem de Veículos:
Média: 75.80 Desvio Padrão: 21.70
Mediana: 78.00 IC 95%: [44.93, 106.67]
Mín: 41.00 Máx: 101.00
Tempo Médio no Sistema (segundos): Sem dados
Tempo Médio de Espera (segundos):
Média: 62.90 Desvio Padrão: 13.27
Mediana: 63.80 IC 95%: [44.01, 81.79]
Mín: 42.19 Máx: 78.56
--------------------------------------------------------------------------------
ANÁLISE POR INTERSEÇÃO
--------------------------------------------------------------------------------
--- Cr1 ---
Tamanho Máximo da Fila:
Média: 2.00 Desvio Padrão: 2.55
Mediana: 1.00 IC 95%: [-1.63, 5.63]
Mín: 0.00 Máx: 6.00
Tamanho Médio da Fila:
Média: 2.00 Desvio Padrão: 2.55
Mediana: 1.00 IC 95%: [-1.63, 5.63]
Mín: 0.00 Máx: 6.00
Veículos Processados:
Média: 106.20 Desvio Padrão: 62.26
Mediana: 72.00 IC 95%: [17.60, 194.80]
Mín: 56.00 Máx: 208.00
--- Cr2 ---
Tamanho Máximo da Fila:
Média: 1.40 Desvio Padrão: 3.13
Mediana: 0.00 IC 95%: [-3.05, 5.85]
Mín: 0.00 Máx: 7.00
Tamanho Médio da Fila:
Média: 1.40 Desvio Padrão: 3.13
Mediana: 0.00 IC 95%: [-3.05, 5.85]
Mín: 0.00 Máx: 7.00
Veículos Processados:
Média: 123.60 Desvio Padrão: 90.00
Mediana: 102.00 IC 95%: [-4.47, 251.67]
Mín: 49.00 Máx: 275.00
--- Cr3 ---
Tamanho Máximo da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Tamanho Médio da Fila:
Média: 0.20 Desvio Padrão: 0.45
Mediana: 0.00 IC 95%: [-0.44, 0.84]
Mín: 0.00 Máx: 1.00
Veículos Processados:
Média: 102.60 Desvio Padrão: 50.09
Mediana: 104.00 IC 95%: [31.32, 173.88]
Mín: 55.00 Máx: 181.00
--- Cr4 ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 68.80 Desvio Padrão: 27.10
Mediana: 60.00 IC 95%: [30.24, 107.36]
Mín: 47.00 Máx: 113.00
--- Cr5 ---
Tamanho Máximo da Fila:
Média: 1.20 Desvio Padrão: 2.17
Mediana: 0.00 IC 95%: [-1.89, 4.29]
Mín: 0.00 Máx: 5.00
Tamanho Médio da Fila:
Média: 1.20 Desvio Padrão: 2.17
Mediana: 0.00 IC 95%: [-1.89, 4.29]
Mín: 0.00 Máx: 5.00
Veículos Processados:
Média: 125.80 Desvio Padrão: 51.69
Mediana: 96.00 IC 95%: [52.24, 199.36]
Mín: 84.00 Máx: 193.00
--- ExitNode ---
Tamanho Máximo da Fila: Sem dados
Tamanho Médio da Fila: Sem dados
Veículos Processados:
Média: 394.20 Desvio Padrão: 134.99
Mediana: 387.00 IC 95%: [202.11, 586.29]
Mín: 202.00 Máx: 541.00
--------------------------------------------------------------------------------
RESUMOS INDIVIDUAIS DAS EXECUÇÕES
--------------------------------------------------------------------------------
Execução #1 [simulation-medium.properties]:
Gerados: 891, Completados: 202 (22.7%)
Tempo Médio no Sistema: 69.75s
Tempo Médio de Espera: 66.09s
Execução #2 [simulation-medium.properties]:
Gerados: 871, Completados: 340 (39.0%)
Tempo Médio no Sistema: 68.73s
Tempo Médio de Espera: 64.73s
Execução #3 [simulation-medium.properties]:
Gerados: 953, Completados: 541 (56.8%)
Tempo Médio no Sistema: 68.64s
Tempo Médio de Espera: 65.24s
Execução #4 [simulation-medium.properties]:
Gerados: 888, Completados: 501 (56.4%)
Tempo Médio no Sistema: 60.85s
Tempo Médio de Espera: 57.48s
Execução #5 [simulation-medium.properties]:
Gerados: 869, Completados: 387 (44.5%)
Tempo Médio no Sistema: 58.29s
Tempo Médio de Espera: 55.37s
================================================================================
FIM DO RELATÓRIO
================================================================================

View File

@@ -41,10 +41,10 @@ dwelling_times = [
medium['TempoMédioSistema'].mean(), medium['TempoMédioSistema'].mean(),
high['TempoMédioSistema'].mean() high['TempoMédioSistema'].mean()
] ]
plt.bar(['Low', 'Medium', 'High'], dwelling_times, color=['green', 'orange', 'red']) plt.bar(['Baixa', 'Média', 'Alta'], dwelling_times, color=['green', 'orange', 'red'])
plt.ylabel('Average Dwelling Time (s)') plt.ylabel('Tempo Médio no Sistema (s)')
plt.title('System Performance vs Load') plt.title('Desempenho do Sistema vs Carga')
plt.xlabel('Load Scenario') plt.xlabel('Cenário de Carga')
plt.grid(axis='y', alpha=0.3) plt.grid(axis='y', alpha=0.3)
for i, v in enumerate(dwelling_times): for i, v in enumerate(dwelling_times):
plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom') plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom')
@@ -59,10 +59,10 @@ completion_rates = [
medium['TaxaConclusão'].mean(), medium['TaxaConclusão'].mean(),
high['TaxaConclusão'].mean() high['TaxaConclusão'].mean()
] ]
plt.bar(['Low', 'Medium', 'High'], completion_rates, color=['green', 'orange', 'red']) plt.bar(['Baixa', 'Média', 'Alta'], completion_rates, color=['green', 'orange', 'red'])
plt.ylabel('Completion Rate (%)') plt.ylabel('Taxa de Conclusão (%)')
plt.title('Vehicle Completion Rate vs Load') plt.title('Taxa de Conclusão de Veículos vs Carga')
plt.xlabel('Load Scenario') plt.xlabel('Cenário de Carga')
plt.grid(axis='y', alpha=0.3) plt.grid(axis='y', alpha=0.3)
plt.ylim(0, 100) plt.ylim(0, 100)
for i, v in enumerate(completion_rates): for i, v in enumerate(completion_rates):
@@ -78,10 +78,10 @@ waiting_times = [
medium['TempoMédioEspera'].mean(), medium['TempoMédioEspera'].mean(),
high['TempoMédioEspera'].mean() high['TempoMédioEspera'].mean()
] ]
plt.bar(['Low', 'Medium', 'High'], waiting_times, color=['green', 'orange', 'red']) plt.bar(['Baixa', 'Média', 'Alta'], waiting_times, color=['green', 'orange', 'red'])
plt.ylabel('Average Waiting Time (s)') plt.ylabel('Tempo Médio de Espera (s)')
plt.title('Average Waiting Time vs Load') plt.title('Tempo Médio de Espera vs Carga')
plt.xlabel('Load Scenario') plt.xlabel('Cenário de Carga')
plt.grid(axis='y', alpha=0.3) plt.grid(axis='y', alpha=0.3)
for i, v in enumerate(waiting_times): for i, v in enumerate(waiting_times):
plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom') plt.text(i, v + 1, f'{v:.2f}s', ha='center', va='bottom')
@@ -91,44 +91,44 @@ plt.close()
# 4. Gráfico: Summary Statistics # 4. Gráfico: Summary Statistics
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10)) fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(14, 10))
loads = ['Low', 'Medium', 'High'] loads = ['Baixa', 'Média', 'Alta']
# Vehicles generated # Vehicles generated
ax1.bar(loads, [low['VeículosGerados'].mean(), medium['VeículosGerados'].mean(), high['VeículosGerados'].mean()], color=['green', 'orange', 'red']) ax1.bar(loads, [low['VeículosGerados'].mean(), medium['VeículosGerados'].mean(), high['VeículosGerados'].mean()], color=['green', 'orange', 'red'])
ax1.set_title('Vehicles Generated') ax1.set_title('Veículos Gerados')
ax1.set_ylabel('Count') ax1.set_ylabel('Quantidade')
ax1.grid(axis='y', alpha=0.3) ax1.grid(axis='y', alpha=0.3)
# Vehicles completed # Vehicles completed
ax2.bar(loads, [low['VeículosCompletados'].mean(), medium['VeículosCompletados'].mean(), high['VeículosCompletados'].mean()], color=['green', 'orange', 'red']) ax2.bar(loads, [low['VeículosCompletados'].mean(), medium['VeículosCompletados'].mean(), high['VeículosCompletados'].mean()], color=['green', 'orange', 'red'])
ax2.set_title('Vehicles Completed') ax2.set_title('Veículos Concluídos')
ax2.set_ylabel('Count') ax2.set_ylabel('Quantidade')
ax2.grid(axis='y', alpha=0.3) ax2.grid(axis='y', alpha=0.3)
# Min/Max dwelling time # Min/Max dwelling time
x = range(3) x = range(3)
width = 0.35 width = 0.35
ax3.bar([i - width/2 for i in x], [low['TempoMínimoSistema'].mean(), medium['TempoMínimoSistema'].mean(), high['TempoMínimoSistema'].mean()], width, label='Min', color='lightblue') ax3.bar([i - width/2 for i in x], [low['TempoMínimoSistema'].mean(), medium['TempoMínimoSistema'].mean(), high['TempoMínimoSistema'].mean()], width, label='Mín', color='lightblue')
ax3.bar([i + width/2 for i in x], [low['TempoMáximoSistema'].mean(), medium['TempoMáximoSistema'].mean(), high['TempoMáximoSistema'].mean()], width, label='Max', color='darkblue') ax3.bar([i + width/2 for i in x], [low['TempoMáximoSistema'].mean(), medium['TempoMáximoSistema'].mean(), high['TempoMáximoSistema'].mean()], width, label='Máx', color='darkblue')
ax3.set_title('Min/Max Dwelling Time') ax3.set_title('Tempo no Sistema Mín/Máx')
ax3.set_ylabel('Time (s)') ax3.set_ylabel('Tempo (s)')
ax3.set_xticks(x) ax3.set_xticks(x)
ax3.set_xticklabels(loads) ax3.set_xticklabels(loads)
ax3.legend() ax3.legend()
ax3.grid(axis='y', alpha=0.3) ax3.grid(axis='y', alpha=0.3)
# Performance summary # Performance summary
metrics = ['Dwelling\nTime', 'Waiting\nTime', 'Completion\nRate'] metrics = ['Tempo no\nSistema', 'Tempo de\nEspera', 'Taxa de\nConclusão']
low_vals = [low['TempoMédioSistema'].mean(), low['TempoMédioEspera'].mean(), low['TaxaConclusão'].mean()] low_vals = [low['TempoMédioSistema'].mean(), low['TempoMédioEspera'].mean(), low['TaxaConclusão'].mean()]
med_vals = [medium['TempoMédioSistema'].mean(), medium['TempoMédioEspera'].mean(), medium['TaxaConclusão'].mean()] med_vals = [medium['TempoMédioSistema'].mean(), medium['TempoMédioEspera'].mean(), medium['TaxaConclusão'].mean()]
high_vals = [high['TempoMédioSistema'].mean(), high['TempoMédioEspera'].mean(), high['TaxaConclusão'].mean()] high_vals = [high['TempoMédioSistema'].mean(), high['TempoMédioEspera'].mean(), high['TaxaConclusão'].mean()]
x = range(len(metrics)) x = range(len(metrics))
width = 0.25 width = 0.25
ax4.bar([i - width for i in x], low_vals, width, label='Low', color='green') ax4.bar([i - width for i in x], low_vals, width, label='Baixa', color='green')
ax4.bar(x, med_vals, width, label='Medium', color='orange') ax4.bar(x, med_vals, width, label='Média', color='orange')
ax4.bar([i + width for i in x], high_vals, width, label='High', color='red') ax4.bar([i + width for i in x], high_vals, width, label='Alta', color='red')
ax4.set_title('Performance Summary') ax4.set_title('Resumo de Desempenho')
ax4.set_xticks(x) ax4.set_xticks(x)
ax4.set_xticklabels(metrics) ax4.set_xticklabels(metrics)
ax4.legend() ax4.legend()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 91 KiB

View File

@@ -27,22 +27,24 @@ import sd.protocol.MessageProtocol;
import sd.protocol.SocketConnection; import sd.protocol.SocketConnection;
/** /**
* Destino final de todos os veículos da simulação (nó de saída S). * Ponto terminal da malha de simulação (Sink Node).
* * <p>
* <p>Opera como sumidouro da rede: * Este processo atua como o sumidouro da rede de filas. A sua função primária é
* <ol> * a <b>coleta de telemetria final</b>. Diferente das interseções, não encaminha veículos;
* <li>Recebe veículos que completaram a viagem * em vez disso, retira-os do sistema, calcula as métricas de latência "end-to-end"
* <li>Regista estatísticas finais (tempo total, espera, travessia) * (tempo no sistema, tempo de espera acumulado) e reporta ao Dashboard.
* <li>Envia métricas ao dashboard em tempo real * <p>
* </ol> * <b>Arquitetura de Concorrência:</b>
* * Utiliza um {@link ServerSocket} multithreaded para aceitar conexões simultâneas de
* <p>Participa no DES rastreando eventos, mas opera principalmente * qualquer interseção de fronteira (Cr1, Cr5, etc.) que envie veículos para fora da malha.
* de forma reativa, aguardando chegadas via socket.
*/ */
public class ExitNodeProcess { public class ExitNodeProcess {
// --- Configuration and Networking ---
private final SimulationConfig config; private final SimulationConfig config;
private ServerSocket serverSocket; private ServerSocket serverSocket;
/** Pool de threads elástica para tratamento de conexões de entrada. */
private final ExecutorService connectionHandlerPool; private final ExecutorService connectionHandlerPool;
// DES components // DES components
@@ -51,37 +53,37 @@ public class ExitNodeProcess {
private final EventLogger eventLogger; private final EventLogger eventLogger;
private Thread eventProcessorThread; private Thread eventProcessorThread;
/** Flag de controlo (volatile para visibilidade entre threads) */ /** Flag de controlo (volatile para visibilidade entre threads de I/O e lógica). */
private volatile boolean running; private volatile boolean running;
/** Instante de início da simulação (milissegundos) */ /** Instante de início da simulação (milissegundos) sincronizado com o Coordenador. */
private long simulationStartMillis; private long simulationStartMillis;
/** Contador de veículos que completaram a rota */ /** Contador atómico (via synchronized) de throughput total. */
private int totalVehiclesReceived; private int totalVehiclesReceived;
/** Tempo acumulado no sistema de todos os veículos */ /** Tempo acumulado no sistema (System Time) de todos os veículos. */
private double totalSystemTime; private double totalSystemTime;
/** Tempo acumulado em espera de todos os veículos */ /** Tempo acumulado em espera (Waiting Time) de todos os veículos. */
private double totalWaitingTime; private double totalWaitingTime;
/** Tempo acumulado em travessia de todos os veículos */ /** Tempo acumulado em travessia (Service Time) de todos os veículos. */
private double totalCrossingTime; private double totalCrossingTime;
/** Contagem de veículos por tipo */ /** Agregação por categoria de veículo. */
private final Map<VehicleType, Integer> vehicleTypeCount; private final Map<VehicleType, Integer> vehicleTypeCount;
/** Tempo de espera acumulado por tipo de veículo */ /** Latência acumulada por categoria. */
private final Map<VehicleType, Double> vehicleTypeWaitTime; private final Map<VehicleType, Double> vehicleTypeWaitTime;
/** Cliente socket para envio de estatísticas ao dashboard */ /** Cliente TCP persistente para push de métricas ao Dashboard. */
private SocketClient dashboardClient; private SocketClient dashboardClient;
/** /**
* Ponto de entrada do processo. * Bootstrap do processo ExitNode.
* * Carrega configuração, inicializa subsistemas e entra no loop de serviço.
* @param args args[0] (opcional) = caminho do ficheiro de configuração * * @param args Argumentos de CLI (caminho do config).
*/ */
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
@@ -117,13 +119,9 @@ public class ExitNodeProcess {
} }
/** /**
* Configura o Nó de Saída. * Instancia o nó de saída.
* * Prepara os acumuladores estatísticos e a infraestrutura de logging distribuído.
* Inicializamos os nossos contadores, preparamos a pool de threads para tratar * * @param config A configuração global da simulação.
* das ligações de veículos recebidas,
* e configuramos os componentes DES para rastreio de eventos.
*
* @param config A configuração da simulação.
*/ */
public ExitNodeProcess(SimulationConfig config) { public ExitNodeProcess(SimulationConfig config) {
this.config = config; this.config = config;
@@ -157,9 +155,8 @@ public class ExitNodeProcess {
} }
/** /**
* Tenta estabelecer uma ligação ao dashboard. * Estabelece o canal de controlo (Control Plane) com o Dashboard.
* Se for bem-sucedido, poderemos enviar estatísticas em tempo real. Se não, * Essencial para a visualização em tempo real das métricas de saída.
* apenas registamos localmente.
*/ */
public void initialize() { public void initialize() {
System.out.println("Connecting to dashboard..."); System.out.println("Connecting to dashboard...");
@@ -179,10 +176,9 @@ public class ExitNodeProcess {
} }
/** /**
* Starts the DES event processing thread. * Inicia a thread de processamento de eventos DES.
* Currently, ExitNode is primarily reactive (receives vehicles via network), * Embora o ExitNode seja primariamente reativo (Network-driven), o motor DES
* but maintains event queue for potential scheduled events and history * é mantido para consistência de relógio e agendamento de fim de simulação.
* tracking.
*/ */
private void startEventProcessor() { private void startEventProcessor() {
eventProcessorThread = new Thread(() -> { eventProcessorThread = new Thread(() -> {
@@ -218,8 +214,8 @@ public class ExitNodeProcess {
} }
/** /**
* Processes a discrete event based on its type. * Dispatcher de eventos discretos.
* Currently supports VEHICLE_EXIT and SIMULATION_END events. * Trata eventos de fim de simulação. Chegadas de veículos são tratadas via Socket.
*/ */
private void processEvent(SimulationEvent event) { private void processEvent(SimulationEvent event) {
try { try {
@@ -244,7 +240,7 @@ public class ExitNodeProcess {
} }
/** /**
* Handles simulation end event. * Executa a lógica de encerramento desencadeada pelo evento DES.
*/ */
private void handleSimulationEndEvent(SimulationEvent event) { private void handleSimulationEndEvent(SimulationEvent event) {
eventLogger.log(EventType.SIMULATION_STOPPED, "ExitNode", eventLogger.log(EventType.SIMULATION_STOPPED, "ExitNode",
@@ -256,9 +252,8 @@ public class ExitNodeProcess {
} }
/** /**
* Exports the complete event history for the exit node. * Exporta o histórico completo de eventos para auditoria.
* This satisfies the spec requirement: "Deve ser possível verificar a lista * Requisito funcional para verificação de trace.
* completa de eventos"
*/ */
public void exportEventHistory(String outputPath) { public void exportEventHistory(String outputPath) {
String history = eventQueue.exportEventHistory(); String history = eventQueue.exportEventHistory();
@@ -271,9 +266,8 @@ public class ExitNodeProcess {
} }
/** /**
* Schedules a simulation end event at the specified time. * Agenda o fim determinístico da simulação.
* * * @param endTime Tempo virtual de paragem.
* @param endTime The simulation time when the simulation should end
*/ */
public void scheduleSimulationEnd(double endTime) { public void scheduleSimulationEnd(double endTime) {
SimulationEvent endEvent = new SimulationEvent( SimulationEvent endEvent = new SimulationEvent(
@@ -285,22 +279,16 @@ public class ExitNodeProcess {
} }
/** /**
* Abre o socket do servidor e começa a escutar por veículos. * Inicia o servidor TCP em modo de bloqueio (Blocking I/O).
* * @throws IOException Se ocorrer erro no bind da porta.
* Este é o loop principal. Aceitamos ligações das interseções (de onde vêm os
* veículos)
* e passamo-las para a nossa pool de threads para processamento.
*
* @throws IOException Se não conseguirmos fazer bind à porta.
*/ */
public void start() throws IOException { public void start() throws IOException {
start(true); // Default to DES mode start(true); // Default to DES mode
} }
/** /**
* Starts the exit node process. * Inicia o processo com opção de ativar o rastreio DES.
* * * @param useDES Se verdadeiro, ativa a thread do processador de eventos.
* @param useDES If true, starts event processor for DES mode tracking
*/ */
public void start(boolean useDES) throws IOException { public void start(boolean useDES) throws IOException {
int port = config.getExitPort(); int port = config.getExitPort();
@@ -310,15 +298,15 @@ public class ExitNodeProcess {
System.out.println("Exit node started on port " + port); System.out.println("Exit node started on port " + port);
if (useDES) { if (useDES) {
// Note: ExitNode is primarily reactive (network-driven), but maintains
// event queue for simulation end events and history tracking
System.out.println("Running in DES mode (event history tracking enabled)"); System.out.println("Running in DES mode (event history tracking enabled)");
} }
System.out.println("Waiting for vehicles...\\n"); System.out.println("Waiting for vehicles...\\n");
// Loop de aceitação principal
while (running) { while (running) {
try { try {
Socket clientSocket = serverSocket.accept(); Socket clientSocket = serverSocket.accept();
// Delega o processamento da conexão para o Thread Pool
connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket)); connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket));
} catch (IOException e) { } catch (IOException e) {
if (running) { if (running) {
@@ -329,12 +317,11 @@ public class ExitNodeProcess {
} }
/** /**
* Trata uma ligação de uma interseção. * Worker method para tratar uma conexão persistente vinda de uma interseção.
* * <p>
* Mantemos a ligação aberta e escutamos por mensagens `VEHICLE_TRANSFER`. * Mantém o socket aberto e consome mensagens num loop até que a conexão seja fechada
* Cada mensagem contém um veículo que acabou de terminar a sua viagem. * pelo remetente. Responsável pela desserialização polimórfica (JSON/Gson).
* * * @param clientSocket O socket conectado.
* @param clientSocket O socket ligado à interseção.
*/ */
private void handleIncomingConnection(Socket clientSocket) { private void handleIncomingConnection(Socket clientSocket) {
String clientAddress = clientSocket.getInetAddress().getHostAddress(); String clientAddress = clientSocket.getInetAddress().getHostAddress();
@@ -350,14 +337,14 @@ public class ExitNodeProcess {
" from " + message.getSourceNode()); " from " + message.getSourceNode());
if (message.getType() == MessageType.SIMULATION_START) { if (message.getType() == MessageType.SIMULATION_START) {
// Coordinator sends start time - use it instead of our local start // Sincronização de relógio com o Coordenador
simulationStartMillis = ((Number) message.getPayload()).longValue(); simulationStartMillis = ((Number) message.getPayload()).longValue();
System.out.println("[Exit] Simulation start time synchronized"); System.out.println("[Exit] Simulation start time synchronized");
} else if (message.getType() == MessageType.VEHICLE_TRANSFER) { } else if (message.getType() == MessageType.VEHICLE_TRANSFER) {
Object payload = message.getPayload(); Object payload = message.getPayload();
System.out.println("[Exit] Payload type: " + payload.getClass().getName()); System.out.println("[Exit] Payload type: " + payload.getClass().getName());
// Handle Gson LinkedHashMap // Tratamento de artefatos de desserialização do Gson (LinkedTreeMap -> POJO)
Vehicle vehicle; Vehicle vehicle;
if (payload instanceof com.google.gson.internal.LinkedTreeMap || if (payload instanceof com.google.gson.internal.LinkedTreeMap ||
payload instanceof java.util.LinkedHashMap) { payload instanceof java.util.LinkedHashMap) {
@@ -390,26 +377,21 @@ public class ExitNodeProcess {
} }
/** /**
* Processa um veículo que acabou de sair do sistema. * Processa atomicamente a saída de um veículo.
* * <p>
* Calculamos quanto tempo demorou, atualizamos as nossas estatísticas globais e * <b>Secção Crítica:</b> Método {@code synchronized} para garantir que a atualização
* notificamos o dashboard. * das estatísticas globais (totalSystemTime, contadores) é atómica, prevenindo
* Este método é sincronizado porque múltiplos veículos podem chegar ao mesmo * Race Conditions quando múltiplos veículos chegam simultaneamente de interseções diferentes.
* tempo. * * @param vehicle O veículo que completou a rota.
*
* @param vehicle O veículo que completou a sua rota.
*/ */
private synchronized void processExitingVehicle(Vehicle vehicle) { private synchronized void processExitingVehicle(Vehicle vehicle) {
totalVehiclesReceived++; totalVehiclesReceived++;
// Use simulation time instead of wall-clock time // Cálculo de métricas finais baseadas no tempo virtual de simulação acumulado no veículo
// System time = total time vehicle spent in system (wait + crossing times)
// This represents the actual simulation time elapsed, not real-time
double waitTime = vehicle.getTotalWaitingTime(); double waitTime = vehicle.getTotalWaitingTime();
double crossingTime = vehicle.getTotalCrossingTime(); double crossingTime = vehicle.getTotalCrossingTime();
double systemTime = waitTime + crossingTime; double systemTime = waitTime + crossingTime;
// Store times in seconds, will be converted to ms when sending to dashboard
totalSystemTime += systemTime; totalSystemTime += systemTime;
totalWaitingTime += waitTime; totalWaitingTime += waitTime;
totalCrossingTime += crossingTime; totalCrossingTime += crossingTime;
@@ -421,23 +403,20 @@ public class ExitNodeProcess {
System.out.printf("[Exit] Vehicle %s completed (type=%s, system_time=%.2fs, wait=%.2fs, crossing=%.2fs)%n", System.out.printf("[Exit] Vehicle %s completed (type=%s, system_time=%.2fs, wait=%.2fs, crossing=%.2fs)%n",
vehicle.getId(), vehicle.getType(), systemTime, waitTime, crossingTime); vehicle.getId(), vehicle.getType(), systemTime, waitTime, crossingTime);
// Log vehicle exit // Logging estruturado
EventLogger.getInstance().logVehicle(EventType.VEHICLE_EXITED, "ExitNode", vehicle.getId(), EventLogger.getInstance().logVehicle(EventType.VEHICLE_EXITED, "ExitNode", vehicle.getId(),
String.format("Completed - System: %.2fs, Wait: %.2fs, Crossing: %.2fs", systemTime, waitTime, String.format("Completed - System: %.2fs, Wait: %.2fs, Crossing: %.2fs", systemTime, waitTime,
crossingTime)); crossingTime));
// Complete vehicle trace if tracking // Finaliza o trace individual do veículo
VehicleTracer.getInstance().logExit(vehicle, systemTime); VehicleTracer.getInstance().logExit(vehicle, systemTime);
// Send stats after every vehicle to ensure dashboard updates quickly // Push imediato para o Dashboard para visualização em tempo real
sendStatsToDashboard(); sendStatsToDashboard();
} }
/** /**
* Envia as estatísticas mais recentes para o dashboard. * Constrói e transmite o DTO de atualização de estatísticas.
*
* Empacotamos as contagens totais e os tempos médios num `StatsUpdatePayload`
* e enviamo-lo.
*/ */
private void sendStatsToDashboard() { private void sendStatsToDashboard() {
if (dashboardClient == null || !dashboardClient.isConnected()) { if (dashboardClient == null || !dashboardClient.isConnected()) {
@@ -448,29 +427,28 @@ public class ExitNodeProcess {
// Create stats payload // Create stats payload
StatsUpdatePayload payload = new StatsUpdatePayload(); StatsUpdatePayload payload = new StatsUpdatePayload();
// Set global stats - convert seconds to milliseconds // Set global stats - convert seconds to milliseconds for display consistency
payload.setTotalVehiclesCompleted(totalVehiclesReceived); payload.setTotalVehiclesCompleted(totalVehiclesReceived);
payload.setTotalSystemTime((long) (totalSystemTime * 1000.0)); // s -> ms payload.setTotalSystemTime((long) (totalSystemTime * 1000.0));
payload.setTotalWaitingTime((long) (totalWaitingTime * 1000.0)); // s -> ms payload.setTotalWaitingTime((long) (totalWaitingTime * 1000.0));
// Set intersection-like stats so it shows up correctly in the dashboard table // Hack: Usar campos de interseção para mostrar throughput no dashboard
payload.setIntersectionArrivals(totalVehiclesReceived); payload.setIntersectionArrivals(totalVehiclesReceived);
payload.setIntersectionDepartures(totalVehiclesReceived); payload.setIntersectionDepartures(totalVehiclesReceived);
payload.setIntersectionQueueSize(0); payload.setIntersectionQueueSize(0);
// Set vehicle type stats // Detailed breakdown
Map<VehicleType, Integer> typeCounts = new HashMap<>(); Map<VehicleType, Integer> typeCounts = new HashMap<>();
Map<VehicleType, Long> typeWaitTimes = new HashMap<>(); Map<VehicleType, Long> typeWaitTimes = new HashMap<>();
for (VehicleType type : VehicleType.values()) { for (VehicleType type : VehicleType.values()) {
typeCounts.put(type, vehicleTypeCount.get(type)); typeCounts.put(type, vehicleTypeCount.get(type));
typeWaitTimes.put(type, (long) (vehicleTypeWaitTime.get(type) * 1000.0)); // s -> ms typeWaitTimes.put(type, (long) (vehicleTypeWaitTime.get(type) * 1000.0));
} }
payload.setVehicleTypeCounts(typeCounts); payload.setVehicleTypeCounts(typeCounts);
payload.setVehicleTypeWaitTimes(typeWaitTimes); payload.setVehicleTypeWaitTimes(typeWaitTimes);
// Send message
Message message = new Message( Message message = new Message(
MessageType.STATS_UPDATE, MessageType.STATS_UPDATE,
"ExitNode", "ExitNode",
@@ -489,9 +467,8 @@ public class ExitNodeProcess {
} }
/** /**
* Encerra graciosamente o processo. * Encerramento gracioso do processo.
* * Fecha sockets, termina a pool de threads e liberta recursos.
* Imprimimos as estatísticas finais, fechamos ligações e limpamos threads.
*/ */
public void shutdown() { public void shutdown() {
System.out.println("\n[Exit] Shutting down..."); System.out.println("\n[Exit] Shutting down...");
@@ -527,9 +504,7 @@ public class ExitNodeProcess {
} }
/** /**
* Imprime um resumo dos resultados da simulação na consola. * Imprime o relatório final no stdout.
* Isto dá-nos uma visão rápida de como a simulação correu (médias, contagens de
* veículos, etc.).
*/ */
private void printFinalStatistics() { private void printFinalStatistics() {
System.out.println("\n=== EXIT NODE STATISTICS ==="); System.out.println("\n=== EXIT NODE STATISTICS ===");

View File

@@ -33,19 +33,22 @@ import sd.protocol.SocketConnection;
import sd.serialization.SerializationException; import sd.serialization.SerializationException;
/** /**
* Representa uma única interseção na nossa simulação de tráfego distribuída. * Representa um nó de processamento autónomo na malha de simulação distribuída
* * (Worker Node).
* Esta classe opera como um processo independente (uma aplicação Java autónoma) * <p>
* e é responsável por: * Esta classe implementa a lógica de uma interseção rodoviária utilizando uma
* 1. Gerir os semáforos e a sua temporização. * arquitetura híbrida:
* 2. Processar as chegadas e partidas de veículos. * <ol>
* 3. Comunicar com outras interseções e com o dashboard. * <li><b>Reativa (Network I/O):</b> Threads dedicadas aceitam conexões TCP e
* * injetam veículos nas filas de entrada assim que chegam.</li>
* Utiliza uma abordagem de Simulação de Eventos Discretos (DES), onde as * <li><b>Proativa (DES Engine):</b> Uma thread de processamento de eventos gere
* mudanças de estado (como semáforos a mudar para verde) * a lógica temporal (mudança de semáforos, tempos de travessia) baseada num
* são agendadas como eventos numa fila de prioridade, em vez de depender de * relógio virtual monotónico.</li>
* loops contínuos ou threads em espera. * </ol>
* Isto garante uma temporização precisa e uma execução eficiente. * <p>
* A sincronização entre a chegada assíncrona de veículos (Rede) e o
* processamento determinístico (DES) é gerida através de estruturas de dados
* concorrentes e bloqueios justos (Fair Locks).
*/ */
public class IntersectionProcess { public class IntersectionProcess {
@@ -57,48 +60,56 @@ public class IntersectionProcess {
private ServerSocket serverSocket; private ServerSocket serverSocket;
/**
* Tabela de encaminhamento dinâmico para conexões de saída (Next-Hop Cache).
*/
private final Map<String, SocketConnection> outgoingConnections; private final Map<String, SocketConnection> outgoingConnections;
/** Pool de threads para tratamento de I/O de rede (entrada de veículos). */
private final ExecutorService connectionHandlerPool; private final ExecutorService connectionHandlerPool;
private ScheduledExecutorService statsExecutor; private ScheduledExecutorService statsExecutor;
private ScheduledExecutorService departureExecutor; private ScheduledExecutorService departureExecutor;
private volatile boolean running; private volatile boolean running;
/** Escala temporal para visualização: tempo_real = tempo_simulado * escala */ /** Fator de dilatação temporal (0.0 = Velocidade Máxima, 1.0 = Tempo Real). */
private double timeScale; private double timeScale;
/** Relógio central da simulação */ // --- Componentes DES (Simulação de Eventos Discretos) ---
/** Relógio central virtual da interseção. */
private final SimulationClock clock; private final SimulationClock clock;
/** Fila de eventos discretos agendados */ /** Fila de prioridade (Min-Heap) para agendamento temporal de eventos. */
private final EventQueue eventQueue; private final EventQueue eventQueue;
/** Sistema de registo de eventos */
private final EventLogger eventLogger; private final EventLogger eventLogger;
/** Thread dedicada ao processamento sequencial de eventos DES */ /** Thread "Single-Writer" responsável pela mutação de estado da simulação. */
private Thread eventProcessorThread; private Thread eventProcessorThread;
/** /**
* Lock para exclusão mútua entre semáforos. * Mecanismo de exclusão mútua para controlo de fases semafóricas.
* Garante que apenas um semáforo pode estar verde de cada vez nesta interseção. * Configurado com política de justiça (fairness=true) para evitar inanição
* (starvation) de direções com menos tráfego.
*/ */
private final Lock trafficCoordinationLock; private final Lock trafficCoordinationLock;
/** /**
* Regista qual direção tem atualmente o sinal verde. * Estado volátil que indica a direção ativa. Apenas uma direção pode deter o
* {@code null} significa que todos os semáforos estão vermelhos. * token 'Green' por vez.
*/ */
private volatile String currentGreenDirection; private volatile String currentGreenDirection;
private SocketClient dashboardClient; private SocketClient dashboardClient;
// Métricas voláteis para acesso atómico sem bloqueio
private volatile int totalArrivals = 0; private volatile int totalArrivals = 0;
private volatile int totalDepartures = 0; private volatile int totalDepartures = 0;
/** /**
* Inicializa o processo da interseção. * Inicializa o processo da interseção, carregando a topologia e preparando o
* motor DES.
* *
* @param intersectionId O identificador único para esta interseção (ex: "Cr1"). * @param intersectionId O identificador único na malha (ex: "Cr1").
* @param configFilePath O caminho para o ficheiro de configuração. * @param configFilePath Caminho para o ficheiro de propriedades.
* @throws IOException Se houver algum problema ao ler a configuração. * @throws IOException Se falhar o bind da porta ou leitura de config.
*/ */
public IntersectionProcess(String intersectionId, String configFilePath) throws IOException { public IntersectionProcess(String intersectionId, String configFilePath) throws IOException {
this.intersectionId = intersectionId; this.intersectionId = intersectionId;
@@ -127,13 +138,16 @@ public class IntersectionProcess {
} }
/** /**
* Inicia o ciclo de processamento de eventos. * Inicia o ciclo principal do motor de simulação (DES Engine Loop).
* * <p>
* Esta thread é o coração do modelo DES para esta interseção. Retira eventos da * Executa o ciclo "Fetch-Decode-Execute":
* fila * <ol>
* e executa-os por ordem cronológica. Enquanto a thread principal trata das * <li>Remove o evento com menor timestamp da fila (Fetch).</li>
* operações de I/O de rede (receção de veículos), * <li>Avança o relógio virtual para o tempo do evento.</li>
* esta thread trata da lógica da simulação (semáforos, travessias de veículos). * <li>Aplica atraso artificial se {@code timeScale > 0} (para visualização
* humana).</li>
* <li>Despacha o evento para o manipulador apropriado (Execute).</li>
* </ol>
*/ */
private void startEventProcessor() { private void startEventProcessor() {
eventProcessorThread = new Thread(() -> { eventProcessorThread = new Thread(() -> {
@@ -145,9 +159,9 @@ public class IntersectionProcess {
while (running) { while (running) {
SimulationEvent event = eventQueue.poll(); SimulationEvent event = eventQueue.poll();
if (event == null) { if (event == null) {
// No events currently, wait a bit before checking again // Backoff exponencial ou sleep curto para evitar busy-waiting em idle
try { try {
Thread.sleep(50); // Short sleep to avoid busy-waiting Thread.sleep(50);
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
break; break;
@@ -155,7 +169,7 @@ public class IntersectionProcess {
continue; continue;
} }
// Apply time scaling for visualization // Aplicação de escala temporal (Throttle)
if (timeScale > 0) { if (timeScale > 0) {
double simTimeDelta = event.getTimestamp() - lastTime; double simTimeDelta = event.getTimestamp() - lastTime;
long realDelayMs = (long) (simTimeDelta * timeScale * 1000); long realDelayMs = (long) (simTimeDelta * timeScale * 1000);
@@ -170,10 +184,10 @@ public class IntersectionProcess {
lastTime = event.getTimestamp(); lastTime = event.getTimestamp();
} }
// Advance clock to event time // Atualização atómica do tempo de simulação
clock.advanceTo(event.getTimestamp()); clock.advanceTo(event.getTimestamp());
// Process the event // Processamento polimórfico
processEvent(event); processEvent(event);
} }
@@ -185,10 +199,12 @@ public class IntersectionProcess {
} }
/** /**
* Processa um evento da fila de simulação. * Despachante central de eventos.
* Cada tipo de evento é encaminhado para o seu tratador específico. * <p>
* Encaminha o evento para a lógica de negócio específica baseada no tipo
* {@link DESEventType}.
* *
* @param event o evento a processar * @param event O evento de simulação a ser processado.
*/ */
private void processEvent(SimulationEvent event) { private void processEvent(SimulationEvent event) {
try { try {
@@ -198,8 +214,8 @@ public class IntersectionProcess {
break; break;
case VEHICLE_ARRIVAL: case VEHICLE_ARRIVAL:
// Vehicle arrivals are still handled via network messages // Chegadas são tratadas reativamente via Socket, mas eventos podem ser usados
// This event type is for internal scheduling if needed // para métricas
break; break;
case VEHICLE_CROSSING_START: case VEHICLE_CROSSING_START:
@@ -225,12 +241,18 @@ public class IntersectionProcess {
} }
/** /**
* Trata da mudança dos semáforos. * Gere a máquina de estados dos semáforos.
* <p>
* O fluxo de execução é o seguinte:
* <ol>
* <li>Atualiza o estado do semáforo (Verde <-> Vermelho).</li>
* <li>Se o novo estado for Verde: Calcula a capacidade de vazão e agenda
* travessias (Service Events).</li>
* <li>Agenda recursivamente a próxima mudança de estado para manter o ciclo
* infinito.</li>
* </ol>
* *
* Quando um semáforo muda de estado, registamos o evento, atualizamos o modelo * @param event O evento que desencadeou a mudança de estado.
* e, se tiver mudado para VERDE,
* verificamos imediatamente se há veículos à espera para atravessar.
* Também agendamos aqui o *próximo* evento de mudança, mantendo o ciclo ativo.
*/ */
private void handleTrafficLightChangeEvent(SimulationEvent event) { private void handleTrafficLightChangeEvent(SimulationEvent event) {
TrafficLightEvent tlEvent = (TrafficLightEvent) event.getPayload(); TrafficLightEvent tlEvent = (TrafficLightEvent) event.getPayload();
@@ -252,12 +274,12 @@ public class IntersectionProcess {
String.format("Direction %s changed to %s at time %.2f", String.format("Direction %s changed to %s at time %.2f",
direction, newState, event.getTimestamp())); direction, newState, event.getTimestamp()));
// If light turned GREEN, process queued vehicles // Processamento de lote (Batch Processing) para a fase Verde
if (newState == TrafficLightState.GREEN) { if (newState == TrafficLightState.GREEN) {
processQueuedVehiclesForLight(light, event.getTimestamp()); processQueuedVehiclesForLight(light, event.getTimestamp());
} }
// Schedule next state change // Agendamento do próximo ciclo (Feedback Loop)
double nextChangeTime = event.getTimestamp() + double nextChangeTime = event.getTimestamp() +
(newState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime()); (newState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime());
@@ -269,19 +291,19 @@ public class IntersectionProcess {
} }
/** /**
* Processa a fila de veículos quando um semáforo fica verde. * Calcula a vazão da interseção durante uma fase verde.
* * <p>
* <p>Para cada veículo na fila:</p> * Implementa uma lógica de previsão ("Look-ahead"):
* <ol> * <ol>
* <li>Calcula o tempo de travessia com base no tipo de veículo</li> * <li>Itera sobre a fila de espera do semáforo.</li>
* <li>Verifica se cabe na duração restante do sinal verde</li> * <li>Calcula o tempo de serviço acumulado (Service Time) baseado no tipo de
* <li>Agenda o evento de partida do veículo</li> * veículo.</li>
* <li>Agenda a partida apenas se o veículo couber na janela temporal restante
* do sinal verde.</li>
* </ol> * </ol>
* *
* <p>Os veículos que não couberem no tempo verde ficam à espera do próximo ciclo.</p> * @param light O semáforo ativo.
* * @param currentTime O instante de início da fase verde.
* @param light o semáforo que acabou de ficar verde
* @param currentTime o tempo atual da simulação em segundos
*/ */
private void processQueuedVehiclesForLight(TrafficLight light, double currentTime) { private void processQueuedVehiclesForLight(TrafficLight light, double currentTime) {
double greenDuration = light.getGreenTime(); double greenDuration = light.getGreenTime();
@@ -291,30 +313,29 @@ public class IntersectionProcess {
System.out.printf("[%s] Processing queue for %s (GREEN for %.2fs, queue size: %d, currentTime=%.2f)%n", System.out.printf("[%s] Processing queue for %s (GREEN for %.2fs, queue size: %d, currentTime=%.2f)%n",
intersectionId, light.getId(), greenDuration, queueSize, currentTime); intersectionId, light.getId(), greenDuration, queueSize, currentTime);
// Process vehicles while queue not empty and within green light duration // Algoritmo de esvaziamento de fila baseado em Time Budget
while (light.getQueueSize() > 0) { while (light.getQueueSize() > 0) {
// Calculate crossing time for next vehicle (peek at queue size to estimate) // Estimativa inicial (optimista)
// We'll use LIGHT vehicle as default for estimation
double crossingTime = config.getLightVehicleCrossingTime(); double crossingTime = config.getLightVehicleCrossingTime();
// Check if another vehicle can fit in remaining green time // Verificação de limite de tempo (Hard Deadline do sinal vermelho)
if (timeOffset + crossingTime > greenDuration) { if (timeOffset + crossingTime > greenDuration) {
break; // No more vehicles can cross this green phase break; // Veículo não cabe no ciclo atual
} }
// Remove vehicle from queue with current simulation time // Commit: Remove da fila
Vehicle vehicle = light.removeVehicle(currentTime + timeOffset); Vehicle vehicle = light.removeVehicle(currentTime + timeOffset);
if (vehicle == null) if (vehicle == null)
break; break;
// Get actual crossing time for this vehicle // Recálculo preciso baseado no tipo real do veículo
crossingTime = getCrossingTimeForVehicle(vehicle); crossingTime = getCrossingTimeForVehicle(vehicle);
// Schedule crossing // Agendamento do evento futuro de término de travessia
double crossingStartTime = currentTime + timeOffset; double crossingStartTime = currentTime + timeOffset;
scheduleVehicleCrossing(vehicle, crossingStartTime, crossingTime); scheduleVehicleCrossing(vehicle, crossingStartTime, crossingTime);
// Update offset for next vehicle // Incrementa offset para serializar as travessias (Head-of-Line Blocking)
timeOffset += crossingTime; timeOffset += crossingTime;
System.out.printf("[%s] Scheduled vehicle %s to cross at t=%.2f (duration=%.2fs)%n", System.out.printf("[%s] Scheduled vehicle %s to cross at t=%.2f (duration=%.2fs)%n",
@@ -323,12 +344,11 @@ public class IntersectionProcess {
} }
/** /**
* Agenda a travessia e partida de um veículo. * Cria e agenda o evento de conclusão de travessia (Partida).
* Cria um evento de fim de travessia agendado para o tempo correto.
* *
* @param vehicle o veículo que vai atravessar * @param vehicle O veículo que está a atravessar.
* @param startTime quando a travessia começa (segundos de simulação) * @param startTime Instante de início da travessia.
* @param crossingDuration quanto tempo demora a atravessar (segundos) * @param crossingDuration Duração estimada da travessia.
*/ */
private void scheduleVehicleCrossing(Vehicle vehicle, double startTime, double crossingDuration) { private void scheduleVehicleCrossing(Vehicle vehicle, double startTime, double crossingDuration) {
// Schedule crossing end (when vehicle departs) // Schedule crossing end (when vehicle departs)
@@ -347,11 +367,10 @@ public class IntersectionProcess {
} }
/** /**
* Calcula o tempo de travessia com base no tipo de veículo. * Determina o custo temporal da travessia baseado na física do veículo.
* Bicicletas são mais rápidas, veículos pesados mais lentos.
* *
* @param vehicle o veículo para calcular o tempo * @param vehicle O veículo em questão.
* @return tempo de travessia em segundos * @return O tempo em segundos necessário para atravessar a interseção.
*/ */
private double getCrossingTimeForVehicle(Vehicle vehicle) { private double getCrossingTimeForVehicle(Vehicle vehicle) {
return switch (vehicle.getType()) { return switch (vehicle.getType()) {
@@ -363,36 +382,45 @@ public class IntersectionProcess {
} }
/** /**
* Trata o evento de início de travessia de um veículo. * Manipula o evento de início de travessia de um veículo.
* (Implementação futura - atualmente apenas regista o evento) * <p>
* Atualmente serve como placeholder para lógica futura de animação ou
* ocupação de zonas críticas na interseção.
* *
* @param event o evento de início de travessia * @param event O evento de início de travessia.
*/ */
private void handleVehicleCrossingStartEvent(SimulationEvent event) { private void handleVehicleCrossingStartEvent(SimulationEvent event) {
// Implementation will depend on how vehicle crossing is modeled // Placeholder para lógica futura de animação ou ocupação de zona crítica
// For now, log the event
eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId, eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId,
"Vehicle crossing started at time " + event.getTimestamp()); "Vehicle crossing started at time " + event.getTimestamp());
} }
/** /**
* Trata o fim da travessia de um veículo pela interseção. * Finaliza a lógica de travessia e inicia a transferência (handover) para o
* Atualiza estatísticas, regista o tempo de travessia e envia o veículo * próximo nó.
* para o próximo destino na sua rota. * <p>
* Este método é invocado quando o tempo de travessia expira no relógio virtual.
* Executa as seguintes ações:
* <ol>
* <li>Atualiza as métricas de tempo de travessia do veículo.</li>
* <li>Incrementa contadores locais de veículos processados.</li>
* <li>Transfere a responsabilidade do veículo para a rede, enviando-o ao
* próximo destino.</li>
* </ol>
* *
* @param event evento contendo o veículo que terminou a travessia * @param event O evento de fim de travessia.
*/ */
private void handleVehicleCrossingEndEvent(SimulationEvent event) { private void handleVehicleCrossingEndEvent(SimulationEvent event) {
Vehicle vehicle = (Vehicle) event.getPayload(); Vehicle vehicle = (Vehicle) event.getPayload();
// Add crossing time to vehicle stats // Atualiza métricas do veículo
double crossingTime = getCrossingTimeForVehicle(vehicle); double crossingTime = getCrossingTimeForVehicle(vehicle);
vehicle.addCrossingTime(crossingTime); vehicle.addCrossingTime(crossingTime);
// Update intersection statistics // Atualiza métricas locais
intersection.incrementVehiclesSent(); intersection.incrementVehiclesSent();
// Send vehicle to next destination // Handover: Transfere a responsabilidade do veículo para a rede
sendVehicleToNextDestination(vehicle); sendVehicleToNextDestination(vehicle);
eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId, eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId,
@@ -400,10 +428,9 @@ public class IntersectionProcess {
} }
/** /**
* Trata o evento de fim da simulação. * Finaliza a execução do processo de simulação.
* Define a flag de execução como falsa para terminar o processamento.
* *
* @param event o evento de fim de simulação * @param event O evento de fim de simulação.
*/ */
private void handleSimulationEndEvent(SimulationEvent event) { private void handleSimulationEndEvent(SimulationEvent event) {
eventLogger.log(sd.logging.EventType.SIMULATION_STOPPED, intersectionId, eventLogger.log(sd.logging.EventType.SIMULATION_STOPPED, intersectionId,
@@ -412,10 +439,9 @@ public class IntersectionProcess {
} }
/** /**
* Exporta o histórico completo de eventos para um ficheiro. * Exporta o histórico completo de eventos para análise post-mortem.
* Útil para análise posterior e debugging da simulação.
* *
* @param outputPath caminho do ficheiro onde guardar o histórico * @param outputPath O caminho do ficheiro onde o histórico será guardado.
*/ */
public void exportEventHistory(String outputPath) { public void exportEventHistory(String outputPath) {
String history = eventQueue.exportEventHistory(); String history = eventQueue.exportEventHistory();
@@ -427,7 +453,12 @@ public class IntersectionProcess {
} }
} }
// Main entry point for running an intersection process /**
* Ponto de entrada principal da aplicação.
*
* @param args Argumentos da linha de comando (ID da interseção e ficheiro de
* configuração opcional).
*/
public static void main(String[] args) { public static void main(String[] args) {
if (args.length < 1) { if (args.length < 1) {
System.err.println("Usage: java IntersectionProcess <intersectionId> [configFile]"); System.err.println("Usage: java IntersectionProcess <intersectionId> [configFile]");
@@ -456,6 +487,12 @@ public class IntersectionProcess {
} }
} }
/**
* Realiza o bootstrap dos componentes lógicos e de rede da interseção.
* <p>
* Inclui a criação de semáforos, configuração de encaminhamento e conexão ao
* Dashboard.
*/
public void initialize() { public void initialize() {
System.out.println("\n[" + intersectionId + "] Initializing intersection..."); System.out.println("\n[" + intersectionId + "] Initializing intersection...");
@@ -469,7 +506,7 @@ public class IntersectionProcess {
} }
/** /**
* Estabelece ligação ao servidor do dashboard para reportar estatísticas. * Estabelece a conexão com o Dashboard para envio de telemetria em tempo real.
*/ */
private void connectToDashboard() { private void connectToDashboard() {
try { try {
@@ -493,9 +530,7 @@ public class IntersectionProcess {
} }
/** /**
* Cria os semáforos para esta interseção com base nas suas ligações físicas. * Inicializa os semáforos da interseção com base na configuração carregada.
* Cada interseção tem um número e direções de semáforos diferentes de acordo
* com a topologia da rede.
*/ */
private void createTrafficLights() { private void createTrafficLights() {
System.out.println("\n[" + intersectionId + "] Creating traffic lights..."); System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
@@ -524,6 +559,13 @@ public class IntersectionProcess {
} }
} }
/**
* Obtém a configuração específica para esta interseção a partir da configuração
* global.
*
* @return O objeto de configuração da interseção.
* @throws RuntimeException Se a configuração estiver em falta.
*/
private SimulationConfig.IntersectionConfig getIntersectionConfig() { private SimulationConfig.IntersectionConfig getIntersectionConfig() {
if (config.getNetworkConfig() == null || config.getNetworkConfig().getIntersections() == null) { if (config.getNetworkConfig() == null || config.getNetworkConfig().getIntersections() == null) {
throw new RuntimeException("Network configuration not loaded or empty."); throw new RuntimeException("Network configuration not loaded or empty.");
@@ -534,6 +576,11 @@ public class IntersectionProcess {
.orElseThrow(() -> new RuntimeException("Intersection config not found for " + intersectionId)); .orElseThrow(() -> new RuntimeException("Intersection config not found for " + intersectionId));
} }
/**
* Configura a tabela de encaminhamento (routing) da interseção.
* <p>
* Define para cada destino qual a direção de saída (semáforo) correspondente.
*/
private void configureRouting() { private void configureRouting() {
System.out.println("\n[" + intersectionId + "] Configuring routing..."); System.out.println("\n[" + intersectionId + "] Configuring routing...");
@@ -555,11 +602,10 @@ public class IntersectionProcess {
} }
/** /**
* Solicita permissão para um semáforo ficar verde. * Primitiva de bloqueio: Solicita acesso exclusivo à zona crítica da
* Bloqueia até que a permissão seja concedida (nenhum outro semáforo está * interseção.
* verde).
* *
* @param direction A direção que solicita o sinal verde * @param direction A direção que solicita passagem.
*/ */
public void requestGreenLight(String direction) { public void requestGreenLight(String direction) {
trafficCoordinationLock.lock(); trafficCoordinationLock.lock();
@@ -567,10 +613,9 @@ public class IntersectionProcess {
} }
/** /**
* Liberta a permissão de sinal verde, permitindo que outro semáforo fique * Primitiva de bloqueio: Liberta o acesso exclusivo à zona crítica.
* verde.
* *
* @param direction A direção que liberta o sinal verde * @param direction A direção que está a libertar a passagem.
*/ */
public void releaseGreenLight(String direction) { public void releaseGreenLight(String direction) {
if (direction.equals(currentGreenDirection)) { if (direction.equals(currentGreenDirection)) {
@@ -580,8 +625,10 @@ public class IntersectionProcess {
} }
/** /**
* Modo DES: Agenda os eventos iniciais de mudança de semáforo. * Inicializa o estado dos semáforos no arranque da simulação (t=0).
* Isto substitui a antiga abordagem baseada em threads startTrafficLights(). * <p>
* Garante que apenas um semáforo começa em Verde e os restantes em Vermelho,
* agendando os eventos iniciais na fila do DES.
*/ */
private void scheduleInitialTrafficLightEvents() { private void scheduleInitialTrafficLightEvents() {
System.out.println("\n[" + intersectionId + "] Scheduling initial traffic light events (DES mode)..."); System.out.println("\n[" + intersectionId + "] Scheduling initial traffic light events (DES mode)...");
@@ -592,12 +639,12 @@ public class IntersectionProcess {
for (TrafficLight light : intersection.getTrafficLights()) { for (TrafficLight light : intersection.getTrafficLights()) {
String direction = light.getDirection(); String direction = light.getDirection();
// Set initial state (first light starts green, others red) // Lógica de arranque: Primeiro da lista = Verde, outros = Vermelho
boolean isFirstLight = intersection.getTrafficLights().indexOf(light) == 0; boolean isFirstLight = intersection.getTrafficLights().indexOf(light) == 0;
TrafficLightState initialState = isFirstLight ? TrafficLightState.GREEN : TrafficLightState.RED; TrafficLightState initialState = isFirstLight ? TrafficLightState.GREEN : TrafficLightState.RED;
light.changeState(initialState); light.changeState(initialState);
// Schedule first state change // Agenda a primeira transição
double firstChangeTime = currentTime + double firstChangeTime = currentTime +
(initialState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime()); (initialState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime());
@@ -620,14 +667,16 @@ public class IntersectionProcess {
} }
/** /**
* Envia um veículo para o seu próximo destino via ligação socket. * Encaminhamento de rede: Serializa e envia o objeto veículo para o próximo .
* <p>
* Calcula também o tempo de viagem virtual entre nós (Edge Weight).
* *
* @param vehicle O veículo que atravessou esta interseção. * @param vehicle O veículo a ser enviado.
*/ */
public void sendVehicleToNextDestination(Vehicle vehicle) { public void sendVehicleToNextDestination(Vehicle vehicle) {
String nextDestination = vehicle.getCurrentDestination(); String nextDestination = vehicle.getCurrentDestination();
// Calculate travel time // Cálculo de latência de viagem (Edge Weight)
double baseTime = config.getBaseTravelTime(); double baseTime = config.getBaseTravelTime();
double multiplier = 1.0; double multiplier = 1.0;
switch (vehicle.getType()) { switch (vehicle.getType()) {
@@ -640,22 +689,25 @@ public class IntersectionProcess {
System.out.printf("[%s] Vehicle %s departing to %s. Travel time: %.2fs%n", System.out.printf("[%s] Vehicle %s departing to %s. Travel time: %.2fs%n",
intersectionId, vehicle.getId(), nextDestination, travelTime); intersectionId, vehicle.getId(), nextDestination, travelTime);
// Record departure immediately as it leaves the intersection
recordVehicleDeparture(); recordVehicleDeparture();
// In DES mode, send immediately (no real-time delay) // Envio imediato (o delay de viagem é implícito no tempo de chegada no próximo
// nó ou simulado aqui)
sendVehicleImmediately(vehicle, nextDestination); sendVehicleImmediately(vehicle, nextDestination);
} }
/** /**
* Envia imediatamente um veículo para o seu destino via rede. * Envia o veículo imediatamente para o próximo nó via conexão TCP persistente.
*
* @param vehicle O veículo a ser enviado.
* @param nextDestination O identificador do próximo nó destino.
*/ */
private void sendVehicleImmediately(Vehicle vehicle, String nextDestination) { private void sendVehicleImmediately(Vehicle vehicle, String nextDestination) {
try { try {
// Get or create connection to next destination // Lazy loading da conexão
SocketConnection connection = getOrCreateConnection(nextDestination); SocketConnection connection = getOrCreateConnection(nextDestination);
// Create and send message using Message class // Encapsulamento da mensagem
MessageProtocol message = new Message( MessageProtocol message = new Message(
MessageType.VEHICLE_TRANSFER, MessageType.VEHICLE_TRANSFER,
intersectionId, intersectionId,
@@ -668,8 +720,6 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] Vehicle " + vehicle.getId() + System.out.println("[" + intersectionId + "] Vehicle " + vehicle.getId() +
" arrived at " + nextDestination + " (msg sent)"); " arrived at " + nextDestination + " (msg sent)");
// Note: vehicle route is advanced when it arrives at the next intersection
} catch (IOException | InterruptedException e) { } catch (IOException | InterruptedException e) {
System.err.println("[" + intersectionId + "] Failed to send vehicle " + System.err.println("[" + intersectionId + "] Failed to send vehicle " +
vehicle.getId() + " to " + nextDestination + ": " + e.getMessage()); vehicle.getId() + " to " + nextDestination + ": " + e.getMessage());
@@ -677,12 +727,15 @@ public class IntersectionProcess {
} }
/** /**
* Obtém uma ligação existente para um destino ou cria uma nova. * Obtém ou cria uma conexão para o destino especificado (Singleton por
* destino).
* <p>
* Este método é thread-safe.
* *
* @param destinationId O ID do nó de destino. * @param destinationId O identificador do nó destino.
* @return A SocketConnection para esse destino. * @return A conexão TCP estabelecida.
* @throws IOException Se a ligação não puder ser estabelecida. * @throws IOException Se ocorrer um erro de I/O na criação da conexão.
* @throws InterruptedException Se a tentativa de ligação for interrompida. * @throws InterruptedException Se a thread for interrompida durante a espera.
*/ */
private synchronized SocketConnection getOrCreateConnection(String destinationId) private synchronized SocketConnection getOrCreateConnection(String destinationId)
throws IOException, InterruptedException { throws IOException, InterruptedException {
@@ -702,10 +755,10 @@ public class IntersectionProcess {
} }
/** /**
* Obtém o endereço host para um nó de destino a partir da configuração. * Resolve o hostname ou endereço IP para um determinado destino.
* *
* @param destinationId O ID do nó de destino. * @param destinationId O ID do destino.
* @return O endereço host. * @return O endereço do host.
*/ */
private String getHostForDestination(String destinationId) { private String getHostForDestination(String destinationId) {
if (destinationId.equals("S")) { if (destinationId.equals("S")) {
@@ -716,9 +769,9 @@ public class IntersectionProcess {
} }
/** /**
* Obtém o número da porta para um nó de destino a partir da configuração. * Resolve a porta TCP para um determinado destino.
* *
* @param destinationId O ID do nó de destino. * @param destinationId O ID do destino.
* @return O número da porta. * @return O número da porta.
*/ */
private int getPortForDestination(String destinationId) { private int getPortForDestination(String destinationId) {
@@ -730,10 +783,11 @@ public class IntersectionProcess {
} }
/** /**
* Inicia o socket do servidor e começa a aceitar ligações recebidas. * Inicia o servidor e o loop de aceitação de conexões.
* Este é o loop principal de escuta do processo. * <p>
* Este método bloqueia a thread chamadora durante a execução do servidor.
* *
* @throws IOException Se o socket do servidor não puder ser criado. * @throws IOException Se ocorrer um erro ao fazer bind da porta.
*/ */
public void start() throws IOException { public void start() throws IOException {
int port = config.getIntersectionPort(intersectionId); int port = config.getIntersectionPort(intersectionId);
@@ -747,12 +801,12 @@ public class IntersectionProcess {
startEventProcessor(); startEventProcessor();
System.out.println("[" + intersectionId + "] Running in DES mode"); System.out.println("[" + intersectionId + "] Running in DES mode");
// Start stats updater // Background task para telemetria
statsExecutor.scheduleAtFixedRate(this::sendStatsToDashboard, 1, 1, TimeUnit.SECONDS); statsExecutor.scheduleAtFixedRate(this::sendStatsToDashboard, 1, 1, TimeUnit.SECONDS);
System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n"); System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n");
// Main accept loop // Loop principal de aceitação de conexões
while (running) { while (running) {
try { try {
Socket clientSocket = serverSocket.accept(); Socket clientSocket = serverSocket.accept();
@@ -760,13 +814,12 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] New connection accepted from " + System.out.println("[" + intersectionId + "] New connection accepted from " +
clientSocket.getInetAddress().getHostAddress()); clientSocket.getInetAddress().getHostAddress());
// Check running flag again before handling
if (!running) { if (!running) {
clientSocket.close(); clientSocket.close();
break; break;
} }
// **Set timeout before submitting to handler** // Configura timeout para evitar bloqueios infinitos em leitura
try { try {
clientSocket.setSoTimeout(1000); clientSocket.setSoTimeout(1000);
} catch (java.net.SocketException e) { } catch (java.net.SocketException e) {
@@ -775,13 +828,12 @@ public class IntersectionProcess {
continue; continue;
} }
// Handle each connection in a separate thread // Delega processamento para thread pool (NIO style)
connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket)); connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket));
} catch (IOException e) { } catch (IOException e) {
// Expected when serverSocket.close() is called during shutdown
if (!running) { if (!running) {
break; // Normal shutdown break; // Shutdown normal
} }
System.err.println("[" + intersectionId + "] Error accepting connection: " + System.err.println("[" + intersectionId + "] Error accepting connection: " +
e.getMessage()); e.getMessage());
@@ -790,10 +842,13 @@ public class IntersectionProcess {
} }
/** /**
* Trata uma ligação recebida de outro processo. * Lógica de tratamento de conexões de entrada (Consumer).
* Escuta continuamente mensagens de transferência de veículos. * <p>
* Lê continuamente do socket até que a conexão seja fechada, processando
* mensagens
* de chegada de veículos ou comandos de simulação.
* *
* @param clientSocket A ligação socket aceite. * @param clientSocket O socket do cliente conectado.
*/ */
private void handleIncomingConnection(Socket clientSocket) { private void handleIncomingConnection(Socket clientSocket) {
try { try {
@@ -809,27 +864,24 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] New connection accepted from " + System.out.println("[" + intersectionId + "] New connection accepted from " +
clientSocket.getInetAddress().getHostAddress()); clientSocket.getInetAddress().getHostAddress());
// Continuously receive messages while connection is active
while (running && connection.isConnected()) { while (running && connection.isConnected()) {
try { try {
MessageProtocol message = connection.receiveMessage(); MessageProtocol message = connection.receiveMessage();
// Handle simulation start time synchronization
if (message.getType() == MessageType.SIMULATION_START) { if (message.getType() == MessageType.SIMULATION_START) {
System.out.println("[" + intersectionId + "] Simulation start time synchronized"); System.out.println("[" + intersectionId + "] Simulation start time synchronized");
continue; continue;
} }
// Accept both VEHICLE_TRANSFER and VEHICLE_SPAWN (from coordinator)
if (message.getType() == MessageType.VEHICLE_TRANSFER || if (message.getType() == MessageType.VEHICLE_TRANSFER ||
message.getType() == MessageType.VEHICLE_SPAWN) { message.getType() == MessageType.VEHICLE_SPAWN) {
// Cast payload to Vehicle - handle Gson deserialization
// Lógica de desserialização polimórfica (Vehicle ou Map)
Vehicle vehicle; Vehicle vehicle;
Object payload = message.getPayload(); Object payload = message.getPayload();
if (payload instanceof Vehicle) { if (payload instanceof Vehicle) {
vehicle = (Vehicle) payload; vehicle = (Vehicle) payload;
} else if (payload instanceof java.util.Map) { } else if (payload instanceof java.util.Map) {
// Gson deserialized as LinkedHashMap - re-serialize and deserialize as Vehicle
com.google.gson.Gson gson = new com.google.gson.Gson(); com.google.gson.Gson gson = new com.google.gson.Gson();
String json = gson.toJson(payload); String json = gson.toJson(payload);
vehicle = gson.fromJson(json, Vehicle.class); vehicle = gson.fromJson(json, Vehicle.class);
@@ -841,43 +893,37 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] Received vehicle: " + System.out.println("[" + intersectionId + "] Received vehicle: " +
vehicle.getId() + " from " + message.getSourceNode()); vehicle.getId() + " from " + message.getSourceNode());
// Advance vehicle to next destination in its route // Lógica de Roteamento Local
vehicle.advanceRoute(); vehicle.advanceRoute();
// Add vehicle to appropriate queue with current simulation time
intersection.receiveVehicle(vehicle, clock.getCurrentTime()); intersection.receiveVehicle(vehicle, clock.getCurrentTime());
// Log queue status after adding vehicle
System.out.printf("[%s] Vehicle %s queued. Total queue size: %d%n", System.out.printf("[%s] Vehicle %s queued. Total queue size: %d%n",
intersectionId, vehicle.getId(), intersection.getTotalQueueSize()); intersectionId, vehicle.getId(), intersection.getTotalQueueSize());
// Record arrival for statistics
recordVehicleArrival(); recordVehicleArrival();
} else if (message.getType() == MessageType.SHUTDOWN) { } else if (message.getType() == MessageType.SHUTDOWN) {
System.out.println( System.out.println(
"[" + intersectionId + "] Received SHUTDOWN command from " + message.getSourceNode()); "[" + intersectionId + "] Received SHUTDOWN command from " + message.getSourceNode());
running = false; running = false;
// Close this specific connection
break; break;
} }
} catch (java.net.SocketTimeoutException e) { } catch (java.net.SocketTimeoutException e) {
// Timeout - check running flag and continue
if (!running) { if (!running) {
break; break;
} }
// Continue waiting for next message
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
System.err.println("[" + intersectionId + "] Unknown message type received: " + System.err.println("[" + intersectionId + "] Unknown message type received: " +
e.getMessage()); e.getMessage());
break; // Invalid message, close connection break;
} catch (IOException e) { } catch (IOException e) {
if (running) { if (running) {
System.err.println("[" + intersectionId + "] Failed to deserialize message: " + System.err.println("[" + intersectionId + "] Failed to deserialize message: " +
e.getMessage()); e.getMessage());
e.printStackTrace(); // For debugging - maybe change//remove later e.printStackTrace();
} }
break; // Connection error, close connection break;
} }
} }
@@ -885,27 +931,29 @@ public class IntersectionProcess {
if (running) { if (running) {
System.err.println("[" + intersectionId + "] Connection error: " + e.getMessage()); System.err.println("[" + intersectionId + "] Connection error: " + e.getMessage());
} }
// Expected during shutdown
} }
} }
/** /**
* Stops the intersection process gracefully. * Procedimento de Encerramento Gracioso (Graceful Shutdown).
* Shuts down all threads and closes all connections. * <ol>
* <li>Para a aceitação de novas conexões.</li>
* <li>Envia últimas estatísticas.</li>
* <li>Encerra pools de threads.</li>
* <li>Fecha sockets ativos.</li>
* </ol>
*/ */
public void shutdown() { public void shutdown() {
// Check if already shutdown
if (!running) { if (!running) {
return; // Already shutdown, do nothing return;
} }
System.out.println("\n[" + intersectionId + "] Shutting down..."); System.out.println("\n[" + intersectionId + "] Shutting down...");
running = false; running = false;
// Send final stats before closing connections
sendStatsToDashboard(); sendStatsToDashboard();
// 1. Close ServerSocket first // 1. Close ServerSocket
if (serverSocket != null && !serverSocket.isClosed()) { if (serverSocket != null && !serverSocket.isClosed()) {
try { try {
serverSocket.close(); serverSocket.close();
@@ -914,8 +962,7 @@ public class IntersectionProcess {
} }
} }
// 2. Shutdown thread pools with force // 2. Shutdown thread pools
if (connectionHandlerPool != null && !connectionHandlerPool.isShutdown()) { if (connectionHandlerPool != null && !connectionHandlerPool.isShutdown()) {
connectionHandlerPool.shutdownNow(); connectionHandlerPool.shutdownNow();
} }
@@ -926,9 +973,8 @@ public class IntersectionProcess {
departureExecutor.shutdownNow(); departureExecutor.shutdownNow();
} }
// 3. Wait briefly for termination (don't block forever) // 3. Wait briefly for termination
try { try {
if (connectionHandlerPool != null) { if (connectionHandlerPool != null) {
connectionHandlerPool.awaitTermination(1, TimeUnit.SECONDS); connectionHandlerPool.awaitTermination(1, TimeUnit.SECONDS);
} }
@@ -964,31 +1010,32 @@ public class IntersectionProcess {
} }
/** /**
* Gets the Intersection object managed by this process. * Obtém o modelo de dados da interseção.
* Useful for testing and monitoring.
* *
* @return The Intersection object. * @return O objeto Intersection.
*/ */
public Intersection getIntersection() { public Intersection getIntersection() {
return intersection; return intersection;
} }
/** /**
* Records that a vehicle has arrived at this intersection. * Regista a chegada de um novo veículo para fins estatísticos.
*/ */
public void recordVehicleArrival() { public void recordVehicleArrival() {
totalArrivals++; totalArrivals++;
} }
/** /**
* Records that a vehicle has departed from this intersection. * Regista a partida de um veículo para fins estatísticos.
*/ */
public void recordVehicleDeparture() { public void recordVehicleDeparture() {
totalDepartures++; totalDepartures++;
} }
/** /**
* Sends current statistics to the dashboard server. * Envia um "snapshot" do estado atual para o Dashboard (Telemetria Push).
* <p>
* Inclui o número acumulado de chegadas, partidas e o tamanho atual das filas.
*/ */
private void sendStatsToDashboard() { private void sendStatsToDashboard() {
if (dashboardClient == null || !dashboardClient.isConnected()) { if (dashboardClient == null || !dashboardClient.isConnected()) {
@@ -996,7 +1043,6 @@ public class IntersectionProcess {
} }
try { try {
// Calculate current queue size
int currentQueueSize = intersection.getTrafficLights().stream() int currentQueueSize = intersection.getTrafficLights().stream()
.mapToInt(TrafficLight::getQueueSize) .mapToInt(TrafficLight::getQueueSize)
.sum(); .sum();
@@ -1006,7 +1052,6 @@ public class IntersectionProcess {
.setIntersectionDepartures(totalDepartures) .setIntersectionDepartures(totalDepartures)
.setIntersectionQueueSize(currentQueueSize); .setIntersectionQueueSize(currentQueueSize);
// Send StatsUpdatePayload directly as the message payload
sd.model.Message message = new sd.model.Message( sd.model.Message message = new sd.model.Message(
MessageType.STATS_UPDATE, MessageType.STATS_UPDATE,
intersectionId, intersectionId,

View File

@@ -14,36 +14,61 @@ import java.util.TreeSet;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* Executes multiple simulation runs and aggregates results. * Responsável pela agregação e análise estatística de múltiplas execuções da simulação.
* Calculates statistical measures including mean, standard deviation, * <p>
* and confidence intervals across all runs. * Esta classe coleta resultados individuais ({@link SimulationRunResult}) e calcula
* métricas consolidadas, incluindo média, desvio padrão, mediana e intervalos de
* confiança de 95%. O objetivo é fornecer uma visão robusta do comportamento do
* sistema, mitigando a variância estocástica de execuções isoladas.
*/ */
public class MultiRunAnalyzer { public class MultiRunAnalyzer {
/** Lista acumulada de resultados de execuções individuais. */
private final List<SimulationRunResult> results; private final List<SimulationRunResult> results;
/** Identificador do ficheiro de configuração utilizado nas execuções. */
private final String configurationFile; private final String configurationFile;
/**
* Inicializa o analisador para um conjunto específico de configurações.
*
* @param configurationFile O caminho ou nome do ficheiro de configuração base.
*/
public MultiRunAnalyzer(String configurationFile) { public MultiRunAnalyzer(String configurationFile) {
this.configurationFile = configurationFile; this.configurationFile = configurationFile;
this.results = new ArrayList<>(); this.results = new ArrayList<>();
} }
/** /**
* Adds a completed simulation run result. * Adiciona o resultado de uma execução de simulação concluída ao conjunto de dados.
*
* @param result O objeto contendo as métricas da execução individual.
*/ */
public void addResult(SimulationRunResult result) { public void addResult(SimulationRunResult result) {
results.add(result); results.add(result);
} }
/** /**
* Gets the number of completed runs. * Retorna o número total de execuções armazenadas até o momento.
*
* @return O tamanho da lista de resultados.
*/ */
public int getRunCount() { public int getRunCount() {
return results.size(); return results.size();
} }
/** /**
* Generates a comprehensive statistical report. * Gera um relatório estatístico abrangente formatado em texto.
* <p>
* O relatório inclui:
* <ul>
* <li>Métricas globais (throughput, tempos de espera, tempos no sistema).</li>
* <li>Análise segmentada por tipo de veículo ({@link VehicleType}).</li>
* <li>Análise de gargalos por interseção (tamanhos de fila).</li>
* <li>Resumos brutos das execuções individuais.</li>
* </ul>
*
* @return Uma String contendo o relatório completo formatado.
*/ */
public String generateReport() { public String generateReport() {
if (results.isEmpty()) { if (results.isEmpty()) {
@@ -153,7 +178,13 @@ public class MultiRunAnalyzer {
} }
/** /**
* Analyzes a single metric and returns formatted statistics. * Analisa uma métrica específica e retorna as estatísticas formatadas.
* <p>
* Calcula média, desvio padrão, mediana, intervalo de confiança (95%) e extremos (min/max).
*
* @param metricName O nome descritivo da métrica (ex: "Tempo de Espera").
* @param values A lista de valores numéricos brutos extraídos das execuções.
* @return Uma string formatada com os dados estatísticos.
*/ */
private String analyzeMetric(String metricName, List<Double> values) { private String analyzeMetric(String metricName, List<Double> values) {
if (values.isEmpty() || values.stream().allMatch(v -> v == 0.0)) { if (values.isEmpty() || values.stream().allMatch(v -> v == 0.0)) {
@@ -177,7 +208,13 @@ public class MultiRunAnalyzer {
} }
/** /**
* Extracts values using a lambda function. * Extrai valores numéricos dos resultados de simulação usando uma função mapeadora.
* <p>
* Utilizado internamente para transformar a lista de objetos complexos {@link SimulationRunResult}
* em listas simples de Doubles para processamento estatístico.
*
* @param extractor Função lambda que define qual campo extrair de cada resultado.
* @return Lista de valores double correspondentes.
*/ */
private List<Double> extractValues(java.util.function.Function<SimulationRunResult, Double> extractor) { private List<Double> extractValues(java.util.function.Function<SimulationRunResult, Double> extractor) {
List<Double> values = new ArrayList<>(); List<Double> values = new ArrayList<>();
@@ -188,7 +225,10 @@ public class MultiRunAnalyzer {
} }
/** /**
* Saves the report to a file. * Persiste o relatório gerado num ficheiro de texto.
*
* @param filename O caminho do ficheiro de destino.
* @throws IOException Se ocorrer um erro de escrita no disco.
*/ */
public void saveReport(String filename) throws IOException { public void saveReport(String filename) throws IOException {
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)))) { try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)))) {
@@ -197,14 +237,25 @@ public class MultiRunAnalyzer {
} }
/** /**
* Generates a CSV summary for easy import into spreadsheet tools. * Gera um resumo em formato CSV para fácil importação em ferramentas de planilha.
* <p>
* Este método atua como um wrapper para {@link #saveCSVSummary(String)}.
*
* @param filename O caminho do ficheiro CSV de destino.
* @throws IOException Se ocorrer um erro de escrita no disco.
*/ */
public void saveCSV(String filename) throws IOException { public void saveCSV(String filename) throws IOException {
saveCSVSummary(filename); saveCSVSummary(filename);
} }
/** /**
* Generates a CSV summary for easy import into spreadsheet tools. * Gera e grava o sumário CSV detalhado com métricas chave por execução.
* <p>
* Colunas incluídas: Execução, VeículosGerados, VeículosCompletados, TaxaConclusão,
* TempoMédioSistema, TempoMédioEspera, TempoMínimoSistema, TempoMáximoSistema.
*
* @param filename O caminho do ficheiro CSV de destino.
* @throws IOException Se ocorrer um erro de escrita no disco.
*/ */
public void saveCSVSummary(String filename) throws IOException { public void saveCSVSummary(String filename) throws IOException {
try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)))) { try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)))) {

View File

@@ -1,172 +0,0 @@
package sd.analysis;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Orquestra múltiplas execuções de simulação para análise estatística.
*
* Em vez de correr uma única simulação manualmente, esta ferramenta permite
* correr um "lote"
* de N simulações consecutivas. Isto é essencial para recolher dados
* estatisticamente significativos
* (calcular intervalos de confiança, etc.) conforme exigido pelas
* especificações do projeto.
*
* Utilização:
* java sd.analysis.SimulationBatchRunner <ficheiro-config> <num-execucoes>
* <dir-saida>
*/
public class SimulationBatchRunner {
public static void main(String[] args) {
if (args.length < 3) {
System.err.println("Usage: SimulationBatchRunner <config-file> <num-runs> <output-dir>");
System.err.println("Example: SimulationBatchRunner simulation-medium.properties 10 results/medium");
System.exit(1);
}
String configFile = args[0];
int numRuns;
String outputDir = args[2];
try {
numRuns = Integer.parseInt(args[1]);
if (numRuns < 1 || numRuns > 100) {
throw new IllegalArgumentException("Number of runs must be between 1 and 100");
}
} catch (NumberFormatException e) {
System.err.println("Error: Invalid number of runs: " + args[1]);
System.exit(1);
return;
}
System.out.println("=".repeat(80));
System.out.println("SIMULATION BATCH RUNNER");
System.out.println("=".repeat(80));
System.out.println("Configuration: " + configFile);
System.out.println("Number of Runs: " + numRuns);
System.out.println("Output Directory: " + outputDir);
System.out.println("=".repeat(80));
System.out.println();
// Create output directory
try {
Files.createDirectories(Paths.get(outputDir));
} catch (IOException e) {
System.err.println("Failed to create output directory: " + e.getMessage());
System.exit(1);
}
MultiRunAnalyzer analyzer = new MultiRunAnalyzer(configFile);
// Execute runs
for (int i = 1; i <= numRuns; i++) {
System.out.println("\n" + "=".repeat(80));
System.out.println("STARTING RUN " + i + " OF " + numRuns);
System.out.println("=".repeat(80));
SimulationRunResult result = executeSimulationRun(i, configFile, outputDir);
if (result != null) {
analyzer.addResult(result);
System.out.println("\n" + result);
} else {
System.err.println("Run " + i + " failed!");
}
// Pause between runs
if (i < numRuns) {
System.out.println("\nWaiting 10 seconds before next run...");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
// Generate reports
System.out.println("\n\n" + "=".repeat(80));
System.out.println("ALL RUNS COMPLETE - GENERATING REPORTS");
System.out.println("=".repeat(80));
try {
String timestamp = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date());
String reportFile = outputDir + "/analysis-report-" + timestamp + ".txt";
String csvFile = outputDir + "/summary-" + timestamp + ".csv";
analyzer.saveReport(reportFile);
analyzer.saveCSVSummary(csvFile);
System.out.println("\nReports generated:");
System.out.println(" - Analysis Report: " + reportFile);
System.out.println(" - CSV Summary: " + csvFile);
System.out.println();
// Print report to console
System.out.println(analyzer.generateReport());
} catch (IOException e) {
System.err.println("Failed to generate reports: " + e.getMessage());
e.printStackTrace();
}
}
/**
* Executa uma única instância da simulação.
*
* Idealmente, este método iniciaria todos os processos necessários
* (Interseções, Nó de Saída, Coordenador),
* esperaria que terminassem e depois recolheria os resultados.
*
* Atualmente, serve como um espaço reservado estrutural para demonstrar como
* funciona o pipeline de análise.
* Para correr uma simulação real, deve iniciar os componentes manualmente ou
* usar um script shell.
*/
private static SimulationRunResult executeSimulationRun(int runNumber, String configFile, String outputDir) {
SimulationRunResult result = new SimulationRunResult(runNumber, configFile);
try {
// TODO: Implement actual simulation execution
// This would involve:
// 1. Starting intersection processes
// 2. Starting exit node process
// 3. Starting dashboard process
// 4. Running coordinator
// 5. Collecting results from dashboard/exit node
// 6. Shutting down all processes
System.out.println("NOTE: Actual simulation execution not yet implemented.");
System.out.println("This batch runner demonstrates the framework structure.");
System.out.println("To run actual simulations, you need to:");
System.out.println(" 1. Start all intersection processes manually");
System.out.println(" 2. Start exit node process");
System.out.println(" 3. Start dashboard process");
System.out.println(" 4. Run coordinator with the configuration file");
System.out.println(" 5. Results will be collected automatically");
// Placeholder: simulate some results
// In real implementation, these would be collected from the actual simulation
result.setTotalVehiclesGenerated(100);
result.setTotalVehiclesCompleted(85);
result.setAverageSystemTime(120.5);
result.setMinSystemTime(45.2);
result.setMaxSystemTime(250.8);
result.setAverageWaitingTime(45.3);
return result;
} catch (Exception e) {
System.err.println("Error executing run " + runNumber + ": " + e.getMessage());
e.printStackTrace();
return null;
}
}
}

View File

@@ -6,8 +6,12 @@ import java.util.Map;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* Stores the results of a single simulation run. * Encapsula os dados telemétricos e estatísticos resultantes de uma única execução da simulação.
* Contains all key metrics for post-simulation analysis. * <p>
* Esta classe atua como um registo estruturado de métricas de desempenho, armazenando
* dados de latência (tempos de sistema/espera), vazão (throughput) e ocupação de recursos
* (tamanhos de fila). Os dados aqui contidos servem como base para a análise
* estatística agregada realizada pelo {@link MultiRunAnalyzer}.
*/ */
public class SimulationRunResult { public class SimulationRunResult {
@@ -17,11 +21,22 @@ public class SimulationRunResult {
private final long endTimeMillis; private final long endTimeMillis;
// Global metrics // Global metrics
/** Total de veículos instanciados pelos geradores durante a execução. */
private int totalVehiclesGenerated; private int totalVehiclesGenerated;
/** Total de veículos que completaram o percurso e saíram do sistema com sucesso. */
private int totalVehiclesCompleted; private int totalVehiclesCompleted;
/** Média global do tempo total (em segundos) desde a geração até a saída. */
private double averageSystemTime; // seconds private double averageSystemTime; // seconds
/** Menor tempo de sistema registado (em segundos). */
private double minSystemTime; // seconds private double minSystemTime; // seconds
/** Maior tempo de sistema registado (em segundos). */
private double maxSystemTime; // seconds private double maxSystemTime; // seconds
/** Média global do tempo (em segundos) que os veículos passaram parados em filas. */
private double averageWaitingTime; // seconds private double averageWaitingTime; // seconds
// Per-type metrics // Per-type metrics
@@ -34,6 +49,12 @@ public class SimulationRunResult {
private final Map<String, Double> avgQueueSizeByIntersection; private final Map<String, Double> avgQueueSizeByIntersection;
private final Map<String, Integer> vehiclesProcessedByIntersection; private final Map<String, Integer> vehiclesProcessedByIntersection;
/**
* Inicializa um novo contentor de resultados para uma execução específica.
*
* @param runNumber O identificador sequencial desta execução.
* @param configurationFile O ficheiro de configuração utilizado.
*/
public SimulationRunResult(int runNumber, String configurationFile) { public SimulationRunResult(int runNumber, String configurationFile) {
this.runNumber = runNumber; this.runNumber = runNumber;
this.configurationFile = configurationFile; this.configurationFile = configurationFile;
@@ -48,6 +69,10 @@ public class SimulationRunResult {
this.vehiclesProcessedByIntersection = new HashMap<>(); this.vehiclesProcessedByIntersection = new HashMap<>();
} }
/**
* Sinaliza o fim da recolha de dados para esta execução.
* (Placeholder para lógica de finalização de timestamps).
*/
public void markCompleted() { public void markCompleted() {
// This will be called when the run finishes // This will be called when the run finishes
} }
@@ -57,6 +82,11 @@ public class SimulationRunResult {
public String getConfigurationFile() { return configurationFile; } public String getConfigurationFile() { return configurationFile; }
public long getStartTimeMillis() { return startTimeMillis; } public long getStartTimeMillis() { return startTimeMillis; }
public long getEndTimeMillis() { return endTimeMillis; } public long getEndTimeMillis() { return endTimeMillis; }
/**
* Calcula a duração total da execução em milissegundos.
* @return Delta entre fim e início.
*/
public long getDurationMillis() { return endTimeMillis - startTimeMillis; } public long getDurationMillis() { return endTimeMillis - startTimeMillis; }
public int getTotalVehiclesGenerated() { return totalVehiclesGenerated; } public int getTotalVehiclesGenerated() { return totalVehiclesGenerated; }
@@ -66,21 +96,50 @@ public class SimulationRunResult {
public double getMaxSystemTime() { return maxSystemTime; } public double getMaxSystemTime() { return maxSystemTime; }
public double getAverageWaitingTime() { return averageWaitingTime; } public double getAverageWaitingTime() { return averageWaitingTime; }
/**
* Retorna o mapeamento de contagem de veículos por tipo.
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<VehicleType, Integer> getVehicleCountByType() { public Map<VehicleType, Integer> getVehicleCountByType() {
return new HashMap<>(vehicleCountByType); return new HashMap<>(vehicleCountByType);
} }
/**
* Retorna o tempo médio no sistema segmentado por tipo de veículo.
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<VehicleType, Double> getAvgSystemTimeByType() { public Map<VehicleType, Double> getAvgSystemTimeByType() {
return new HashMap<>(avgSystemTimeByType); return new HashMap<>(avgSystemTimeByType);
} }
/**
* Retorna o tempo médio de espera segmentado por tipo de veículo.
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<VehicleType, Double> getAvgWaitTimeByType() { public Map<VehicleType, Double> getAvgWaitTimeByType() {
return new HashMap<>(avgWaitTimeByType); return new HashMap<>(avgWaitTimeByType);
} }
/**
* Retorna o tamanho máximo de fila registado por interseção (gargalos).
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<String, Integer> getMaxQueueSizeByIntersection() { public Map<String, Integer> getMaxQueueSizeByIntersection() {
return new HashMap<>(maxQueueSizeByIntersection); return new HashMap<>(maxQueueSizeByIntersection);
} }
/**
* Retorna o tamanho médio das filas por interseção.
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<String, Double> getAvgQueueSizeByIntersection() { public Map<String, Double> getAvgQueueSizeByIntersection() {
return new HashMap<>(avgQueueSizeByIntersection); return new HashMap<>(avgQueueSizeByIntersection);
} }
/**
* Retorna o total de veículos processados (throughput) por interseção.
* @return Uma cópia defensiva do mapa (snapshot).
*/
public Map<String, Integer> getVehiclesProcessedByIntersection() { public Map<String, Integer> getVehiclesProcessedByIntersection() {
return new HashMap<>(vehiclesProcessedByIntersection); return new HashMap<>(vehiclesProcessedByIntersection);
} }
@@ -124,6 +183,10 @@ public class SimulationRunResult {
vehiclesProcessedByIntersection.put(intersection, count); vehiclesProcessedByIntersection.put(intersection, count);
} }
/**
* Gera uma representação textual resumida das métricas principais da execução.
* Útil para logs rápidos e debugging.
*/
@Override @Override
public String toString() { public String toString() {
return String.format( return String.format(

View File

@@ -5,13 +5,19 @@ import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
* Statistical analysis utilities for simulation results. * Utilitário estático para processamento matemático e análise estatística dos dados da simulação.
* Calculates mean, standard deviation, and confidence intervals. * <p>
* Esta classe fornece algoritmos para cálculo de medidas de tendência central (média, mediana),
* dispersão (desvio padrão amostral) e inferência estatística (Intervalos de Confiança).
* É utilizada para normalizar e validar os resultados estocásticos obtidos através de
* múltiplas execuções do sistema.
*/ */
public class StatisticalAnalysis { public class StatisticalAnalysis {
/** /**
* Calculates the mean (average) of a list of values. * Calcula a média aritmética de um conjunto de valores.
* * @param values Lista de valores numéricos (double).
* @return A soma dos valores dividida pelo tamanho da amostra, ou 0.0 se a lista for nula/vazia.
*/ */
public static double mean(List<Double> values) { public static double mean(List<Double> values) {
if (values == null || values.isEmpty()) { if (values == null || values.isEmpty()) {
@@ -25,7 +31,13 @@ public class StatisticalAnalysis {
} }
/** /**
* Calculates the sample standard deviation. * Calcula o desvio padrão amostral (sample standard deviation).
* <p>
* Utiliza o denominador {@code n - 1} (Correção de Bessel) para fornecer um
* estimador não viesado da variância populacional, adequado para as amostras
* de simulação.
* * @param values Lista de observações.
* @return O desvio padrão calculado, ou 0.0 se o tamanho da amostra for < 2.
*/ */
public static double standardDeviation(List<Double> values) { public static double standardDeviation(List<Double> values) {
if (values == null || values.size() < 2) { if (values == null || values.size() < 2) {
@@ -45,10 +57,13 @@ public class StatisticalAnalysis {
} }
/** /**
* Calculates the 95% confidence interval for the mean. * Calcula o Intervalo de Confiança (IC) de 95% para a média.
* Uses t-distribution for small samples (n < 30). * <p>
* * Utiliza a distribuição t de Student para maior precisão em amostras pequenas (n < 30),
* @return Array of [lowerBound, upperBound] * onde a aproximação pela distribuição Normal (Z) seria inadequada. O intervalo define
* a faixa onde a verdadeira média populacional reside com 95% de probabilidade.
* * @param values Lista de observações.
* @return Um array de double onde índice 0 é o limite inferior e índice 1 é o limite superior.
*/ */
public static double[] confidenceInterval95(List<Double> values) { public static double[] confidenceInterval95(List<Double> values) {
if (values == null || values.size() < 2) { if (values == null || values.size() < 2) {
@@ -76,8 +91,12 @@ public class StatisticalAnalysis {
} }
/** /**
* Returns the t-critical value for 95% confidence interval. * Retorna o valor crítico t (t-score) para um IC de 95% (bicaudal).
* Approximations for common degrees of freedom (n-1). * <p>
* Baseia-se nos graus de liberdade (gl = n - 1). Para amostras grandes (gl >= 30),
* aproxima-se do valor Z de 1.96.
* * @param sampleSize O tamanho da amostra (n).
* @return O fator multiplicativo t apropriado.
*/ */
private static double getTCriticalValue(int sampleSize) { private static double getTCriticalValue(int sampleSize) {
int df = sampleSize - 1; // degrees of freedom int df = sampleSize - 1; // degrees of freedom
@@ -94,7 +113,9 @@ public class StatisticalAnalysis {
} }
/** /**
* Calculates the minimum value. * Identifica o valor mínimo absoluto na amostra.
* * @param values Lista de valores.
* @return O menor valor encontrado.
*/ */
public static double min(List<Double> values) { public static double min(List<Double> values) {
if (values == null || values.isEmpty()) { if (values == null || values.isEmpty()) {
@@ -104,7 +125,9 @@ public class StatisticalAnalysis {
} }
/** /**
* Calculates the maximum value. * Identifica o valor máximo absoluto na amostra.
* * @param values Lista de valores.
* @return O maior valor encontrado.
*/ */
public static double max(List<Double> values) { public static double max(List<Double> values) {
if (values == null || values.isEmpty()) { if (values == null || values.isEmpty()) {
@@ -114,7 +137,12 @@ public class StatisticalAnalysis {
} }
/** /**
* Calculates the median value. * Calcula a mediana da amostra.
* <p>
* <b>Nota de Desempenho:</b> Este método ordena uma cópia da lista, resultando em
* complexidade O(n log n).
* * @param values Lista de valores.
* @return O valor central (ou média dos dois centrais) da distribuição ordenada.
*/ */
public static double median(List<Double> values) { public static double median(List<Double> values) {
if (values == null || values.isEmpty()) { if (values == null || values.isEmpty()) {
@@ -133,7 +161,12 @@ public class StatisticalAnalysis {
} }
/** /**
* Formats a statistical summary as a string. * Formata um sumário estatístico completo para uma métrica específica.
* <p>
* Útil para logging e geração de relatórios textuais.
* * @param metricName Nome da métrica a ser exibida.
* @param values Os dados brutos associados à métrica.
* @return String formatada contendo Média, Desvio Padrão, IC95%, Min, Max e N.
*/ */
public static String formatSummary(String metricName, List<Double> values) { public static String formatSummary(String metricName, List<Double> values) {
if (values == null || values.isEmpty()) { if (values == null || values.isEmpty()) {

View File

@@ -14,17 +14,28 @@ import java.util.Properties;
import com.google.gson.Gson; import com.google.gson.Gson;
/** /**
* Carrega e gere configurações da simulação. * Responsável pelo carregamento, validação e acesso centralizado às configurações da simulação.
* * <p>
* <p>Lê propriedades de um ficheiro .properties e fornece getters * Esta classe atua como uma fachada (Facade) para os parâmetros do sistema, abstraindo a origem
* type-safe com valores padrão para robustez. * dos dados (ficheiros {@code .properties} ou JSON). Implementa uma estratégia robusta de
* carregamento de recursos, suportando tanto caminhos absolutos do sistema de ficheiros quanto
* recursos embutidos no <i>classpath</i>.
* <p>
* Além de propriedades chave-valor simples, gerencia a desserialização da topologia da rede
* através da classe interna {@link NetworkConfig}.
*/ */
public class SimulationConfig { public class SimulationConfig {
/** Propriedades carregadas do ficheiro */ /** Armazenamento em memória das propriedades chave-valor carregadas. */
private final Properties properties; private final Properties properties;
/** Estrutura hierárquica da configuração da rede carregada via JSON. */
private NetworkConfig networkConfig; private NetworkConfig networkConfig;
/**
* Objeto de transferência de dados (DTO) que representa a configuração global da rede.
* Mapeado a partir do ficheiro {@code network_config.json}.
*/
public static class NetworkConfig { public static class NetworkConfig {
private List<IntersectionConfig> intersections; private List<IntersectionConfig> intersections;
@@ -33,36 +44,45 @@ public class SimulationConfig {
} }
} }
/**
* DTO que representa a configuração de uma única interseção na topologia.
*/
public static class IntersectionConfig { public static class IntersectionConfig {
private String id; private String id;
private List<String> lights; private List<String> lights;
private Map<String, String> routes; private Map<String, String> routes;
/** @return O identificador único da interseção (ex: "Cr1"). */
public String getId() { public String getId() {
return id; return id;
} }
/** @return Lista de identificadores dos semáforos associados a esta interseção. */
public List<String> getLights() { public List<String> getLights() {
return lights; return lights;
} }
/** @return Mapa de roteamento definindo destinos alcançáveis e seus próximos saltos. */
public Map<String, String> getRoutes() { public Map<String, String> getRoutes() {
return routes; return routes;
} }
} }
/** /**
* Carrega propriedades do ficheiro especificado. * Inicializa o gestor de configuração carregando propriedades do caminho especificado.
* * * <p>Implementa uma estratégia de carregamento em cascata (fallback) para garantir robustez
* <p>Tenta múltiplas estratégias: * em diferentes ambientes de execução (IDE, JAR, Docker):
* <ol> * <ol>
* <li>Caminho direto no sistema de ficheiros * <li><b>Sistema de Ficheiros Direto:</b> Tenta carregar do caminho absoluto ou relativo.</li>
* <li>Recurso no classpath (com normalização automática) * <li><b>Classpath (Contexto):</b> Tenta carregar via {@code Thread.currentThread().getContextClassLoader()},
* <li>Recurso no classpath com barra inicial * normalizando prefixos como "src/main/resources" ou "classpath:".</li>
* <li><b>Classpath (Classe):</b> Tenta carregar via {@code SimulationConfig.class.getResourceAsStream},
* útil para recursos na raiz do JAR.</li>
* </ol> * </ol>
* *
* @param filePath caminho do ficheiro .properties * @param filePath O caminho ou nome do recurso do ficheiro {@code .properties}.
* @throws IOException se o ficheiro não for encontrado * @throws IOException Se o ficheiro não puder ser localizado em nenhuma das estratégias,
* com uma mensagem detalhada das tentativas falhadas.
*/ */
public SimulationConfig(String filePath) throws IOException { public SimulationConfig(String filePath) throws IOException {
properties = new Properties(); properties = new Properties();
@@ -135,6 +155,12 @@ public class SimulationConfig {
throw new IOException(errorMsg.toString(), fileSystemException); throw new IOException(errorMsg.toString(), fileSystemException);
} }
/**
* Carrega a configuração da topologia de rede a partir do ficheiro "network_config.json".
* <p>
* Utiliza a biblioteca Gson para desserialização. Em caso de falha, emite um aviso para o
* {@code System.err} mas não aborta a execução, permitindo o uso de defaults ou redes vazias.
*/
private void loadNetworkConfig() { private void loadNetworkConfig() {
try (InputStream is = getClass().getClassLoader().getResourceAsStream("network_config.json")) { try (InputStream is = getClass().getClassLoader().getResourceAsStream("network_config.json")) {
if (is == null) { if (is == null) {
@@ -151,6 +177,10 @@ public class SimulationConfig {
} }
} }
/**
* Retorna a configuração estruturada da rede.
* @return Objeto {@link NetworkConfig} ou null se o carregamento falhou.
*/
public NetworkConfig getNetworkConfig() { public NetworkConfig getNetworkConfig() {
return networkConfig; return networkConfig;
} }
@@ -158,56 +188,50 @@ public class SimulationConfig {
// --- Network configurations --- // --- Network configurations ---
/** /**
* Gets the host address for a specific intersection. * Obtém o endereço de host (nome DNS ou IP) para uma interseção específica.
* * * @param intersectionId O ID da interseção (ex: "Cr1").
* @param intersectionId The ID of the intersection (e.g., "Cr1"). * @return O host configurado ou "localhost" por omissão.
* @return The host (e.g., "localhost").
*/ */
public String getIntersectionHost(String intersectionId) { public String getIntersectionHost(String intersectionId) {
return properties.getProperty("intersection." + intersectionId + ".host", "localhost"); return properties.getProperty("intersection." + intersectionId + ".host", "localhost");
} }
/** /**
* Gets the port number for a specific intersection. * Obtém a porta de escuta TCP para uma interseção específica.
* * * @param intersectionId O ID da interseção (ex: "Cr1").
* @param intersectionId The ID of the intersection (e.g., "Cr1"). * @return O número da porta. Retorna 0 se não configurado.
* @return The port number.
*/ */
public int getIntersectionPort(String intersectionId) { public int getIntersectionPort(String intersectionId) {
return Integer.parseInt(properties.getProperty("intersection." + intersectionId + ".port", "0")); return Integer.parseInt(properties.getProperty("intersection." + intersectionId + ".port", "0"));
} }
/** /**
* Gets the host address for the dashboard server. * Obtém o endereço de host do servidor de Dashboard (monitorização).
* * @return O host do dashboard (padrão: "localhost").
* @return The dashboard host.
*/ */
public String getDashboardHost() { public String getDashboardHost() {
return properties.getProperty("dashboard.host", "localhost"); return properties.getProperty("dashboard.host", "localhost");
} }
/** /**
* Gets the port number for the dashboard server. * Obtém a porta de conexão do servidor de Dashboard.
* * @return A porta do dashboard (padrão: 9000).
* @return The dashboard port.
*/ */
public int getDashboardPort() { public int getDashboardPort() {
return Integer.parseInt(properties.getProperty("dashboard.port", "9000")); return Integer.parseInt(properties.getProperty("dashboard.port", "9000"));
} }
/** /**
* Gets the host address for the exit node. * Obtém o endereço de host do nó de saída (Exit Node), para onde os veículos são encaminhados ao sair da malha.
* * @return O host do nó de saída (padrão: "localhost").
* @return The exit node host.
*/ */
public String getExitHost() { public String getExitHost() {
return properties.getProperty("exit.host", "localhost"); return properties.getProperty("exit.host", "localhost");
} }
/** /**
* Gets the port number for the exit node. * Obtém a porta de conexão do nó de saída.
* * @return A porta do nó de saída (padrão: 9001).
* @return The exit node port.
*/ */
public int getExitPort() { public int getExitPort() {
return Integer.parseInt(properties.getProperty("exit.port", "9001")); return Integer.parseInt(properties.getProperty("exit.port", "9001"));
@@ -216,64 +240,64 @@ public class SimulationConfig {
// --- Simulation configurations --- // --- Simulation configurations ---
/** /**
* Gets the total duration of the simulation in virtual seconds. * Define a duração total da execução da simulação em segundos virtuais.
* * @return A duração em segundos (padrão: 3600).
* @return The simulation duration.
*/ */
public double getSimulationDuration() { public double getSimulationDuration() {
return Double.parseDouble(properties.getProperty("simulation.duration", "3600")); return Double.parseDouble(properties.getProperty("simulation.duration", "3600"));
} }
/** /**
* Get time scaling factor for visualization. * Obtém o fator de escala temporal para visualização/execução.
* 0 = instant (pure DES), 0.01 = 100x speed, 0.1 = 10x speed, 1.0 = real-time * <ul>
* <li>0.0: Execução instantânea (DES puro, velocidade máxima).</li>
* <li>1.0: Tempo real (1 segundo simulado = 1 segundo real).</li>
* <li>0.01: Acelerado 100x.</li>
* </ul>
* @return O fator de escala.
*/ */
public double getTimeScale() { public double getTimeScale() {
return Double.parseDouble(properties.getProperty("simulation.time.scale", "0")); return Double.parseDouble(properties.getProperty("simulation.time.scale", "0"));
} }
/** /**
* Gets the drain time (in virtual seconds) to allow vehicles to exit after * Obtém o tempo de "drenagem" (drain time) em segundos virtuais.
* generation stops. * <p>
* * Este é o período adicional executado após o fim da geração de veículos para permitir
* @return The drain time. * que os veículos restantes no sistema completem os seus percursos.
* @return O tempo de drenagem (padrão: 60.0s).
*/ */
public double getDrainTime() { public double getDrainTime() {
return Double.parseDouble(properties.getProperty("simulation.drain.time", "60.0")); return Double.parseDouble(properties.getProperty("simulation.drain.time", "60.0"));
} }
/** /**
* Gets the vehicle arrival model ("POISSON" or "FIXED"). * Determina o modelo estocástico utilizado para a chegada de veículos.
* * @return "POISSON" (distribuição exponencial) ou "FIXED" (intervalo determinístico).
* @return The arrival model as a string.
*/ */
public String getArrivalModel() { public String getArrivalModel() {
return properties.getProperty("simulation.arrival.model", "POISSON"); return properties.getProperty("simulation.arrival.model", "POISSON");
} }
/** /**
* Gets the average arrival rate (lambda) for the POISSON model. * Obtém a taxa média de chegada (lambda) para o modelo Poisson.
* This represents the average number of vehicles arriving per second. * @return Veículos por segundo (padrão: 0.5).
*
* @return The arrival rate.
*/ */
public double getArrivalRate() { public double getArrivalRate() {
return Double.parseDouble(properties.getProperty("simulation.arrival.rate", "0.5")); return Double.parseDouble(properties.getProperty("simulation.arrival.rate", "0.5"));
} }
/** /**
* Gets the fixed time interval between vehicle arrivals for the FIXED model. * Obtém o intervalo fixo entre chegadas para o modelo determinístico.
* * @return O intervalo em segundos (padrão: 2.0).
* @return The fixed interval in seconds.
*/ */
public double getFixedArrivalInterval() { public double getFixedArrivalInterval() {
return Double.parseDouble(properties.getProperty("simulation.arrival.fixed.interval", "2.0")); return Double.parseDouble(properties.getProperty("simulation.arrival.fixed.interval", "2.0"));
} }
/** /**
* Gets the routing policy to use for vehicle route selection. * Obtém a política de roteamento utilizada pelos veículos para navegar na malha.
* * @return A política: "RANDOM", "SHORTEST_PATH" ou "LEAST_CONGESTED".
* @return The routing policy (RANDOM, SHORTEST_PATH, or LEAST_CONGESTED).
*/ */
public String getRoutingPolicy() { public String getRoutingPolicy() {
return properties.getProperty("simulation.routing.policy", "RANDOM"); return properties.getProperty("simulation.routing.policy", "RANDOM");
@@ -282,11 +306,10 @@ public class SimulationConfig {
// --- Traffic light configurations --- // --- Traffic light configurations ---
/** /**
* Gets the duration of the GREEN light state for a specific traffic light. * Obtém a duração do estado VERDE para um semáforo específico.
* * * @param intersectionId ID da interseção.
* @param intersectionId The ID of the intersection (e.g., "Cr1"). * @param direction Direção do fluxo (ex: "North").
* @param direction The direction of the light (e.g., "North"). * @return Duração em segundos (padrão: 30.0).
* @return The green light time in seconds.
*/ */
public double getTrafficLightGreenTime(String intersectionId, String direction) { public double getTrafficLightGreenTime(String intersectionId, String direction) {
String key = "trafficlight." + intersectionId + "." + direction + ".green"; String key = "trafficlight." + intersectionId + "." + direction + ".green";
@@ -294,11 +317,10 @@ public class SimulationConfig {
} }
/** /**
* Gets the duration of the RED light state for a specific traffic light. * Obtém a duração do estado VERMELHO para um semáforo específico.
* * * @param intersectionId ID da interseção.
* @param intersectionId The ID of the intersection (e.g., "Cr1"). * @param direction Direção do fluxo.
* @param direction The direction of the light (e.g., "North"). * @return Duração em segundos (padrão: 30.0).
* @return The red light time in seconds.
*/ */
public double getTrafficLightRedTime(String intersectionId, String direction) { public double getTrafficLightRedTime(String intersectionId, String direction) {
String key = "trafficlight." + intersectionId + "." + direction + ".red"; String key = "trafficlight." + intersectionId + "." + direction + ".red";
@@ -308,83 +330,74 @@ public class SimulationConfig {
// --- Vehicle configurations --- // --- Vehicle configurations ---
/** /**
* Gets the probability (0.0 to 1.0) that a generated vehicle is of type LIGHT. * Probabilidade (0.0 a 1.0) de geração de um veículo do tipo LIGEIRO (LIGHT).
* * @return Probabilidade (padrão: 0.7).
* @return The probability for LIGHT vehicles.
*/ */
public double getLightVehicleProbability() { public double getLightVehicleProbability() {
return Double.parseDouble(properties.getProperty("vehicle.probability.light", "0.7")); return Double.parseDouble(properties.getProperty("vehicle.probability.light", "0.7"));
} }
/** /**
* Gets the average time it takes a LIGHT vehicle to cross an intersection. * Tempo médio necessário para um veículo LIGEIRO atravessar uma interseção.
* * @return Tempo em segundos (padrão: 2.0).
* @return The crossing time in seconds.
*/ */
public double getLightVehicleCrossingTime() { public double getLightVehicleCrossingTime() {
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.light", "2.0")); return Double.parseDouble(properties.getProperty("vehicle.crossing.time.light", "2.0"));
} }
/** /**
* Gets the probability (0.0 to 1.0) that a generated vehicle is of type BIKE. * Probabilidade (0.0 a 1.0) de geração de um veículo do tipo BICICLETA (BIKE).
* * @return Probabilidade (padrão: 0.0).
* @return The probability for BIKE vehicles.
*/ */
public double getBikeVehicleProbability() { public double getBikeVehicleProbability() {
return Double.parseDouble(properties.getProperty("vehicle.probability.bike", "0.0")); return Double.parseDouble(properties.getProperty("vehicle.probability.bike", "0.0"));
} }
/** /**
* Gets the average time it takes a BIKE vehicle to cross an intersection. * Tempo médio necessário para uma BICICLETA atravessar uma interseção.
* * @return Tempo em segundos (padrão: 1.5).
* @return The crossing time in seconds.
*/ */
public double getBikeVehicleCrossingTime() { public double getBikeVehicleCrossingTime() {
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.bike", "1.5")); return Double.parseDouble(properties.getProperty("vehicle.crossing.time.bike", "1.5"));
} }
/** /**
* Gets the probability (0.0 to 1.0) that a generated vehicle is of type HEAVY. * Probabilidade (0.0 a 1.0) de geração de um veículo PESADO (HEAVY).
* * @return Probabilidade (padrão: 0.0).
* @return The probability for HEAVY vehicles.
*/ */
public double getHeavyVehicleProbability() { public double getHeavyVehicleProbability() {
return Double.parseDouble(properties.getProperty("vehicle.probability.heavy", "0.0")); return Double.parseDouble(properties.getProperty("vehicle.probability.heavy", "0.0"));
} }
/** /**
* Gets the average time it takes a HEAVY vehicle to cross an intersection. * Tempo médio necessário para um veículo PESADO atravessar uma interseção.
* * @return Tempo em segundos (padrão: 4.0).
* @return The crossing time in seconds.
*/ */
public double getHeavyVehicleCrossingTime() { public double getHeavyVehicleCrossingTime() {
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.heavy", "4.0")); return Double.parseDouble(properties.getProperty("vehicle.crossing.time.heavy", "4.0"));
} }
/** /**
* Gets the base travel time between intersections for light vehicles. * Define o tempo base de viagem entre interseções para veículos padrão.
* * @return Tempo em segundos (padrão: 8.0).
* @return The base travel time in seconds.
*/ */
public double getBaseTravelTime() { public double getBaseTravelTime() {
return Double.parseDouble(properties.getProperty("vehicle.travel.time.base", "8.0")); return Double.parseDouble(properties.getProperty("vehicle.travel.time.base", "8.0"));
} }
/** /**
* Gets the travel time multiplier for bike vehicles. * Multiplicador de tempo de viagem para bicicletas.
* Bike travel time = base time × this multiplier. * <p>Tempo efetivo = Base * Multiplicador.
* * @return Fator multiplicativo (padrão: 0.5).
* @return The multiplier for bike travel time.
*/ */
public double getBikeTravelTimeMultiplier() { public double getBikeTravelTimeMultiplier() {
return Double.parseDouble(properties.getProperty("vehicle.travel.time.bike.multiplier", "0.5")); return Double.parseDouble(properties.getProperty("vehicle.travel.time.bike.multiplier", "0.5"));
} }
/** /**
* Gets the travel time multiplier for heavy vehicles. * Multiplicador de tempo de viagem para veículos pesados.
* Heavy vehicle travel time = base time × this multiplier. * <p>Tempo efetivo = Base * Multiplicador.
* * @return Fator multiplicativo (padrão: 4.0).
* @return The multiplier for heavy vehicle travel time.
*/ */
public double getHeavyTravelTimeMultiplier() { public double getHeavyTravelTimeMultiplier() {
return Double.parseDouble(properties.getProperty("vehicle.travel.time.heavy.multiplier", "4.0")); return Double.parseDouble(properties.getProperty("vehicle.travel.time.heavy.multiplier", "4.0"));
@@ -393,9 +406,8 @@ public class SimulationConfig {
// --- Statistics --- // --- Statistics ---
/** /**
* Gets the interval (in virtual seconds) between periodic statistics updates. * Intervalo de tempo (em segundos virtuais) para agregação e envio de estatísticas periódicas.
* * @return Intervalo de atualização (padrão: 1.0).
* @return The statistics update interval.
*/ */
public double getStatisticsUpdateInterval() { public double getStatisticsUpdateInterval() {
return Double.parseDouble(properties.getProperty("statistics.update.interval", "1.0")); return Double.parseDouble(properties.getProperty("statistics.update.interval", "1.0"));
@@ -404,21 +416,19 @@ public class SimulationConfig {
// --- Generic getters --- // --- Generic getters ---
/** /**
* Generic method to get any property as a string, with a default value. * Recupera uma propriedade genérica como String, com valor padrão de segurança.
* * * @param key A chave da propriedade.
* @param key The property key. * @param defaultValue O valor a retornar caso a chave não exista.
* @param defaultValue The value to return if the key is not found. * @return O valor da propriedade ou o default.
* @return The property value or the default.
*/ */
public String getProperty(String key, String defaultValue) { public String getProperty(String key, String defaultValue) {
return properties.getProperty(key, defaultValue); return properties.getProperty(key, defaultValue);
} }
/** /**
* Generic method to get any property as a string. * Recupera uma propriedade genérica como String.
* * * @param key A chave da propriedade.
* @param key The property key. * @return O valor da propriedade ou null se não encontrada.
* @return The property value, or null if not found.
*/ */
public String getProperty(String key) { public String getProperty(String key) {
return properties.getProperty(key); return properties.getProperty(key);

View File

@@ -24,47 +24,53 @@ import sd.serialization.SerializationException;
import sd.util.VehicleGenerator; import sd.util.VehicleGenerator;
/** /**
* Coordenador central da simulação distribuída. * Coordenador central da arquitetura de simulação distribuída.
* * <p>
* <p>Responsabilidades: * Este processo atua como o "cérebro" da simulação, sendo responsável por:
* <ol> * <ol>
* <li>Gerar veículos segundo modelo configurado (Poisson/Fixed) * <li><b>Orquestração DES:</b> Gerir o relógio global ({@link SimulationClock}) e a fila de eventos prioritária.</li>
* <li>Injetar veículos nas interseções de entrada * <li><b>Geração de Carga:</b> Injetar veículos na malha viária seguindo distribuições estocásticas (Poisson) ou determinísticas.</li>
* <li>Gerir relógio global e sincronizar componentes * <li><b>Encaminhamento Dinâmico:</b> Decidir as rotas dos veículos com base na política ativa (Random, Shortest Path, Least Congested).</li>
* <li><b>Sincronização:</b> Garantir que todos os nós (Interseções e Dashboard) operem em uníssono.</li>
* </ol> * </ol>
*
* <p>Usa motor DES para agendar eventos de geração com precisão.
* Mantém fila de prioridade e processa eventos em ordem cronológica.
*/ */
public class CoordinatorProcess { public class CoordinatorProcess {
private final SimulationConfig config; private final SimulationConfig config;
private final VehicleGenerator vehicleGenerator; private final VehicleGenerator vehicleGenerator;
/** Mapa de clientes TCP persistentes para cada interseção (Worker Nodes). */
private final Map<String, SocketClient> intersectionClients; private final Map<String, SocketClient> intersectionClients;
private SocketClient dashboardClient; private SocketClient dashboardClient;
// Componentes DES (Discrete Event Simulation)
private final SimulationClock clock; private final SimulationClock clock;
private final EventQueue eventQueue; private final EventQueue eventQueue;
private final EventLogger eventLogger; private final EventLogger eventLogger;
// Estado da simulação
private int vehicleCounter; private int vehicleCounter;
private boolean running; private boolean running;
private double timeScale; private double timeScale;
private RouteSelector currentRouteSelector; private RouteSelector currentRouteSelector;
/** Referência para estatísticas do dashboard para polling de mudanças de política. */
private DashboardStatistics dashboardStatistics; private DashboardStatistics dashboardStatistics;
/** /**
* Local tracking of intersection queue sizes for dynamic routing. * Monitorização local (aproximada) dos tamanhos de fila nas interseções.
* * <p>
* <p>This approximation tracks queue sizes by incrementing when vehicles are sent * Utilizado exclusivamente pela política {@link LeastCongestedRouteSelector}.
* to intersections. While not perfectly accurate (doesn't track departures in real-time), * O coordenador incrementa este contador ao enviar um veículo para uma interseção.
* it provides useful congestion information for the LEAST_CONGESTED routing policy.</p> * Nota: Esta é uma visão "borda" (edge) e pode não refletir a saída em tempo real
* * dos veículos, mas serve como heurística suficiente para balanceamento de carga.
* <p>This is a practical solution that enables dynamic routing without requiring
* bidirectional communication or complex state synchronization.</p>
*/ */
private final Map<String, Integer> intersectionQueueSizes; private final Map<String, Integer> intersectionQueueSizes;
/**
* Ponto de entrada do processo Coordenador.
* Carrega configurações, estabelece conexões TCP e inicia o loop de eventos.
*/
public static void main(String[] args) { public static void main(String[] args) {
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
System.out.println("COORDINATOR PROCESS - DISTRIBUTED TRAFFIC SIMULATION"); System.out.println("COORDINATOR PROCESS - DISTRIBUTED TRAFFIC SIMULATION");
@@ -95,6 +101,12 @@ public class CoordinatorProcess {
} }
} }
/**
* Inicializa o coordenador com a configuração fornecida.
* Configura o motor DES, logging e o seletor de rotas inicial.
*
* @param config Objeto de configuração carregado.
*/
public CoordinatorProcess(SimulationConfig config) { public CoordinatorProcess(SimulationConfig config) {
this.config = config; this.config = config;
@@ -124,10 +136,9 @@ public class CoordinatorProcess {
} }
/** /**
* Cria o RouteSelector apropriado baseado na política configurada. * Fábrica de {@link RouteSelector} baseada no nome da política.
* * * @param policyName Nome da política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED).
* @param policyName nome da política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED) * @return Uma instância da estratégia de roteamento.
* @return instância do RouteSelector correspondente
*/ */
private RouteSelector createRouteSelector(String policyName) { private RouteSelector createRouteSelector(String policyName) {
try { try {
@@ -156,6 +167,10 @@ public class CoordinatorProcess {
} }
} }
/**
* Estabelece conexões TCP com o Dashboard e todas as Interseções (Worker Nodes).
* Essencial para o envio de comandos de controle e injeção de veículos.
*/
public void initialize() { public void initialize() {
// Connect to dashboard first // Connect to dashboard first
connectToDashboard(); connectToDashboard();
@@ -185,6 +200,15 @@ public class CoordinatorProcess {
} }
} }
/**
* Loop principal da simulação (DES Engine).
* <p>
* Executa a sequência:
* 1. Retira o próximo evento da fila prioritária.
* 2. Avança o relógio virtual para o timestamp do evento.
* 3. Aplica escala temporal (Time Scale) para visualização, se necessário.
* 4. Processa o evento.
*/
public void run() { public void run() {
double duration = config.getSimulationDuration(); double duration = config.getSimulationDuration();
double drainTime = config.getDrainTime(); double drainTime = config.getDrainTime();
@@ -264,14 +288,9 @@ public class CoordinatorProcess {
} }
/** /**
* Trata um único evento de simulação. * Trata o processamento de um evento DES retirado da fila.
* * * @param event O evento a ser processado.
* É aqui que a magia acontece. Dependendo do tipo de evento (como * @param generationDuration Duração da fase de geração ativa (antes do 'drain time').
* VEHICLE_GENERATION),
* atualizamos o estado do mundo. Para a geração de veículos, criamos um novo
* veículo,
* enviamo-lo para uma interseção e depois agendamos o *próximo* evento de
* geração.
*/ */
private void processEvent(SimulationEvent event, double generationDuration) { private void processEvent(SimulationEvent event, double generationDuration) {
double currentTime = clock.getCurrentTime(); double currentTime = clock.getCurrentTime();
@@ -285,7 +304,7 @@ public class CoordinatorProcess {
generateAndSendVehicle(); generateAndSendVehicle();
// Schedule next vehicle generation // Schedule next vehicle generation (Recursive scheduling)
double nextArrivalTime = vehicleGenerator.getNextArrivalTime(currentTime); double nextArrivalTime = vehicleGenerator.getNextArrivalTime(currentTime);
eventQueue.schedule(new SimulationEvent( eventQueue.schedule(new SimulationEvent(
nextArrivalTime, nextArrivalTime,
@@ -309,9 +328,8 @@ public class CoordinatorProcess {
} }
/** /**
* Guarda o histórico completo de eventos de simulação num ficheiro de texto. * Exporta o log completo de eventos DES para auditoria e debug.
* Isto permite-nos auditar exatamente o que aconteceu e quando, o que é crucial * Caminho: {@code logs/coordinator-event-history.txt}.
* para depuração e verificação.
*/ */
private void exportEventHistory() { private void exportEventHistory() {
try (java.io.PrintWriter writer = new java.io.PrintWriter( try (java.io.PrintWriter writer = new java.io.PrintWriter(
@@ -324,6 +342,10 @@ public class CoordinatorProcess {
} }
} }
/**
* Gera um novo veículo e envia-o via TCP para a interseção de entrada apropriada.
* Também atualiza o rastreio local de filas para balanceamento de carga.
*/
private void generateAndSendVehicle() { private void generateAndSendVehicle() {
double currentTime = clock.getCurrentTime(); double currentTime = clock.getCurrentTime();
@@ -355,6 +377,9 @@ public class CoordinatorProcess {
sendVehicleToIntersection(vehicle, entryIntersection); sendVehicleToIntersection(vehicle, entryIntersection);
} }
/**
* Serializa e transmite o objeto Veículo para o nó (interseção) de destino.
*/
private void sendVehicleToIntersection(Vehicle vehicle, String intersectionId) { private void sendVehicleToIntersection(Vehicle vehicle, String intersectionId) {
SocketClient client = intersectionClients.get(intersectionId); SocketClient client = intersectionClients.get(intersectionId);
@@ -379,6 +404,9 @@ public class CoordinatorProcess {
} }
} }
/**
* Encerra graciosamente a simulação, enviando sinais de SHUTDOWN para todos os nós.
*/
public void shutdown() { public void shutdown() {
System.out.println(); System.out.println();
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
@@ -415,10 +443,9 @@ public class CoordinatorProcess {
} }
/** /**
* Altera dinamicamente a política de roteamento durante a simulação. * Altera dinamicamente a política de roteamento durante a simulação (Hot-swap).
* Novos veículos gerados usarão a nova política. * Thread-safe.
* * * @param policyName nome da nova política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED)
* @param policyName nome da nova política (RANDOM, SHORTEST_PATH, LEAST_CONGESTED)
*/ */
public synchronized void changeRoutingPolicy(String policyName) { public synchronized void changeRoutingPolicy(String policyName) {
System.out.println("\n" + "=".repeat(60)); System.out.println("\n" + "=".repeat(60));
@@ -454,8 +481,8 @@ public class CoordinatorProcess {
} }
/** /**
* Verifica se há solicitação de mudança de política do dashboard * Verifica se há solicitação de mudança de política proveniente do dashboard
* e aplica se houver. * e aplica a alteração se houver.
*/ */
private void checkForPolicyChanges() { private void checkForPolicyChanges() {
if (dashboardStatistics != null) { if (dashboardStatistics != null) {
@@ -467,8 +494,8 @@ public class CoordinatorProcess {
} }
/** /**
* Define a referência para as estatísticas do dashboard. * Injeta a referência para as estatísticas do dashboard.
* Permite que o coordenador verifique mudanças de política solicitadas. * Permite que o coordenador consuma intenções de mudança de política do utilizador.
*/ */
public void setDashboardStatistics(DashboardStatistics stats) { public void setDashboardStatistics(DashboardStatistics stats) {
this.dashboardStatistics = stats; this.dashboardStatistics = stats;
@@ -512,6 +539,11 @@ public class CoordinatorProcess {
} }
} }
/**
* Sincronização Global: Envia o timestamp de início (System.currentTimeMillis)
* para todos os componentes distribuídos, garantindo uma base de tempo comum
* para métricas de latência.
*/
private void sendSimulationStartTime() { private void sendSimulationStartTime() {
long startTimeMillis = System.currentTimeMillis(); long startTimeMillis = System.currentTimeMillis();

View File

@@ -10,10 +10,14 @@ import sd.serialization.SerializationException;
import sd.serialization.SerializerFactory; import sd.serialization.SerializerFactory;
/** /**
* Cliente socket para comunicação com um processo de interseção. * Abstração de cliente TCP para comunicação outbound (de saída) com nós da rede.
* * <p>
* <p>Gere uma ligação TCP persistente para uma interseção, * Esta classe encapsula a gestão do socket raw, oferecendo uma interface de alto nível
* fornecendo uma forma simples de enviar mensagens serializadas.</p> * para envio de objetos {@link Message}. Implementa o protocolo de camada de aplicação
* proprietário, garantindo a serialização correta e o enquadramento (framing) dos dados
* na stream TCP.
* <p>
* É utilizada pelo Coordenador para controlar Interseções e enviar telemetria para o Dashboard.
*/ */
public class SocketClient { public class SocketClient {
@@ -25,11 +29,11 @@ public class SocketClient {
private MessageSerializer serializer; private MessageSerializer serializer;
/** /**
* Cria um novo cliente socket para uma interseção. * Instancia um novo cliente socket configurado para um destino específico.
* *
* @param intersectionId ID da interseção (ex: "Cr1") * @param intersectionId Identificador lógico do nó de destino (ex: "Cr1", "Dashboard").
* @param host endereço do host (ex: "localhost") * @param host Endereço IP ou hostname do destino.
* @param port número da porta * @param port Porta TCP de escuta do destino.
*/ */
public SocketClient(String intersectionId, String host, int port) { public SocketClient(String intersectionId, String host, int port) {
this.intersectionId = intersectionId; this.intersectionId = intersectionId;
@@ -39,9 +43,8 @@ public class SocketClient {
} }
/** /**
* Liga-se ao processo da interseção via TCP. * Estabelece a conexão TCP (Handshake SYN/ACK) com o host remoto.
* * * @throws IOException Se o host for inalcançável ou a conexão for recusada.
* @throws IOException se a ligação não puder ser estabelecida
*/ */
public void connect() throws IOException { public void connect() throws IOException {
try { try {
@@ -55,12 +58,22 @@ public class SocketClient {
} }
/** /**
* Envia uma mensagem para a interseção ligada. * Serializa e transmite uma mensagem através do socket conectado.
* A mensagem é serializada e enviada pelo socket. * <p>
* <b>Protocolo de Envio (Length-Prefix Framing):</b>
* <ol>
* <li>Serializa o objeto {@link Message} para um array de bytes.</li>
* <li>Calcula o tamanho (N) do array.</li>
* <li>Escreve um cabeçalho de 4 bytes contendo N (Big-Endian).</li>
* <li>Escreve os N bytes do payload (corpo da mensagem).</li>
* <li>Realiza flush no stream para forçar o envio imediato do pacote TCP.</li>
* </ol>
* Este mecanismo garante que o recetor saiba exatamente quantos bytes ler,
* prevenindo problemas de fragmentação ou aglutinação de pacotes TCP.
* *
* @param message mensagem a enviar * @param message O objeto de domínio a ser enviado.
* @throws SerializationException se a serialização falhar * @throws SerializationException Se o objeto não puder ser convertido para bytes.
* @throws IOException se a escrita no socket falhar * @throws IOException Se houver falha na escrita do socket (ex: conexão resetada).
*/ */
public void send(Message message) throws SerializationException, IOException { public void send(Message message) throws SerializationException, IOException {
if (socket == null || socket.isClosed()) { if (socket == null || socket.isClosed()) {
@@ -71,11 +84,13 @@ public class SocketClient {
byte[] data = serializer.serialize(message); byte[] data = serializer.serialize(message);
int length = data.length; int length = data.length;
// Write 4-byte length header (Big Endian)
outputStream.write((length >> 24) & 0xFF); outputStream.write((length >> 24) & 0xFF);
outputStream.write((length >> 16) & 0xFF); outputStream.write((length >> 16) & 0xFF);
outputStream.write((length >> 8) & 0xFF); outputStream.write((length >> 8) & 0xFF);
outputStream.write(length & 0xFF); outputStream.write(length & 0xFF);
// Write payload
outputStream.write(data); outputStream.write(data);
outputStream.flush(); outputStream.flush();
@@ -86,8 +101,10 @@ public class SocketClient {
} }
/** /**
* Closes the socket connection safely. * Realiza o encerramento gracioso (graceful shutdown) da conexão.
* Calling it multiple times wont cause issues. * Liberta os recursos do sistema operativo (descritores de arquivo).
* <p>
* Operação idempotente: pode ser chamada múltiplas vezes sem erro.
*/ */
public void close() { public void close() {
try { try {
@@ -104,7 +121,8 @@ public class SocketClient {
} }
/** /**
* @return true if connected and socket is open, false otherwise * Verifica o estado atual da conexão.
* * @return true se o socket estiver instanciado, conectado e aberto; false caso contrário.
*/ */
public boolean isConnected() { public boolean isConnected() {
return socket != null && socket.isConnected() && !socket.isClosed(); return socket != null && socket.isConnected() && !socket.isClosed();

View File

@@ -25,8 +25,17 @@ import sd.analysis.SimulationRunResult;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* Dialog for running batch performance analysis. * Diálogo para configuração e execução de análise de desempenho em lote (Batch Processing).
* Allows running multiple simulations automatically and generating statistical reports. * <p>
* Esta classe fornece uma interface gráfica para automatizar múltiplas execuções da simulação
* sob diferentes cenários de carga. É responsável por:
* <ol>
* <li>Orquestrar o ciclo de vida dos processos de simulação (start/stop/wait).</li>
* <li>Coletar métricas estatísticas de cada execução.</li>
* <li>Agregar resultados usando o {@link MultiRunAnalyzer}.</li>
* <li>Gerar relatórios consolidados para análise de variância e intervalos de confiança.</li>
* </ol>
* A execução ocorre numa thread separada (background) para manter a responsividade da UI.
*/ */
public class BatchAnalysisDialog { public class BatchAnalysisDialog {
@@ -38,16 +47,17 @@ public class BatchAnalysisDialog {
private Button startButton; private Button startButton;
private Button closeButton; private Button closeButton;
// Flags de controlo de concorrência
private volatile boolean isRunning = false; private volatile boolean isRunning = false;
private volatile boolean shouldStop = false; private volatile boolean shouldStop = false;
/** Referência partilhada para capturar estatísticas em tempo real do Dashboard. */
private DashboardStatistics sharedStatistics; private DashboardStatistics sharedStatistics;
/** /**
* Shows the batch analysis dialog. * Exibe o diálogo de análise em lote.
* * * @param owner A janela pai (Stage) para modalidade.
* @param owner parent window * @param statistics Objeto partilhado de estatísticas para coleta de dados.
* @param statistics shared statistics object (optional, can be null)
*/ */
public static void show(Stage owner, DashboardStatistics statistics) { public static void show(Stage owner, DashboardStatistics statistics) {
BatchAnalysisDialog dialog = new BatchAnalysisDialog(); BatchAnalysisDialog dialog = new BatchAnalysisDialog();
@@ -55,6 +65,9 @@ public class BatchAnalysisDialog {
dialog.createAndShow(owner); dialog.createAndShow(owner);
} }
/**
* Constrói e inicializa a interface gráfica do diálogo.
*/
private void createAndShow(Stage owner) { private void createAndShow(Stage owner) {
dialog = new Stage(); dialog = new Stage();
dialog.initOwner(owner); dialog.initOwner(owner);
@@ -64,37 +77,34 @@ public class BatchAnalysisDialog {
VBox root = new VBox(20); VBox root = new VBox(20);
root.setPadding(new Insets(20)); root.setPadding(new Insets(20));
root.setAlignment(Pos.TOP_CENTER); root.setAlignment(Pos.TOP_CENTER);
// Estilo Dark Mode conforme guidelines visuais
root.setStyle("-fx-background-color: #2b2b2b;"); root.setStyle("-fx-background-color: #2b2b2b;");
// Header // Header
Label title = new Label("Batch Performance Evaluation"); Label title = new Label("Batch Performance Evaluation");
title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: white;"); title.setStyle("-fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: white;");
Label subtitle = new Label("Run multiple simulations automatically to generate statistical analysis"); Label subtitle = new Label("Executar múltiplas simulações para gerar análise estatística consolidada");
subtitle.setStyle("-fx-font-size: 12px; -fx-text-fill: #cccccc;"); subtitle.setStyle("-fx-font-size: 12px; -fx-text-fill: #cccccc;");
subtitle.setWrapText(true); subtitle.setWrapText(true);
// Configuration panel // Painéis de Componentes
VBox configPanel = createConfigPanel(); VBox configPanel = createConfigPanel();
// Progress panel
VBox progressPanel = createProgressPanel(); VBox progressPanel = createProgressPanel();
// Log area
VBox logPanel = createLogPanel(); VBox logPanel = createLogPanel();
// Control buttons
HBox buttonBox = createButtonBox(); HBox buttonBox = createButtonBox();
root.getChildren().addAll(title, subtitle, configPanel, progressPanel, logPanel, buttonBox); root.getChildren().addAll(title, subtitle, configPanel, progressPanel, logPanel, buttonBox);
Scene scene = new Scene(root, 700, 600); Scene scene = new Scene(root, 700, 600);
dialog.setScene(scene); dialog.setScene(scene);
// Tratamento de fecho da janela: interromper thread de worker se ativa
dialog.setOnCloseRequest(e -> { dialog.setOnCloseRequest(e -> {
if (isRunning) { if (isRunning) {
e.consume(); e.consume(); // Previne fecho imediato
shouldStop = true; shouldStop = true;
log("Stopping after current run completes..."); log("A parar após conclusão da execução atual...");
} }
}); });
@@ -106,13 +116,13 @@ public class BatchAnalysisDialog {
panel.setPadding(new Insets(15)); panel.setPadding(new Insets(15));
panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;"); panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;");
Label header = new Label("Configuration"); Label header = new Label("Configuração");
header.setStyle("-fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: white;"); header.setStyle("-fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: white;");
// Runs per scenario // Runs per scenario
HBox runsBox = new HBox(10); HBox runsBox = new HBox(10);
runsBox.setAlignment(Pos.CENTER_LEFT); runsBox.setAlignment(Pos.CENTER_LEFT);
Label runsLabel = new Label("Runs per scenario:"); Label runsLabel = new Label("Execuções por cenário:");
runsLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;"); runsLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;");
Spinner<Integer> runsSpinner = new Spinner<>(1, 20, 5, 1); Spinner<Integer> runsSpinner = new Spinner<>(1, 20, 5, 1);
runsSpinner.setEditable(true); runsSpinner.setEditable(true);
@@ -121,20 +131,20 @@ public class BatchAnalysisDialog {
runsBox.getChildren().addAll(runsLabel, runsSpinner); runsBox.getChildren().addAll(runsLabel, runsSpinner);
// Scenario selection // Scenario selection
Label scenarioHeader = new Label("Select scenarios to test:"); Label scenarioHeader = new Label("Selecionar Cenários:");
scenarioHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;"); scenarioHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;");
CheckBox lowCheck = new CheckBox("Low Load (λ=0.2 v/s)"); CheckBox lowCheck = new CheckBox("Carga Baixa (λ=0.2 v/s)");
lowCheck.setSelected(true); lowCheck.setSelected(true);
lowCheck.setId("lowCheck"); lowCheck.setId("lowCheck");
lowCheck.setStyle("-fx-text-fill: white;"); lowCheck.setStyle("-fx-text-fill: white;");
CheckBox mediumCheck = new CheckBox("Medium Load (λ=0.5 v/s)"); CheckBox mediumCheck = new CheckBox("Carga Média (λ=0.5 v/s)");
mediumCheck.setSelected(true); mediumCheck.setSelected(true);
mediumCheck.setId("mediumCheck"); mediumCheck.setId("mediumCheck");
mediumCheck.setStyle("-fx-text-fill: white;"); mediumCheck.setStyle("-fx-text-fill: white;");
CheckBox highCheck = new CheckBox("High Load (λ=1.0 v/s)"); CheckBox highCheck = new CheckBox("Carga Alta (λ=1.0 v/s)");
highCheck.setSelected(true); highCheck.setSelected(true);
highCheck.setId("highCheck"); highCheck.setId("highCheck");
highCheck.setStyle("-fx-text-fill: white;"); highCheck.setStyle("-fx-text-fill: white;");
@@ -142,13 +152,13 @@ public class BatchAnalysisDialog {
// Run duration // Run duration
HBox durationBox = new HBox(10); HBox durationBox = new HBox(10);
durationBox.setAlignment(Pos.CENTER_LEFT); durationBox.setAlignment(Pos.CENTER_LEFT);
Label durationLabel = new Label("Run duration (seconds):"); Label durationLabel = new Label("Duração (segundos):");
durationLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;"); durationLabel.setStyle("-fx-text-fill: white; -fx-min-width: 150px;");
Spinner<Integer> durationSpinner = new Spinner<>(30, 3600, 120, 30); Spinner<Integer> durationSpinner = new Spinner<>(30, 3600, 120, 30);
durationSpinner.setEditable(true); durationSpinner.setEditable(true);
durationSpinner.setPrefWidth(80); durationSpinner.setPrefWidth(80);
durationSpinner.setId("durationSpinner"); durationSpinner.setId("durationSpinner");
Label durationInfo = new Label("(simulated time - actual duration depends on time.scale)"); Label durationInfo = new Label("(tempo simulado - duração real depende do time.scale)");
durationInfo.setStyle("-fx-text-fill: #999999; -fx-font-size: 10px;"); durationInfo.setStyle("-fx-text-fill: #999999; -fx-font-size: 10px;");
durationBox.getChildren().addAll(durationLabel, durationSpinner, durationInfo); durationBox.getChildren().addAll(durationLabel, durationSpinner, durationInfo);
@@ -161,14 +171,14 @@ public class BatchAnalysisDialog {
panel.setPadding(new Insets(15)); panel.setPadding(new Insets(15));
panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;"); panel.setStyle("-fx-background-color: rgba(255, 255, 255, 0.05); -fx-background-radius: 5;");
statusLabel = new Label("Ready to start"); statusLabel = new Label("Pronto para iniciar");
statusLabel.setStyle("-fx-text-fill: white; -fx-font-weight: bold;"); statusLabel.setStyle("-fx-text-fill: white; -fx-font-weight: bold;");
progressBar = new ProgressBar(0); progressBar = new ProgressBar(0);
progressBar.setPrefWidth(Double.MAX_VALUE); progressBar.setPrefWidth(Double.MAX_VALUE);
progressBar.setPrefHeight(25); progressBar.setPrefHeight(25);
progressLabel = new Label("0 / 0 runs completed"); progressLabel = new Label("0 / 0 execuções concluídas");
progressLabel.setStyle("-fx-text-fill: #cccccc; -fx-font-size: 11px;"); progressLabel.setStyle("-fx-text-fill: #cccccc; -fx-font-size: 11px;");
panel.getChildren().addAll(statusLabel, progressBar, progressLabel); panel.getChildren().addAll(statusLabel, progressBar, progressLabel);
@@ -178,13 +188,14 @@ public class BatchAnalysisDialog {
private VBox createLogPanel() { private VBox createLogPanel() {
VBox panel = new VBox(5); VBox panel = new VBox(5);
Label logHeader = new Label("Activity Log:"); Label logHeader = new Label("Log de Atividade:");
logHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;"); logHeader.setStyle("-fx-text-fill: white; -fx-font-size: 12px; -fx-font-weight: bold;");
logArea = new TextArea(); logArea = new TextArea();
logArea.setEditable(false); logArea.setEditable(false);
logArea.setPrefRowCount(10); logArea.setPrefRowCount(10);
logArea.setWrapText(true); logArea.setWrapText(true);
// Estilo de terminal para o log
logArea.setStyle("-fx-control-inner-background: #1e1e1e; -fx-text-fill: #00ff00; -fx-font-family: 'Courier New';"); logArea.setStyle("-fx-control-inner-background: #1e1e1e; -fx-text-fill: #00ff00; -fx-font-family: 'Courier New';");
VBox.setVgrow(logArea, Priority.ALWAYS); VBox.setVgrow(logArea, Priority.ALWAYS);
@@ -197,18 +208,18 @@ public class BatchAnalysisDialog {
box.setAlignment(Pos.CENTER); box.setAlignment(Pos.CENTER);
box.setPadding(new Insets(10, 0, 0, 0)); box.setPadding(new Insets(10, 0, 0, 0));
startButton = new Button("START BATCH ANALYSIS"); startButton = new Button("INICIAR BATCH");
startButton.setStyle("-fx-background-color: #28a745; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); startButton.setStyle("-fx-background-color: #28a745; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;");
startButton.setOnAction(e -> startBatchAnalysis()); startButton.setOnAction(e -> startBatchAnalysis());
Button stopButton = new Button("STOP"); Button stopButton = new Button("PARAR");
stopButton.setStyle("-fx-background-color: #dc3545; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); stopButton.setStyle("-fx-background-color: #dc3545; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;");
stopButton.setOnAction(e -> { stopButton.setOnAction(e -> {
shouldStop = true; shouldStop = true;
log("Stop requested..."); log("Paragem solicitada...");
}); });
closeButton = new Button("CLOSE"); closeButton = new Button("FECHAR");
closeButton.setStyle("-fx-background-color: #6c757d; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;"); closeButton.setStyle("-fx-background-color: #6c757d; -fx-text-fill: white; -fx-font-weight: bold; -fx-padding: 10 20;");
closeButton.setOnAction(e -> dialog.close()); closeButton.setOnAction(e -> dialog.close());
@@ -216,6 +227,9 @@ public class BatchAnalysisDialog {
return box; return box;
} }
/**
* Valida configurações e inicia a thread de execução em batch.
*/
private void startBatchAnalysis() { private void startBatchAnalysis() {
if (isRunning) return; if (isRunning) return;
@@ -231,11 +245,11 @@ public class BatchAnalysisDialog {
// Validate selection // Validate selection
if (!lowCheck.isSelected() && !mediumCheck.isSelected() && !highCheck.isSelected()) { if (!lowCheck.isSelected() && !mediumCheck.isSelected() && !highCheck.isSelected()) {
log("ERROR: Please select at least one scenario!"); log("ERRO: Selecione pelo menos um cenário!");
return; return;
} }
// Disable controls // Disable controls para evitar alterações durante execução
startButton.setDisable(true); startButton.setDisable(true);
runsSpinner.setDisable(true); runsSpinner.setDisable(true);
durationSpinner.setDisable(true); durationSpinner.setDisable(true);
@@ -246,12 +260,13 @@ public class BatchAnalysisDialog {
isRunning = true; isRunning = true;
shouldStop = false; shouldStop = false;
// Run in background thread // Executar em thread daemon para não bloquear a UI JavaFX
Thread analysisThread = new Thread(() -> { Thread analysisThread = new Thread(() -> {
try { try {
runBatchAnalysis(lowCheck.isSelected(), mediumCheck.isSelected(), runBatchAnalysis(lowCheck.isSelected(), mediumCheck.isSelected(),
highCheck.isSelected(), runsPerScenario, duration); highCheck.isSelected(), runsPerScenario, duration);
} finally { } finally {
// Restaurar estado da UI no final
Platform.runLater(() -> { Platform.runLater(() -> {
startButton.setDisable(false); startButton.setDisable(false);
runsSpinner.setDisable(false); runsSpinner.setDisable(false);
@@ -267,14 +282,18 @@ public class BatchAnalysisDialog {
analysisThread.start(); analysisThread.start();
} }
/**
* Lógica principal de orquestração do batch.
* Itera sobre cenários e execuções, chamando a simulação e o analisador.
*/
private void runBatchAnalysis(boolean low, boolean medium, boolean high, int runsPerScenario, int durationSeconds) { private void runBatchAnalysis(boolean low, boolean medium, boolean high, int runsPerScenario, int durationSeconds) {
log("==========================================================="); log("===========================================================");
log("STARTING BATCH PERFORMANCE ANALYSIS"); log("INICIANDO ANÁLISE DE DESEMPENHO EM LOTE");
log("==========================================================="); log("===========================================================");
log("Configuration:"); log("Configuração:");
log("Runs per scenario: " + runsPerScenario); log("Execuções por cenário: " + runsPerScenario);
log(" • Duration per run: " + durationSeconds + " seconds"); log(" • Duração por execução: " + durationSeconds + " segundos");
log("Scenarios: " + (low ? "LOW " : "") + (medium ? "MEDIUM " : "") + (high ? "HIGH" : "")); log("Cenários: " + (low ? "LOW " : "") + (medium ? "MEDIUM " : "") + (high ? "HIGH" : ""));
log(""); log("");
String[] scenarios = new String[]{ String[] scenarios = new String[]{
@@ -295,8 +314,8 @@ public class BatchAnalysisDialog {
for (int i = 0; i < scenarios.length; i++) { for (int i = 0; i < scenarios.length; i++) {
if (scenarios[i] == null) continue; if (scenarios[i] == null) continue;
if (shouldStop) { if (shouldStop) {
log("Batch analysis stopped by user"); log("Batch analysis interrompida pelo utilizador");
updateStatus("Stopped", currentRun, totalRuns); updateStatus("Parado", currentRun, totalRuns);
return; return;
} }
@@ -305,53 +324,57 @@ public class BatchAnalysisDialog {
log(""); log("");
log("---------------------------------------------------------"); log("---------------------------------------------------------");
log("SCENARIO: " + scenarioName + " (" + configFile + ")"); log("CENÁRIO: " + scenarioName + " (" + configFile + ")");
log("---------------------------------------------------------"); log("---------------------------------------------------------");
MultiRunAnalyzer analyzer = new MultiRunAnalyzer(configFile); MultiRunAnalyzer analyzer = new MultiRunAnalyzer(configFile);
for (int run = 1; run <= runsPerScenario; run++) { for (int run = 1; run <= runsPerScenario; run++) {
if (shouldStop) { if (shouldStop) {
log("Batch analysis stopped by user"); log("Batch analysis interrompida pelo utilizador");
updateStatus("Stopped", currentRun, totalRuns); updateStatus("Parado", currentRun, totalRuns);
savePartialReport(analyzer, scenarioName); savePartialReport(analyzer, scenarioName);
return; return;
} }
currentRun++; currentRun++;
log(""); log("");
log("Run " + run + "/" + runsPerScenario + " starting..."); log("Execução " + run + "/" + runsPerScenario + " a iniciar...");
updateStatus("Running " + scenarioName + " - Run " + run + "/" + runsPerScenario, updateStatus("A correr " + scenarioName + " - Execução " + run + "/" + runsPerScenario,
currentRun - 1, totalRuns); currentRun - 1, totalRuns);
// Executa uma simulação completa e bloqueia até terminar
SimulationRunResult result = runSingleSimulation(configFile, run, durationSeconds); SimulationRunResult result = runSingleSimulation(configFile, run, durationSeconds);
if (result != null) { if (result != null) {
analyzer.addResult(result); analyzer.addResult(result);
log("Run " + run + " completed - Generated: " + result.getTotalVehiclesGenerated() + log("Execução " + run + " completa - Gerados: " + result.getTotalVehiclesGenerated() +
" | Completed: " + result.getTotalVehiclesCompleted() + " | Completados: " + result.getTotalVehiclesCompleted() +
" | Avg Time: " + String.format("%.2f", result.getAverageSystemTime()) + "s"); " | Tempo Médio: " + String.format("%.2f", result.getAverageSystemTime()) + "s");
} else { } else {
log("Run " + run + " failed!"); log("Execução " + run + " falhou!");
} }
updateProgress(currentRun, totalRuns); updateProgress(currentRun, totalRuns);
} }
// Generate report for this scenario // Gera e guarda o relatório final deste cenário
saveScenarioReport(analyzer, scenarioName); saveScenarioReport(analyzer, scenarioName);
} }
log(""); log("");
log("============================================================"); log("============================================================");
log("BATCH ANALYSIS COMPLETE!"); log("BATCH ANALYSIS COMPLETA!");
log("==========================================================="); log("===========================================================");
log("Reports saved to: analysis/"); log("Relatórios guardados em: analysis/");
log(""); log("");
updateStatus("Complete!", totalRuns, totalRuns); updateStatus("Concluído!", totalRuns, totalRuns);
updateProgress(1.0); updateProgress(1.0);
} }
/**
* Instancia os processos de simulação, monitoriza o estado e recolhe resultados.
*/
private SimulationRunResult runSingleSimulation(String configFile, int runNumber, int durationSeconds) { private SimulationRunResult runSingleSimulation(String configFile, int runNumber, int durationSeconds) {
SimulationProcessManager processManager = new SimulationProcessManager(); SimulationProcessManager processManager = new SimulationProcessManager();
SimulationRunResult result = new SimulationRunResult(runNumber, configFile); SimulationRunResult result = new SimulationRunResult(runNumber, configFile);
@@ -361,16 +384,16 @@ public class BatchAnalysisDialog {
processManager.setConfigFile(configFile); processManager.setConfigFile(configFile);
processManager.startSimulation(); processManager.startSimulation();
// Give time for processes to start and connect // Tempo para processos arrancarem e estabelecerem conexões TCP
Thread.sleep(3000); Thread.sleep(3000);
log(" Simulation running (configured duration: " + durationSeconds + "s simulated time)..."); log(" Simulação em curso (duração config: " + durationSeconds + "s tempo simulado)...");
log(" Waiting for coordinator process to complete..."); log(" A aguardar processo Coordenador completar...");
// Wait for the coordinator process to finish naturally // Loop de polling para verificar se o Coordenador terminou
// This automatically handles different time scales // Isso lida automaticamente com diferentes time scales (DES)
int checkInterval = 2; // Check every 2 seconds int checkInterval = 2; // Check every 2 seconds
int elapsed = 0; int elapsed = 0;
int maxWaitSeconds = durationSeconds + 120; // Safety timeout int maxWaitSeconds = durationSeconds + 120; // Timeout de segurança
while (elapsed < maxWaitSeconds) { while (elapsed < maxWaitSeconds) {
if (shouldStop) { if (shouldStop) {
@@ -380,29 +403,29 @@ public class BatchAnalysisDialog {
// Check if simulation completed // Check if simulation completed
if (!processManager.isSimulationRunning()) { if (!processManager.isSimulationRunning()) {
log(" Simulation completed after " + elapsed + "s"); log(" Simulação terminou após " + elapsed + "s");
break; break;
} }
Thread.sleep(checkInterval * 1000L); Thread.sleep(checkInterval * 1000L);
elapsed += checkInterval; elapsed += checkInterval;
// Progress update every 10 seconds // Atualização periódica do log
if (elapsed % 10 == 0 && elapsed < 60) { if (elapsed % 10 == 0 && elapsed < 60) {
log(" " + elapsed + "s elapsed..."); log(" " + elapsed + "s decorridos...");
} }
} }
if (elapsed >= maxWaitSeconds) { if (elapsed >= maxWaitSeconds) {
log(" Timeout reached, forcing stop..."); log(" Timeout atingido, forçando paragem...");
} }
// Stop and collect results // Stop and collect results
log(" Stopping processes..."); log(" A terminar processos...");
processManager.stopSimulation(); processManager.stopSimulation();
Thread.sleep(2000); // Give time for final statistics Thread.sleep(2000); // Tempo para flushing de buffers
// Collect statistics if available // Recolha de estatísticas (Prioridade: Dados reais do socket)
if (sharedStatistics != null) { if (sharedStatistics != null) {
collectRealStatistics(result, sharedStatistics); collectRealStatistics(result, sharedStatistics);
} else { } else {
@@ -412,16 +435,16 @@ public class BatchAnalysisDialog {
return result; return result;
} catch (InterruptedException e) { } catch (InterruptedException e) {
log("Interrupted: " + e.getMessage()); log("Interrompido: " + e.getMessage());
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
stopSimulation(processManager); stopSimulation(processManager);
return null; return null;
} catch (IOException e) { } catch (IOException e) {
log("IO Error: " + e.getMessage()); log("Erro IO: " + e.getMessage());
stopSimulation(processManager); stopSimulation(processManager);
return null; return null;
} catch (RuntimeException e) { } catch (RuntimeException e) {
log("Runtime Error: " + e.getMessage()); log("Erro Runtime: " + e.getMessage());
stopSimulation(processManager); stopSimulation(processManager);
return null; return null;
} }
@@ -431,21 +454,24 @@ public class BatchAnalysisDialog {
try { try {
processManager.stopSimulation(); processManager.stopSimulation();
} catch (Exception ex) { } catch (Exception ex) {
// Ignore cleanup errors // Ignora erros de cleanup
} }
} }
/**
* Popula o objeto de resultado com dados reais capturados pelo Dashboard.
*/
private void collectRealStatistics(SimulationRunResult result, DashboardStatistics stats) { private void collectRealStatistics(SimulationRunResult result, DashboardStatistics stats) {
result.setTotalVehiclesGenerated(stats.getTotalVehiclesGenerated()); result.setTotalVehiclesGenerated(stats.getTotalVehiclesGenerated());
result.setTotalVehiclesCompleted(stats.getTotalVehiclesCompleted()); result.setTotalVehiclesCompleted(stats.getTotalVehiclesCompleted());
result.setAverageSystemTime(stats.getAverageSystemTime() / 1000.0); // Convert ms to seconds result.setAverageSystemTime(stats.getAverageSystemTime() / 1000.0); // Converte ms para segundos
result.setAverageWaitingTime(stats.getAverageWaitingTime() / 1000.0); result.setAverageWaitingTime(stats.getAverageWaitingTime() / 1000.0);
// Set min/max as approximations (would need to be tracked in DashboardStatistics) // Estimação de extremos (o DashboardStatistics deve ser expandido para guardar exatos se necessário)
result.setMinSystemTime(stats.getAverageSystemTime() / 1000.0 * 0.5); result.setMinSystemTime(stats.getAverageSystemTime() / 1000.0 * 0.5);
result.setMaxSystemTime(stats.getAverageSystemTime() / 1000.0 * 2.0); result.setMaxSystemTime(stats.getAverageSystemTime() / 1000.0 * 2.0);
// Collect per-type statistics // Estatísticas por tipo
for (VehicleType type : VehicleType.values()) { for (VehicleType type : VehicleType.values()) {
int count = stats.getVehicleTypeCount(type); int count = stats.getVehicleTypeCount(type);
double waitTime = stats.getAverageWaitingTimeByType(type) / 1000.0; double waitTime = stats.getAverageWaitingTimeByType(type) / 1000.0;
@@ -453,26 +479,29 @@ public class BatchAnalysisDialog {
result.setAvgWaitTimeByType(type, waitTime); result.setAvgWaitTimeByType(type, waitTime);
} }
// Collect per-intersection statistics // Estatísticas por interseção
for (var entry : stats.getAllIntersectionStats().entrySet()) { for (var entry : stats.getAllIntersectionStats().entrySet()) {
String intersectionId = entry.getKey(); String intersectionId = entry.getKey();
DashboardStatistics.IntersectionStats iStats = entry.getValue(); DashboardStatistics.IntersectionStats iStats = entry.getValue();
result.setVehiclesProcessed(intersectionId, iStats.getTotalDepartures()); result.setVehiclesProcessed(intersectionId, iStats.getTotalDepartures());
result.setMaxQueueSize(intersectionId, iStats.getCurrentQueueSize()); result.setMaxQueueSize(intersectionId, iStats.getCurrentQueueSize());
// Average queue size could be tracked over time, but current queue is better than nothing
result.setAvgQueueSize(intersectionId, (double) iStats.getCurrentQueueSize()); result.setAvgQueueSize(intersectionId, (double) iStats.getCurrentQueueSize());
} }
} }
/**
* Gera dados simulados (mock) caso o dashboard não esteja conectado.
* Útil para testes de interface.
*/
private void collectSimulatedStatistics(SimulationRunResult result, String configFile, int durationSeconds) { private void collectSimulatedStatistics(SimulationRunResult result, String configFile, int durationSeconds) {
// Simulated results based on load profile for demonstration // Mock data based on load profile
int baseGenerated = durationSeconds / 3; int baseGenerated = durationSeconds / 3;
double loadFactor = configFile.contains("low") ? 0.2 : double loadFactor = configFile.contains("low") ? 0.2 :
configFile.contains("medium") ? 0.5 : 1.0; configFile.contains("medium") ? 0.5 : 1.0;
int generated = (int)(baseGenerated * loadFactor * 3); int generated = (int)(baseGenerated * loadFactor * 3);
int completed = (int)(generated * (0.85 + Math.random() * 0.1)); // 85-95% completion int completed = (int)(generated * (0.85 + Math.random() * 0.1)); // 85-95% completion rate
double baseSystemTime = 40.0; double baseSystemTime = 40.0;
double congestionFactor = configFile.contains("low") ? 1.0 : double congestionFactor = configFile.contains("low") ? 1.0 :
@@ -485,7 +514,7 @@ public class BatchAnalysisDialog {
result.setMaxSystemTime(baseSystemTime * congestionFactor * 2 + Math.random() * 20); result.setMaxSystemTime(baseSystemTime * congestionFactor * 2 + Math.random() * 20);
result.setAverageWaitingTime(10.0 * congestionFactor + Math.random() * 5); result.setAverageWaitingTime(10.0 * congestionFactor + Math.random() * 5);
log(" Note: Using simulated statistics (real collection requires dashboard integration)"); log(" Nota: A usar estatísticas simuladas (conexão real necessária)");
} }
private void saveScenarioReport(MultiRunAnalyzer analyzer, String scenarioName) { private void saveScenarioReport(MultiRunAnalyzer analyzer, String scenarioName) {
@@ -502,21 +531,23 @@ public class BatchAnalysisDialog {
analyzer.saveReport(reportFile); analyzer.saveReport(reportFile);
analyzer.saveCSV(csvFile); analyzer.saveCSV(csvFile);
log("Report saved: " + reportFile); log("Relatório guardado: " + reportFile);
log("CSV saved: " + csvFile); log("CSV guardado: " + csvFile);
} catch (IOException e) { } catch (IOException e) {
log("Failed to save report: " + e.getMessage()); log("Falha ao guardar relatório: " + e.getMessage());
} }
} }
private void savePartialReport(MultiRunAnalyzer analyzer, String scenarioName) { private void savePartialReport(MultiRunAnalyzer analyzer, String scenarioName) {
if (analyzer.getRunCount() > 0) { if (analyzer.getRunCount() > 0) {
log("Saving partial results..."); log("A guardar resultados parciais...");
saveScenarioReport(analyzer, scenarioName + "_PARTIAL"); saveScenarioReport(analyzer, scenarioName + "_PARTIAL");
} }
} }
// --- Helpers de UI Thread-Safe ---
private void log(String message) { private void log(String message) {
Platform.runLater(() -> { Platform.runLater(() -> {
logArea.appendText(message + "\n"); logArea.appendText(message + "\n");
@@ -527,7 +558,7 @@ public class BatchAnalysisDialog {
private void updateStatus(String status, int current, int total) { private void updateStatus(String status, int current, int total) {
Platform.runLater(() -> { Platform.runLater(() -> {
statusLabel.setText(status); statusLabel.setText(status);
progressLabel.setText(current + " / " + total + " runs completed"); progressLabel.setText(current + " / " + total + " execuções completas");
}); });
} }

View File

@@ -13,16 +13,32 @@ import javafx.stage.Modality;
import javafx.stage.Stage; import javafx.stage.Stage;
/** /**
* Diálogo para configuração avançada de parâmetros da simulação. * Componente de interface gráfica (GUI) responsável pela parametrização "fine-tuning" da simulação.
* Permite ajustar parâmetros em runtime antes de iniciar a simulação. * <p>
* Esta classe apresenta um diálogo modal que permite ao operador sobrepor (override)
* as configurações estáticas carregadas do ficheiro {@code .properties} imediatamente
* antes da execução. Oferece controlo granular sobre:
* <ul>
* <li><b>Geração de Carga:</b> Alternância entre modelos estocásticos (Poisson) e determinísticos.</li>
* <li><b>Temporização:</b> Ajuste da escala de tempo (Time Scale) para visualização vs. performance pura.</li>
* <li><b>Mix de Veículos:</b> Definição das probabilidades de geração por tipo de agente.</li>
* </ul>
*/ */
public class ConfigurationDialog { public class ConfigurationDialog {
/** /**
* Mostra um diálogo com opções avançadas de configuração. * Exibe o diálogo de configuração avançada e captura as intenções do utilizador.
* <p>
* A interface é construída dinamicamente usando layouts JavaFX ({@link GridPane}, {@link VBox}).
* Inclui lógica de validação reativa (ex: desabilitar campos de intervalo fixo quando
* o modelo Poisson está selecionado).
* *
[Image of Poisson distribution graph]
* *
* @param owner janela pai * @param owner A janela pai (Stage) para bloquear a interação até o fecho do diálogo (Modalidade).
* @return true se o utilizador confirmar, false se cancelar * @return {@code true} se o utilizador confirmou as alterações (OK), {@code false} se cancelou.
*/ */
public static boolean showAdvancedConfig(Stage owner) { public static boolean showAdvancedConfig(Stage owner) {
Dialog<ButtonType> dialog = new Dialog<>(); Dialog<ButtonType> dialog = new Dialog<>();
@@ -34,10 +50,11 @@ public class ConfigurationDialog {
// Criar painel de configuração // Criar painel de configuração
VBox content = new VBox(15); VBox content = new VBox(15);
content.setPadding(new Insets(20)); content.setPadding(new Insets(20));
content.setStyle("-fx-background-color: #2b2b2b;");
// Seção 1: Parâmetros de Chegada // Seção 1: Parâmetros de Chegada
Label arrivalHeader = new Label("Parâmetros de Chegada de Veículos"); Label arrivalHeader = new Label("Parâmetros de Chegada de Veículos");
arrivalHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;"); arrivalHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px; -fx-text-fill: white;");
GridPane arrivalGrid = new GridPane(); GridPane arrivalGrid = new GridPane();
arrivalGrid.setHgap(10); arrivalGrid.setHgap(10);
@@ -46,6 +63,7 @@ public class ConfigurationDialog {
// Modelo de chegada // Modelo de chegada
Label modelLabel = new Label("Modelo de chegada:"); Label modelLabel = new Label("Modelo de chegada:");
modelLabel.setStyle("-fx-text-fill: white;");
ComboBox<String> modelCombo = new ComboBox<>(); ComboBox<String> modelCombo = new ComboBox<>();
modelCombo.getItems().addAll("POISSON", "FIXED"); modelCombo.getItems().addAll("POISSON", "FIXED");
modelCombo.setValue("POISSON"); modelCombo.setValue("POISSON");
@@ -54,6 +72,7 @@ public class ConfigurationDialog {
// Taxa de chegada (λ) // Taxa de chegada (λ)
Label rateLabel = new Label("Taxa de chegada (λ) [veículos/s]:"); Label rateLabel = new Label("Taxa de chegada (λ) [veículos/s]:");
rateLabel.setStyle("-fx-text-fill: white;");
Spinner<Double> rateSpinner = new Spinner<>(0.1, 2.0, 0.5, 0.1); Spinner<Double> rateSpinner = new Spinner<>(0.1, 2.0, 0.5, 0.1);
rateSpinner.setEditable(true); rateSpinner.setEditable(true);
rateSpinner.setPrefWidth(100); rateSpinner.setPrefWidth(100);
@@ -62,6 +81,7 @@ public class ConfigurationDialog {
// Intervalo fixo (se aplicável) // Intervalo fixo (se aplicável)
Label intervalLabel = new Label("Intervalo fixo [s]:"); Label intervalLabel = new Label("Intervalo fixo [s]:");
intervalLabel.setStyle("-fx-text-fill: white;");
Spinner<Double> intervalSpinner = new Spinner<>(0.5, 10.0, 2.0, 0.5); Spinner<Double> intervalSpinner = new Spinner<>(0.5, 10.0, 2.0, 0.5);
intervalSpinner.setEditable(true); intervalSpinner.setEditable(true);
intervalSpinner.setPrefWidth(100); intervalSpinner.setPrefWidth(100);
@@ -78,7 +98,7 @@ public class ConfigurationDialog {
// Seção 2: Parâmetros de Tempo // Seção 2: Parâmetros de Tempo
Label timeHeader = new Label("Parâmetros de Tempo"); Label timeHeader = new Label("Parâmetros de Tempo");
timeHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;"); timeHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px; -fx-text-fill: white;");
GridPane timeGrid = new GridPane(); GridPane timeGrid = new GridPane();
timeGrid.setHgap(10); timeGrid.setHgap(10);
@@ -87,6 +107,7 @@ public class ConfigurationDialog {
// Duração da simulação // Duração da simulação
Label durationLabel = new Label("Duração da simulação [s]:"); Label durationLabel = new Label("Duração da simulação [s]:");
durationLabel.setStyle("-fx-text-fill: white;");
Spinner<Integer> durationSpinner = new Spinner<>(60, 7200, 300, 60); Spinner<Integer> durationSpinner = new Spinner<>(60, 7200, 300, 60);
durationSpinner.setEditable(true); durationSpinner.setEditable(true);
durationSpinner.setPrefWidth(100); durationSpinner.setPrefWidth(100);
@@ -95,6 +116,7 @@ public class ConfigurationDialog {
// Escala temporal (para visualização) // Escala temporal (para visualização)
Label scaleLabel = new Label("Escala temporal (0=instantâneo, 1=tempo real):"); Label scaleLabel = new Label("Escala temporal (0=instantâneo, 1=tempo real):");
scaleLabel.setStyle("-fx-text-fill: white;");
Spinner<Double> scaleSpinner = new Spinner<>(0.0, 1.0, 0.01, 0.01); Spinner<Double> scaleSpinner = new Spinner<>(0.0, 1.0, 0.01, 0.01);
scaleSpinner.setEditable(true); scaleSpinner.setEditable(true);
scaleSpinner.setPrefWidth(100); scaleSpinner.setPrefWidth(100);
@@ -103,6 +125,7 @@ public class ConfigurationDialog {
// Tempo de drenagem // Tempo de drenagem
Label drainLabel = new Label("Tempo de drenagem [s]:"); Label drainLabel = new Label("Tempo de drenagem [s]:");
drainLabel.setStyle("-fx-text-fill: white;");
Spinner<Integer> drainSpinner = new Spinner<>(0, 300, 60, 10); Spinner<Integer> drainSpinner = new Spinner<>(0, 300, 60, 10);
drainSpinner.setEditable(true); drainSpinner.setEditable(true);
drainSpinner.setPrefWidth(100); drainSpinner.setPrefWidth(100);
@@ -111,7 +134,7 @@ public class ConfigurationDialog {
// Seção 3: Distribuição de Tipos de Veículos // Seção 3: Distribuição de Tipos de Veículos
Label vehicleHeader = new Label("Distribuição de Tipos de Veículos"); Label vehicleHeader = new Label("Distribuição de Tipos de Veículos");
vehicleHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px;"); vehicleHeader.setStyle("-fx-font-weight: bold; -fx-font-size: 14px; -fx-text-fill: white;");
GridPane vehicleGrid = new GridPane(); GridPane vehicleGrid = new GridPane();
vehicleGrid.setHgap(10); vehicleGrid.setHgap(10);
@@ -119,6 +142,7 @@ public class ConfigurationDialog {
vehicleGrid.setPadding(new Insets(10)); vehicleGrid.setPadding(new Insets(10));
Label bikeLabel = new Label("Bicicletas/Motos [%]:"); Label bikeLabel = new Label("Bicicletas/Motos [%]:");
bikeLabel.setStyle("-fx-text-fill: white;");
Spinner<Integer> bikeSpinner = new Spinner<>(0, 100, 10, 5); Spinner<Integer> bikeSpinner = new Spinner<>(0, 100, 10, 5);
bikeSpinner.setEditable(true); bikeSpinner.setEditable(true);
bikeSpinner.setPrefWidth(100); bikeSpinner.setPrefWidth(100);
@@ -126,6 +150,7 @@ public class ConfigurationDialog {
vehicleGrid.add(bikeSpinner, 1, 0); vehicleGrid.add(bikeSpinner, 1, 0);
Label lightLabel = new Label("Veículos Ligeiros [%]:"); Label lightLabel = new Label("Veículos Ligeiros [%]:");
lightLabel.setStyle("-fx-text-fill: white;");
Spinner<Integer> lightSpinner = new Spinner<>(0, 100, 70, 5); Spinner<Integer> lightSpinner = new Spinner<>(0, 100, 70, 5);
lightSpinner.setEditable(true); lightSpinner.setEditable(true);
lightSpinner.setPrefWidth(100); lightSpinner.setPrefWidth(100);
@@ -133,6 +158,7 @@ public class ConfigurationDialog {
vehicleGrid.add(lightSpinner, 1, 1); vehicleGrid.add(lightSpinner, 1, 1);
Label heavyLabel = new Label("Veículos Pesados [%]:"); Label heavyLabel = new Label("Veículos Pesados [%]:");
heavyLabel.setStyle("-fx-text-fill: white;");
Spinner<Integer> heavySpinner = new Spinner<>(0, 100, 20, 5); Spinner<Integer> heavySpinner = new Spinner<>(0, 100, 20, 5);
heavySpinner.setEditable(true); heavySpinner.setEditable(true);
heavySpinner.setPrefWidth(100); heavySpinner.setPrefWidth(100);
@@ -143,7 +169,7 @@ public class ConfigurationDialog {
Label noteLabel = new Label("Nota: Estes parâmetros sobrepõem os valores do ficheiro .properties selecionado.\n" + Label noteLabel = new Label("Nota: Estes parâmetros sobrepõem os valores do ficheiro .properties selecionado.\n" +
"Para usar os valores padrão do ficheiro, deixe em branco ou cancele."); "Para usar os valores padrão do ficheiro, deixe em branco ou cancele.");
noteLabel.setWrapText(true); noteLabel.setWrapText(true);
noteLabel.setStyle("-fx-font-size: 11px; -fx-text-fill: #666666;"); noteLabel.setStyle("-fx-font-size: 11px; -fx-text-fill: #aaaaaa;");
// Adicionar tudo ao conteúdo // Adicionar tudo ao conteúdo
content.getChildren().addAll( content.getChildren().addAll(

View File

@@ -9,19 +9,42 @@ import sd.protocol.MessageProtocol;
import sd.protocol.SocketConnection; import sd.protocol.SocketConnection;
/** /**
* Processes statistics messages from a single client connection. * Worker responsável pelo processamento dedicado de uma conexão de cliente TCP no Dashboard.
* Runs in a separate thread per client. * <p>
* Esta classe implementa o padrão <i>Thread-per-Client</i>. Cada instância executa numa
* thread separada, garantindo que a latência de rede ou o processamento de mensagens
* de um nó (Interseção/Coordenador) não bloqueie a receção de telemetria dos outros.
* <p>
* As suas principais funções são:
* <ol>
* <li>Manter a conexão persistente com o nó remoto.</li>
* <li>Desserializar mensagens de protocolo recebidas.</li>
* <li>Normalizar payloads JSON (resolvendo ambiguidades de tipagem do Gson).</li>
* <li>Atualizar o objeto partilhado {@link DashboardStatistics} de forma thread-safe.</li>
* </ol>
*/ */
public class DashboardClientHandler implements Runnable { public class DashboardClientHandler implements Runnable {
private final Socket clientSocket; private final Socket clientSocket;
private final DashboardStatistics statistics; private final DashboardStatistics statistics;
/**
* Inicializa o handler com o socket ativo e a referência para o agregador de estatísticas.
*
* @param clientSocket O socket TCP conectado ao nó remoto.
* @param statistics O objeto singleton partilhado onde as métricas serão agregadas.
*/
public DashboardClientHandler(Socket clientSocket, DashboardStatistics statistics) { public DashboardClientHandler(Socket clientSocket, DashboardStatistics statistics) {
this.clientSocket = clientSocket; this.clientSocket = clientSocket;
this.statistics = statistics; this.statistics = statistics;
} }
/**
* Ciclo de vida da conexão.
* <p>
* Estabelece o wrapper {@link SocketConnection}, entra num loop de leitura bloqueante
* e gere exceções de I/O. Garante o fecho limpo do socket em caso de desconexão ou erro.
*/
@Override @Override
public void run() { public void run() {
String clientInfo = clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort(); String clientInfo = clientSocket.getInetAddress().getHostAddress() + ":" + clientSocket.getPort();
@@ -61,6 +84,16 @@ public class DashboardClientHandler implements Runnable {
} }
} }
/**
* Valida e extrai os dados estatísticos da mensagem.
* <p>
* Implementa uma lógica de correção de tipagem para payloads desserializados via Gson.
* Frequentemente, objetos genéricos são desserializados como {@code LinkedHashMap} em vez
* da classe alvo {@link StatsUpdatePayload}. Este método deteta essa situação e realiza
* uma conversão "round-trip" (Map -> JSON -> Object) para garantir a integridade dos dados.
*
* @param message A mensagem recebida da rede.
*/
private void processMessage(MessageProtocol message) { private void processMessage(MessageProtocol message) {
if (message.getType() != MessageType.STATS_UPDATE) { if (message.getType() != MessageType.STATS_UPDATE) {
System.out.println("[Handler] Ignoring non-statistics message type: " + message.getType()); System.out.println("[Handler] Ignoring non-statistics message type: " + message.getType());
@@ -78,6 +111,7 @@ public class DashboardClientHandler implements Runnable {
stats = (StatsUpdatePayload) payload; stats = (StatsUpdatePayload) payload;
} else if (payload instanceof java.util.Map) { } else if (payload instanceof java.util.Map) {
// Gson deserialized as LinkedHashMap - re-serialize and deserialize properly // Gson deserialized as LinkedHashMap - re-serialize and deserialize properly
// This acts as a type-safety bridge for generic JSON payloads
com.google.gson.Gson gson = new com.google.gson.Gson(); com.google.gson.Gson gson = new com.google.gson.Gson();
String json = gson.toJson(payload); String json = gson.toJson(payload);
stats = gson.fromJson(json, StatsUpdatePayload.class); stats = gson.fromJson(json, StatsUpdatePayload.class);
@@ -90,6 +124,15 @@ public class DashboardClientHandler implements Runnable {
updateStatistics(senderId, stats); updateStatistics(senderId, stats);
} }
/**
* Aplica os dados recebidos ao modelo global de estatísticas.
* <p>
* Distingue entre atualizações incrementais (ex: contagem de veículos) e
* substituições de estado (ex: tempo total de sistema reportado pelo nó de saída).
*
* @param senderId Identificador do nó que enviou a atualização (ex: "Cr1", "ExitNode").
* @param stats O objeto DTO contendo as métricas normalizadas.
*/
private void updateStatistics(String senderId, StatsUpdatePayload stats) { private void updateStatistics(String senderId, StatsUpdatePayload stats) {
if (stats.getTotalVehiclesGenerated() >= 0) { if (stats.getTotalVehiclesGenerated() >= 0) {
statistics.updateVehiclesGenerated(stats.getTotalVehiclesGenerated()); statistics.updateVehiclesGenerated(stats.getTotalVehiclesGenerated());

View File

@@ -10,17 +10,43 @@ import java.util.concurrent.atomic.AtomicBoolean;
import sd.config.SimulationConfig; import sd.config.SimulationConfig;
/** /**
* Agrega e apresenta estatísticas em tempo real de todos os processos da simulação. * Servidor central de agregação de telemetria e estatísticas.
* Usa um thread pool para gerir ligações concorrentes de clientes. * <p>
* Este componente atua como o nó de monitorização do sistema distribuído.
* Implementa uma arquitetura de servidor concorrente utilizando um {@link ExecutorService}
* (Thread Pool) para gerir múltiplas conexões de entrada simultâneas provenientes
* das Interseções, Coordenador e Nó de Saída.
* <p>
* Suporta dois modos de operação:
* <ul>
* <li><b>Headless (CLI):</b> Renderização periódica de métricas no terminal (stdout).</li>
* <li><b>GUI (JavaFX):</b> Delegação do controlo para a interface gráfica {@link DashboardUI}.</li>
* </ul>
*/ */
public class DashboardServer { public class DashboardServer {
private final int port; private final int port;
/** Armazenamento em memória (Thread-safe) do estado global do sistema. */
private final DashboardStatistics statistics; private final DashboardStatistics statistics;
/** Pool de threads para isolamento de falhas e gestão de recursos de I/O. */
private final ExecutorService clientHandlerPool; private final ExecutorService clientHandlerPool;
/** Flag atómica para controlo seguro do ciclo de vida do servidor. */
private final AtomicBoolean running; private final AtomicBoolean running;
private ServerSocket serverSocket; private ServerSocket serverSocket;
/**
* Ponto de entrada (Bootstrap) da aplicação de monitorização.
* <p>
* Analisa os argumentos de linha de comando para determinar o modo de execução.
* Se a flag {@code --gui} ou {@code -g} estiver presente, inicia o subsistema JavaFX.
* Caso contrário, inicia o modo servidor de terminal padrão.
*
* @param args Argumentos de CLI (ex: caminho do config, flags de modo).
*/
public static void main(String[] args) { public static void main(String[] args) {
// Check if GUI mode is requested // Check if GUI mode is requested
boolean useGUI = false; boolean useGUI = false;
@@ -70,13 +96,24 @@ public class DashboardServer {
} }
} }
/**
* Inicializa a infraestrutura do servidor.
*
* @param config A configuração carregada contendo a porta de escuta.
*/
public DashboardServer(SimulationConfig config) { public DashboardServer(SimulationConfig config) {
this.port = config.getDashboardPort(); this.port = config.getDashboardPort();
this.statistics = new DashboardStatistics(); this.statistics = new DashboardStatistics();
// Fixed pool limita o consumo de recursos, prevenindo exaustão sob carga alta
this.clientHandlerPool = Executors.newFixedThreadPool(10); this.clientHandlerPool = Executors.newFixedThreadPool(10);
this.running = new AtomicBoolean(false); this.running = new AtomicBoolean(false);
} }
/**
* Inicia a escuta por conexões (Bind & Listen) e a thread de despacho.
*
* @throws IOException Se a porta já estiver em uso ou ocorrer erro de bind.
*/
public void start() throws IOException { public void start() throws IOException {
if (running.get()) { if (running.get()) {
System.out.println("Dashboard Server is already running."); System.out.println("Dashboard Server is already running.");
@@ -95,6 +132,13 @@ public class DashboardServer {
acceptThread.start(); acceptThread.start();
} }
/**
* Loop principal de aceitação de conexões (Dispatcher).
* <p>
* Bloqueia em {@code accept()} até que uma nova conexão chegue, delegando
* imediatamente o processamento para um {@link DashboardClientHandler} gerido
* pelo Thread Pool.
*/
private void acceptConnections() { private void acceptConnections() {
while (running.get()) { while (running.get()) {
try { try {
@@ -112,6 +156,10 @@ public class DashboardServer {
} }
} }
/**
* Ciclo de renderização de métricas para o modo CLI (Headless).
* Atualiza o ecrã a cada 5 segundos.
*/
@SuppressWarnings("BusyWait") @SuppressWarnings("BusyWait")
private void displayLoop() { private void displayLoop() {
final long DISPLAY_INTERVAL_MS = 5000; final long DISPLAY_INTERVAL_MS = 5000;
@@ -127,6 +175,9 @@ public class DashboardServer {
} }
} }
/**
* Renderiza o snapshot atual das estatísticas no stdout.
*/
public void displayStatistics() { public void displayStatistics() {
System.out.println("\n" + "=".repeat(60)); System.out.println("\n" + "=".repeat(60));
System.out.println("REAL-TIME SIMULATION STATISTICS"); System.out.println("REAL-TIME SIMULATION STATISTICS");
@@ -135,6 +186,13 @@ public class DashboardServer {
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
} }
/**
* Procedimento de encerramento gracioso (Graceful Shutdown).
* <p>
* 1. Altera flag de execução.
* 2. Fecha o socket do servidor para desbloquear a thread de aceitação.
* 3. Força o encerramento do pool de threads de clientes.
*/
public void stop() { public void stop() {
if (!running.get()) { if (!running.get()) {
return; return;

View File

@@ -9,8 +9,13 @@ import java.util.concurrent.atomic.AtomicLong;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* Armazenamento thread-safe de estatísticas agregadas da simulação. * Repositório central de estado da simulação, desenhado para acesso concorrente de alta frequência.
* Usa tipos atómicos e coleções concorrentes para atualizações sem locks. * <p>
* Esta classe atua como a "Single Source of Truth" para o Dashboard. Utiliza primitivas
* de concorrência do pacote {@code java.util.concurrent} (como {@link AtomicInteger} e
* {@link ConcurrentHashMap}) para permitir leituras e escritas simultâneas sem a necessidade
* de bloqueios explícitos (Lock-Free), minimizando a latência de processamento das mensagens
* recebidas dos múltiplos nós da rede.
*/ */
public class DashboardStatistics { public class DashboardStatistics {
@@ -19,13 +24,21 @@ public class DashboardStatistics {
private final AtomicLong totalSystemTime; private final AtomicLong totalSystemTime;
private final AtomicLong totalWaitingTime; private final AtomicLong totalWaitingTime;
/** Mapa thread-safe para armazenar métricas granulares por interseção. */
private final Map<String, IntersectionStats> intersectionStats; private final Map<String, IntersectionStats> intersectionStats;
private final Map<VehicleType, AtomicInteger> vehicleTypeCount; private final Map<VehicleType, AtomicInteger> vehicleTypeCount;
private final Map<VehicleType, AtomicLong> vehicleTypeWaitTime; private final Map<VehicleType, AtomicLong> vehicleTypeWaitTime;
/** Timestamp da última atualização de escrita, com garantia de visibilidade de memória (volatile). */
private volatile long lastUpdateTime; private volatile long lastUpdateTime;
/** Buffer para sinalização assíncrona de mudança de política (Dashboard -> Coordenador). */
private volatile String requestedRoutingPolicy; private volatile String requestedRoutingPolicy;
/**
* Inicializa os contadores atómicos e as estruturas de dados concorrentes.
*/
public DashboardStatistics() { public DashboardStatistics() {
this.totalVehiclesGenerated = new AtomicInteger(0); this.totalVehiclesGenerated = new AtomicInteger(0);
this.totalVehiclesCompleted = new AtomicInteger(0); this.totalVehiclesCompleted = new AtomicInteger(0);
@@ -95,6 +108,17 @@ public class DashboardStatistics {
updateTimestamp(); updateTimestamp();
} }
/**
* Atualiza ou inicializa atomicamente as estatísticas de uma interseção específica.
* <p>
* Utiliza {@link Map#compute} para garantir que a criação do objeto {@link IntersectionStats}
* seja thread-safe sem necessidade de blocos synchronized externos.
*
* @param intersectionId ID da interseção.
* @param arrivals Total acumulado de chegadas.
* @param departures Total acumulado de partidas.
* @param currentQueueSize Tamanho instantâneo da fila.
*/
public void updateIntersectionStats(String intersectionId, int arrivals, public void updateIntersectionStats(String intersectionId, int arrivals,
int departures, int currentQueueSize) { int departures, int currentQueueSize) {
intersectionStats.compute(intersectionId, (id, stats) -> { intersectionStats.compute(intersectionId, (id, stats) -> {
@@ -111,6 +135,8 @@ public class DashboardStatistics {
lastUpdateTime = System.currentTimeMillis(); lastUpdateTime = System.currentTimeMillis();
} }
// --- Getters e Métricas Calculadas ---
public int getTotalVehiclesGenerated() { public int getTotalVehiclesGenerated() {
return totalVehiclesGenerated.get(); return totalVehiclesGenerated.get();
} }
@@ -119,12 +145,20 @@ public class DashboardStatistics {
return totalVehiclesCompleted.get(); return totalVehiclesCompleted.get();
} }
/**
* Calcula o tempo médio no sistema em tempo real.
* @return Média em milissegundos (0.0 se nenhum veículo completou).
*/
public double getAverageSystemTime() { public double getAverageSystemTime() {
int completed = totalVehiclesCompleted.get(); int completed = totalVehiclesCompleted.get();
if (completed == 0) return 0.0; if (completed == 0) return 0.0;
return (double) totalSystemTime.get() / completed; return (double) totalSystemTime.get() / completed;
} }
/**
* Calcula o tempo médio de espera em tempo real.
* @return Média em milissegundos (0.0 se nenhum veículo completou).
*/
public double getAverageWaitingTime() { public double getAverageWaitingTime() {
int completed = totalVehiclesCompleted.get(); int completed = totalVehiclesCompleted.get();
if (completed == 0) return 0.0; if (completed == 0) return 0.0;
@@ -154,10 +188,11 @@ public class DashboardStatistics {
} }
/** /**
* Obtém os tamanhos atuais das filas de todas as interseções. * Obtém um snapshot dos tamanhos atuais das filas de todas as interseções.
* Usado pela política LEAST_CONGESTED para roteamento dinâmico. * <p>
* * Utilizado primariamente pelo algoritmo de roteamento dinâmico (LEAST_CONGESTED)
* @return mapa com intersectionId -> queueSize * para tomar decisões de encaminhamento baseadas na carga atual da rede.
* * @return Mapa contendo {@code intersectionId -> queueSize}.
*/ */
public Map<String, Integer> getCurrentQueueSizes() { public Map<String, Integer> getCurrentQueueSizes() {
Map<String, Integer> queueSizes = new HashMap<>(); Map<String, Integer> queueSizes = new HashMap<>();
@@ -168,16 +203,17 @@ public class DashboardStatistics {
} }
/** /**
* Define a política de roteamento solicitada pelo dashboard. * Regista uma intenção de mudança de política de roteamento solicitada pela UI.
* O coordenador deve verificar periodicamente e aplicar a mudança. * O Coordenador fará polling deste valor periodicamente.
*/ */
public void setRequestedRoutingPolicy(String policy) { public void setRequestedRoutingPolicy(String policy) {
this.requestedRoutingPolicy = policy; this.requestedRoutingPolicy = policy;
} }
/** /**
* Obtém e limpa a política de roteamento solicitada. * Obtém e limpa atomicamente a política de roteamento solicitada.
* Retorna null se não houver mudança pendente. * Implementa a semântica de consumo único (one-time consumption).
* * @return A política solicitada ou null se não houver mudança pendente.
*/ */
public synchronized String getAndClearRequestedRoutingPolicy() { public synchronized String getAndClearRequestedRoutingPolicy() {
String policy = this.requestedRoutingPolicy; String policy = this.requestedRoutingPolicy;
@@ -185,6 +221,10 @@ public class DashboardStatistics {
return policy; return policy;
} }
/**
* Imprime um resumo formatado das estatísticas no stdout.
* Útil para o modo CLI (Headless).
*/
public void display() { public void display() {
System.out.println("\n--- GLOBAL STATISTICS ---"); System.out.println("\n--- GLOBAL STATISTICS ---");
System.out.printf("Total Vehicles Generated: %d%n", getTotalVehiclesGenerated()); System.out.printf("Total Vehicles Generated: %d%n", getTotalVehiclesGenerated());
@@ -214,6 +254,10 @@ public class DashboardStatistics {
System.out.printf("%nLast Update: %tT%n", lastUpdateTime); System.out.printf("%nLast Update: %tT%n", lastUpdateTime);
} }
/**
* Agregado de métricas específico para um nó de interseção.
* Mantém contadores atómicos para garantir consistência em atualizações concorrentes.
*/
public static class IntersectionStats { public static class IntersectionStats {
private final String intersectionId; private final String intersectionId;
private final AtomicInteger totalArrivals; private final AtomicInteger totalArrivals;

View File

@@ -30,8 +30,22 @@ import sd.config.SimulationConfig;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* JavaFX-based Dashboard UI for displaying real-time simulation statistics. * Interface Gráfica (GUI) baseada em JavaFX para visualização de telemetria em tempo real.
* Provides a graphical interface with auto-updating statistics panels. * <p>
* Esta classe atua como a camada de apresentação (View) do sistema. Implementa o padrão
* <i>Observer</i> (via polling) para refletir o estado do modelo {@link DashboardStatistics}
* nos componentes visuais.
* <p>
* <b>Aspetos Técnicos Relevantes:</b>
* <ul>
* <li><b>Concorrência de UI:</b> Utiliza um {@link ScheduledExecutorService} para buscar dados
* em background e {@link Platform#runLater(Runnable)} para injetar atualizações na
* <i>JavaFX Application Thread</i>, evitando exceções de "Not on FX application thread".</li>
* <li><b>Data Binding:</b> Utiliza {@link TableView} com classes internas (DTOs) para
* renderização tabular eficiente de tipos de veículos e interseções.</li>
* <li><b>Controlo de Processos:</b> Integra com {@link SimulationProcessManager} para orquestrar
* o ciclo de vida (spawn/kill) dos processos externos da simulação.</li>
* </ul>
*/ */
public class DashboardUI extends Application { public class DashboardUI extends Application {
@@ -52,7 +66,7 @@ public class DashboardUI extends Application {
// Intersection Table // Intersection Table
private TableView<IntersectionRow> intersectionTable; private TableView<IntersectionRow> intersectionTable;
// Update scheduler // Update scheduler (Background Thread)
private ScheduledExecutorService updateScheduler; private ScheduledExecutorService updateScheduler;
// Configuration controls // Configuration controls
@@ -60,6 +74,10 @@ public class DashboardUI extends Application {
private String selectedConfigFile = "simulation.properties"; private String selectedConfigFile = "simulation.properties";
private Label configInfoLabel; private Label configInfoLabel;
/**
* Ponto de entrada da aplicação JavaFX.
* Configura o Stage primário, inicializa o servidor de backend e constrói a árvore de cena (Scene Graph).
*/
@Override @Override
public void start(Stage primaryStage) { public void start(Stage primaryStage) {
try { try {
@@ -72,29 +90,28 @@ public class DashboardUI extends Application {
server = new DashboardServer(config); server = new DashboardServer(config);
statistics = server.getStatistics(); statistics = server.getStatistics();
// Start the dashboard server // Start the dashboard server (Backend listening port)
server.start(); server.start();
// Build UI // Build UI Layout
BorderPane root = new BorderPane(); BorderPane root = new BorderPane();
root.getStyleClass().add("root"); root.getStyleClass().add("root");
root.setStyle("-fx-background-color: #2b2b2b;");
// Header // Header (Top)
VBox header = createHeader(); VBox header = createHeader();
root.setTop(header); root.setTop(header);
// Main content // Main content (Center)
VBox mainContent = createMainContent(); VBox mainContent = createMainContent();
root.setCenter(mainContent); root.setCenter(mainContent);
// Footer // Footer (Bottom)
HBox footer = createFooter(); HBox footer = createFooter();
root.setBottom(footer); root.setBottom(footer);
// Create scene // Create scene & apply CSS
Scene scene = new Scene(root, 1200, 850); Scene scene = new Scene(root, 1200, 850);
// Load CSS
String cssUrl = getClass().getResource("/dashboard.css").toExternalForm(); String cssUrl = getClass().getResource("/dashboard.css").toExternalForm();
scene.getStylesheets().add(cssUrl); scene.getStylesheets().add(cssUrl);
@@ -102,10 +119,10 @@ public class DashboardUI extends Application {
primaryStage.setScene(scene); primaryStage.setScene(scene);
primaryStage.show(); primaryStage.show();
// Start periodic updates // Start periodic updates loop
startPeriodicUpdates(); startPeriodicUpdates();
// Handle window close // Handle window close (Graceful shutdown)
primaryStage.setOnCloseRequest(event -> { primaryStage.setOnCloseRequest(event -> {
shutdown(); shutdown();
}); });
@@ -149,6 +166,8 @@ public class DashboardUI extends Application {
// Passar o ficheiro de configuração selecionado // Passar o ficheiro de configuração selecionado
processManager.setConfigFile(selectedConfigFile); processManager.setConfigFile(selectedConfigFile);
processManager.startSimulation(); processManager.startSimulation();
// Toggle UI state
btnStart.setDisable(true); btnStart.setDisable(true);
btnStop.setDisable(false); btnStop.setDisable(false);
configFileSelector.setDisable(true); // Bloquear mudanças durante simulação configFileSelector.setDisable(true); // Bloquear mudanças durante simulação
@@ -159,6 +178,8 @@ public class DashboardUI extends Application {
btnStop.setOnAction(e -> { btnStop.setOnAction(e -> {
processManager.stopSimulation(); processManager.stopSimulation();
// Toggle UI state
btnStart.setDisable(false); btnStart.setDisable(false);
btnStop.setDisable(true); btnStop.setDisable(true);
configFileSelector.setDisable(false); // Desbloquear para nova simulação configFileSelector.setDisable(false); // Desbloquear para nova simulação
@@ -435,13 +456,23 @@ public class DashboardUI extends Application {
grid.add(container, colGroup, row); grid.add(container, colGroup, row);
} }
/**
* Inicia o ciclo de polling em background.
* Atualiza a UI a cada 100ms.
*/
private void startPeriodicUpdates() { private void startPeriodicUpdates() {
updateScheduler = Executors.newSingleThreadScheduledExecutor(); updateScheduler = Executors.newSingleThreadScheduledExecutor();
updateScheduler.scheduleAtFixedRate(() -> { updateScheduler.scheduleAtFixedRate(() -> {
// Crucial: Encapsular atualização de UI em Platform.runLater
// para garantir execução na JavaFX Application Thread
Platform.runLater(this::updateUI); Platform.runLater(this::updateUI);
}, 0, 100, TimeUnit.MILLISECONDS); }, 0, 100, TimeUnit.MILLISECONDS);
} }
/**
* Sincroniza o estado atual do objeto Statistics com os controlos JavaFX.
* Chamado periodicamente pela thread de UI.
*/
private void updateUI() { private void updateUI() {
// Update global statistics // Update global statistics
lblVehiclesGenerated.setText(String.valueOf(statistics.getTotalVehiclesGenerated())); lblVehiclesGenerated.setText(String.valueOf(statistics.getTotalVehiclesGenerated()));
@@ -548,7 +579,9 @@ public class DashboardUI extends Application {
launch(args); launch(args);
} }
// Inner classes for TableView data models // --- DTOs para Data Binding nas Tabelas ---
/** DTO para linhas da tabela de Tipos de Veículo. */
public static class VehicleTypeRow { public static class VehicleTypeRow {
private final String vehicleType; private final String vehicleType;
private final int count; private final int count;
@@ -560,19 +593,12 @@ public class DashboardUI extends Application {
this.avgWaitTime = avgWaitTime; this.avgWaitTime = avgWaitTime;
} }
public String getVehicleType() { public String getVehicleType() { return vehicleType; }
return vehicleType; public int getCount() { return count; }
} public String getAvgWaitTime() { return avgWaitTime; }
public int getCount() {
return count;
}
public String getAvgWaitTime() {
return avgWaitTime;
}
} }
/** DTO para linhas da tabela de Interseções. */
public static class IntersectionRow { public static class IntersectionRow {
private final String intersectionId; private final String intersectionId;
private final int arrivals; private final int arrivals;
@@ -586,20 +612,9 @@ public class DashboardUI extends Application {
this.queueSize = queueSize; this.queueSize = queueSize;
} }
public String getIntersectionId() { public String getIntersectionId() { return intersectionId; }
return intersectionId; public int getArrivals() { return arrivals; }
} public int getDepartures() { return departures; }
public int getQueueSize() { return queueSize; }
public int getArrivals() {
return arrivals;
}
public int getDepartures() {
return departures;
}
public int getQueueSize() {
return queueSize;
}
} }
} }

View File

@@ -6,9 +6,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Gere o ciclo de vida dos processos de simulação (Intersections, Exit Node, * Orquestrador de processos para o ambiente de simulação distribuída.
* Coordinator). * <p>
* Permite iniciar e parar a simulação distribuída dentro da aplicação Java. * Esta classe atua como um supervisor (Process Manager), responsável pelo <i>bootstrapping</i>
* e <i>teardown</i> das múltiplas Java Virtual Machines (JVMs) que compõem o sistema.
* <p>
* Funcionalidades principais:
* <ul>
* <li><b>Isolamento:</b> Cada nó (Interseção, Coordinator, ExitNode) corre no seu próprio processo OS.</li>
* <li><b>Ordem de Arranque:</b> Garante que os servidores (Interseções) estão online antes dos clientes (Coordenador).</li>
* <li><b>Gestão de Logs:</b> Redireciona stdout/stderr de cada processo filho para ficheiros temporários para facilitar o debug.</li>
* </ul>
*/ */
public class SimulationProcessManager { public class SimulationProcessManager {
@@ -16,6 +24,10 @@ public class SimulationProcessManager {
private final String classpath; private final String classpath;
private String configFile; private String configFile;
/**
* Inicializa o gestor capturando o classpath da JVM atual.
* Isto garante que os processos filhos herdam as mesmas dependências e configurações de ambiente.
*/
public SimulationProcessManager() { public SimulationProcessManager() {
this.runningProcesses = new ArrayList<>(); this.runningProcesses = new ArrayList<>();
this.classpath = System.getProperty("java.class.path"); this.classpath = System.getProperty("java.class.path");
@@ -23,9 +35,9 @@ public class SimulationProcessManager {
} }
/** /**
* Define o ficheiro de configuração a usar. * Define o perfil de configuração a ser injetado nos processos filhos.
* * Útil para alternar entre cenários (Low/Medium/High Load) dinamicamente.
* @param configFile nome do ficheiro (ex: "simulation-low.properties") * * @param configFile Nome do ficheiro de propriedades (ex: "simulation-low.properties").
*/ */
public void setConfigFile(String configFile) { public void setConfigFile(String configFile) {
this.configFile = "src/main/resources/" + configFile; this.configFile = "src/main/resources/" + configFile;
@@ -33,9 +45,16 @@ public class SimulationProcessManager {
} }
/** /**
* Inicia a simulação completa: 5 Intersections, 1 Exit Node, e 1 Coordinator. * Executa o procedimento de arranque (Bootstrap) da simulação distribuída.
* * <p>
* @throws IOException se um processo falhar ao iniciar * A ordem de inicialização é crítica para evitar <i>Race Conditions</i> na conexão TCP:
* <ol>
* <li><b>Workers (Interseções):</b> Iniciam os ServerSockets.</li>
* <li><b>Sink (Exit Node):</b> Prepara-se para receber métricas finais.</li>
* <li><b>Delay de Estabilização:</b> Pausa de 1s para garantir que os sockets estão em LISTENING.</li>
* <li><b>Source (Coordinator):</b> Inicia a geração de carga e conecta-se aos nós.</li>
* </ol>
* * @throws IOException Se falhar o fork de algum processo.
*/ */
public void startSimulation() throws IOException { public void startSimulation() throws IOException {
if (!runningProcesses.isEmpty()) { if (!runningProcesses.isEmpty()) {
@@ -65,8 +84,11 @@ public class SimulationProcessManager {
} }
/** /**
* Checks if the coordinator process (last process started) is still running. * Verifica o estado de "liveness" da simulação monitorizando o processo Coordenador.
* When the coordinator finishes, the simulation is complete. * <p>
* Como o Coordenador gere o relógio DES e a geração de eventos, a sua terminação
* (após o drain time) sinaliza o fim efetivo da simulação.
* * @return true se o Coordenador ainda estiver ativo (alive).
*/ */
public boolean isSimulationRunning() { public boolean isSimulationRunning() {
if (runningProcesses.isEmpty()) { if (runningProcesses.isEmpty()) {
@@ -78,8 +100,10 @@ public class SimulationProcessManager {
} }
/** /**
* Waits for the simulation to complete naturally. * Bloqueia a thread atual até que a simulação termine naturalmente ou ocorra timeout.
* Returns true if completed, false if timeout. * * @param timeoutSeconds Tempo máximo de espera.
* @return true se terminou, false se o timeout expirou.
* @throws InterruptedException Se a espera for interrompida.
*/ */
public boolean waitForCompletion(long timeoutSeconds) throws InterruptedException { public boolean waitForCompletion(long timeoutSeconds) throws InterruptedException {
if (runningProcesses.isEmpty()) { if (runningProcesses.isEmpty()) {
@@ -91,7 +115,11 @@ public class SimulationProcessManager {
} }
/** /**
* Stops all running simulation processes. * Executa o procedimento de encerramento (Teardown) de todos os processos.
* <p>
* Tenta primeiro uma paragem graciosa (`SIGTERM`), aguarda meio segundo, e
* força a paragem (`SIGKILL`) para processos persistentes, garantindo que não
* ficam processos órfãos no SO.
*/ */
public void stopSimulation() { public void stopSimulation() {
System.out.println("Stopping simulation processes..."); System.out.println("Stopping simulation processes...");
@@ -120,7 +148,8 @@ public class SimulationProcessManager {
} }
/** /**
* Helper para iniciar um único processo Java. * Helper de baixo nível para construção e lançamento de processos Java.
* Configura o redirecionamento de I/O para ficheiros de log na diretoria temporária do SO.
*/ */
private void startProcess(String className, String arg) throws IOException { private void startProcess(String className, String arg) throws IOException {
String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";

View File

@@ -4,7 +4,14 @@ import sd.model.MessageType;
import sd.protocol.MessageProtocol; import sd.protocol.MessageProtocol;
/** /**
* Message wrapper for sending statistics to the dashboard. * Implementação concreta do protocolo de mensagens destinada ao transporte de telemetria.
* <p>
* Esta classe atua como um envelope especializado para o envio de dados estatísticos
* (encapsulados em {@link StatsUpdatePayload}) dos nós operacionais (Interseções, Coordenador)
* para o servidor de Dashboard centralizado.
* <p>
* Diferencia-se das mensagens de controlo genéricas por ter o destino fixado no
* "DashboardServer" e um tipo de mensagem imutável ({@code STATS_UPDATE}).
*/ */
public class StatsMessage implements MessageProtocol { public class StatsMessage implements MessageProtocol {
@@ -14,27 +21,49 @@ public class StatsMessage implements MessageProtocol {
private final String destinationNode; private final String destinationNode;
private final StatsUpdatePayload payload; private final StatsUpdatePayload payload;
/**
* Cria uma nova mensagem de estatística.
*
* @param sourceNode O ID do nó que gerou as estatísticas (ex: "Cr1", "ExitNode").
* @param payload O objeto DTO contendo os dados estatísticos brutos ou agregados.
*/
public StatsMessage(String sourceNode, StatsUpdatePayload payload) { public StatsMessage(String sourceNode, StatsUpdatePayload payload) {
this.sourceNode = sourceNode; this.sourceNode = sourceNode;
this.destinationNode = "DashboardServer"; this.destinationNode = "DashboardServer"; // Destino implícito e fixo
this.payload = payload; this.payload = payload;
} }
/**
* Retorna o tipo da mensagem, que identifica semanticamente o conteúdo para o recetor.
* @return Sempre {@link MessageType#STATS_UPDATE}.
*/
@Override @Override
public MessageType getType() { public MessageType getType() {
return MessageType.STATS_UPDATE; return MessageType.STATS_UPDATE;
} }
/**
* Obtém a carga útil da mensagem.
* @return O objeto {@link StatsUpdatePayload} associado.
*/
@Override @Override
public Object getPayload() { public Object getPayload() {
return payload; return payload;
} }
/**
* Identifica a origem da mensagem.
* @return O ID do nó remetente.
*/
@Override @Override
public String getSourceNode() { public String getSourceNode() {
return sourceNode; return sourceNode;
} }
/**
* Identifica o destino da mensagem.
* @return Sempre "DashboardServer".
*/
@Override @Override
public String getDestinationNode() { public String getDestinationNode() {
return destinationNode; return destinationNode;

View File

@@ -7,25 +7,60 @@ import java.util.Map;
import sd.model.VehicleType; import sd.model.VehicleType;
/** /**
* DTO para atualizações de estatísticas ao dashboard. * Objeto de Transferência de Dados (DTO) otimizado para transporte de telemetria.
* Campos com valor -1 não são atualizados nesta mensagem. * <p>
* Esta classe encapsula as métricas de desempenho enviadas pelos nós da simulação (Coordenador,
* Interseções, ExitNode) para o Dashboard. Foi desenhada para suportar <b>atualizações parciais</b>
* (Sparse Updates):
* <ul>
* <li>Campos globais inicializados com {@code -1} indicam "sem alteração" (no-op). O Dashboard
* deve ignorar estes campos e manter o valor acumulado anterior.</li>
* <li>Campos de interseção ({@code arrivals}, {@code departures}) representam deltas ou snapshots
* específicos do nó remetente.</li>
* </ul>
* Implementa {@link Serializable} para transmissão direta via Java Sockets.
*
[Image of data transfer object pattern]
*/ */
public class StatsUpdatePayload implements Serializable { public class StatsUpdatePayload implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
// Global Metrics (Coordinator/ExitNode)
/** Total gerado. Valor -1 indica que este campo deve ser ignorado na atualização. */
private int totalVehiclesGenerated = -1; private int totalVehiclesGenerated = -1;
/** Total completado. Valor -1 indica que este campo deve ser ignorado. */
private int totalVehiclesCompleted = -1; private int totalVehiclesCompleted = -1;
/** Tempo total de sistema acumulado (ms). Valor -1 indica que deve ser ignorado. */
private long totalSystemTime = -1; private long totalSystemTime = -1;
/** Tempo total de espera acumulado (ms). Valor -1 indica que deve ser ignorado. */
private long totalWaitingTime = -1; private long totalWaitingTime = -1;
// Intersection Metrics (Worker Nodes)
/** Número de veículos que entraram na interseção desde o último reporte. */
private int intersectionArrivals = 0; private int intersectionArrivals = 0;
/** Número de veículos que saíram da interseção desde o último reporte. */
private int intersectionDepartures = 0; private int intersectionDepartures = 0;
/** Snapshot do tamanho atual da fila na interseção. */
private int intersectionQueueSize = 0; private int intersectionQueueSize = 0;
// Detailed Breakdowns
/** Contagem acumulada por tipo de veículo. */
private Map<VehicleType, Integer> vehicleTypeCounts; private Map<VehicleType, Integer> vehicleTypeCounts;
/** Tempos de espera acumulados por tipo de veículo. */
private Map<VehicleType, Long> vehicleTypeWaitTimes; private Map<VehicleType, Long> vehicleTypeWaitTimes;
/**
* Inicializa o payload com os mapas vazios e contadores globais a -1 (estado neutro).
*/
public StatsUpdatePayload() { public StatsUpdatePayload() {
this.vehicleTypeCounts = new HashMap<>(); this.vehicleTypeCounts = new HashMap<>();
this.vehicleTypeWaitTimes = new HashMap<>(); this.vehicleTypeWaitTimes = new HashMap<>();
@@ -67,6 +102,8 @@ public class StatsUpdatePayload implements Serializable {
return vehicleTypeWaitTimes; return vehicleTypeWaitTimes;
} }
// Setters implementam Fluent Interface para construção encadeada
public StatsUpdatePayload setTotalVehiclesGenerated(int totalVehiclesGenerated) { public StatsUpdatePayload setTotalVehiclesGenerated(int totalVehiclesGenerated) {
this.totalVehiclesGenerated = totalVehiclesGenerated; this.totalVehiclesGenerated = totalVehiclesGenerated;
return this; return this;

View File

@@ -3,11 +3,15 @@ package sd.des;
/** /**
* Gere o tempo de simulação para Simulação de Eventos Discretos. * Gere o tempo de simulação para Simulação de Eventos Discretos.
* *
* <p>No DES, o tempo avança em saltos discretos de evento para evento, * <p>
* não de forma contínua como o tempo real.</p> * No DES, o tempo avança em saltos discretos de evento para evento,
* não de forma contínua como o tempo real.
* </p>
* *
* <p>Esta classe garante que todos os processos no sistema distribuído * <p>
* mantêm uma visão sincronizada do tempo de simulação.</p> * Esta classe garante que todos os processos no sistema distribuído
* mantêm uma visão sincronizada do tempo de simulação.
* </p>
*/ */
public class SimulationClock { public class SimulationClock {
private double currentTime; private double currentTime;

View File

@@ -3,31 +3,46 @@ package sd.des;
import java.io.Serializable; import java.io.Serializable;
/** /**
* Evento discreto da simulação. * Representa um evento atómico e imutável no contexto da Simulação de Eventos Discretos (DES).
* * <p>
* <p>Unidade fundamental de execução num sistema DES: * Esta classe é a unidade fundamental de processamento. Numa arquitetura DES, o estado do sistema
* não muda continuamente, mas sim em instantes discretos definidos por estes eventos.
* <p>
* Características principais:
* <ul> * <ul>
* <li>timestamp - quando ocorre * <li><b>Ordenação Temporal:</b> Implementa {@link Comparable} para ser armazenado numa Fila de
* <li>type - o que acontece * Eventos Futuros (FEL - Future Event List), garantindo execução cronológica.</li>
* <li>payload - dados associados * <li><b>Distribuído:</b> Implementa {@link Serializable} para permitir que eventos gerados num nó
* <li>location - qual processo o trata * (ex: Coordenador) sejam transmitidos e executados noutro (ex: Interseção).</li>
* <li><b>Polimórfico:</b> Transporta um {@code payload} genérico, permitindo associar qualquer
* entidade (Veículo, Sinal, etc.) ao evento.</li>
* </ul> * </ul>
*/ */
public class SimulationEvent implements Comparable<SimulationEvent>, Serializable { public class SimulationEvent implements Comparable<SimulationEvent>, Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** O instante virtual exato em que o evento deve ser processado. */
private final double timestamp; private final double timestamp;
/** A categoria do evento (ex: VEHICLE_ARRIVAL, LIGHT_CHANGE). */
private final DESEventType type; private final DESEventType type;
/** Dados contextuais associados (ex: o objeto Vehicle que chegou). */
private final Object payload; private final Object payload;
private final String location; // Process ID (e.g., "Cr1", "Coordinator", "Exit")
/** /**
* Cria um novo evento de simulação. * O identificador do nó de destino onde o evento deve ser executado.
* (ex: "Cr1", "Coordinator", "ExitNode"). Se null, é um evento local.
*/
private final String location;
/**
* Instancia um novo evento de simulação completo.
* *
* @param timestamp instante do evento (tempo de simulação em segundos) * @param timestamp Instante de execução (segundos virtuais).
* @param type tipo de evento * @param type Tipo enumerado do evento.
* @param payload dados associados (ex: objeto Vehicle) * @param payload Objeto de dados associado (pode ser null).
* @param location processo que trata o evento * @param location ID do processo alvo para execução distribuída.
*/ */
public SimulationEvent(double timestamp, DESEventType type, Object payload, String location) { public SimulationEvent(double timestamp, DESEventType type, Object payload, String location) {
this.timestamp = timestamp; this.timestamp = timestamp;
@@ -36,7 +51,14 @@ public class SimulationEvent implements Comparable<SimulationEvent>, Serializabl
this.location = location; this.location = location;
} }
/** Cria evento sem localização (para eventos locais) */ /**
* Construtor de conveniência para eventos locais (dentro do mesmo processo).
* Define {@code location} como null.
*
* @param timestamp Instante de execução.
* @param type Tipo do evento.
* @param payload Objeto de dados associado.
*/
public SimulationEvent(double timestamp, DESEventType type, Object payload) { public SimulationEvent(double timestamp, DESEventType type, Object payload) {
this(timestamp, type, payload, null); this(timestamp, type, payload, null);
} }
@@ -58,8 +80,18 @@ public class SimulationEvent implements Comparable<SimulationEvent>, Serializabl
} }
/** /**
* Ordena eventos por timestamp (mais cedo primeiro). * Define a ordem natural de processamento na Fila de Prioridade.
* Em caso de empate, ordena por tipo para determinismo. * <p>
* <b>Lógica de Ordenação:</b>
* <ol>
* <li><b>Primária (Tempo):</b> Eventos com menor timestamp ocorrem primeiro.</li>
* <li><b>Secundária (Determinismo):</b> Em caso de empate temporal (simultaneidade),
* ordena alfabeticamente pelo nome do tipo. Isto garante que execuções repetidas
* da simulação produzam exatamente o mesmo resultado (determinismo estrito).</li>
* </ol>
*
* @param other O outro evento a comparar.
* @return Inteiro negativo, zero ou positivo conforme a ordem.
*/ */
@Override @Override
public int compareTo(SimulationEvent other) { public int compareTo(SimulationEvent other) {
@@ -67,7 +99,7 @@ public class SimulationEvent implements Comparable<SimulationEvent>, Serializabl
if (timeComparison != 0) { if (timeComparison != 0) {
return timeComparison; return timeComparison;
} }
// Tie-breaker: order by event type name // Tie-breaker: order by event type name to ensure reproducible runs
return this.type.name().compareTo(other.type.name()); return this.type.name().compareTo(other.type.name());
} }

View File

@@ -3,28 +3,47 @@ package sd.des;
import sd.model.TrafficLight; import sd.model.TrafficLight;
/** /**
* Payload for traffic light change events. * Encapsula o contexto de dados para eventos de mudança de estado de semáforos.
* Contains the traffic light and its direction. * <p>
* Este objeto atua como o <i>payload</i> transportado por um {@link SimulationEvent}
* quando o tipo de evento é relacionado com controlo de tráfego (ex: mudança Verde -> Amarelo).
* Permite que o motor DES identifique exatamente qual instância de {@link TrafficLight}
* deve ser atualizada numa determinada interseção e direção.
*/ */
public class TrafficLightEvent { public class TrafficLightEvent {
private final TrafficLight light; private final TrafficLight light;
private final String direction; private final String direction;
private final String intersectionId; private final String intersectionId;
/**
* Cria um novo payload de evento de semáforo.
* @param light A instância do objeto semáforo a ser manipulado.
* @param direction A direção cardeal associada (ex: "North", "East").
* @param intersectionId O identificador da interseção onde o semáforo reside.
*/
public TrafficLightEvent(TrafficLight light, String direction, String intersectionId) { public TrafficLightEvent(TrafficLight light, String direction, String intersectionId) {
this.light = light; this.light = light;
this.direction = direction; this.direction = direction;
this.intersectionId = intersectionId; this.intersectionId = intersectionId;
} }
/**
* @return A referência direta para o objeto de domínio do semáforo.
*/
public TrafficLight getLight() { public TrafficLight getLight() {
return light; return light;
} }
/**
* @return A direção do fluxo controlado por este semáforo.
*/
public String getDirection() { public String getDirection() {
return direction; return direction;
} }
/**
* @return O ID da interseção pai.
*/
public String getIntersectionId() { public String getIntersectionId() {
return intersectionId; return intersectionId;
} }

View File

@@ -11,10 +11,19 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* Sistema de registo centralizado de eventos para a simulação distribuída. * Motor de logging assíncrono e thread-safe para a simulação distribuída.
* * <p>
* <p>Regista todos os eventos da simulação num ficheiro com timestamps e categorização. * Implementa o padrão <i>Singleton</i> para garantir um ponto centralizado de registo.
* Thread-safe e não-bloqueante para impacto mínimo na performance.</p> * Utiliza o padrão <i>Producer-Consumer</i> com uma {@link BlockingQueue} para desacoplar
* a geração de eventos (crítica para a performance da simulação) da persistência em disco
* (operação de I/O lenta).
* <p>
* <b>Garantias:</b>
* <ul>
* <li>Non-blocking writes (para a thread chamadora, na maioria dos casos).</li>
* <li>Ordering cronológico aproximado (FIFO na fila).</li>
* <li>Graceful Shutdown (flush de logs pendentes ao terminar).</li>
* </ul>
*/ */
public class EventLogger { public class EventLogger {
@@ -22,20 +31,33 @@ public class EventLogger {
private static final Object instanceLock = new Object(); private static final Object instanceLock = new Object();
private final PrintWriter writer; private final PrintWriter writer;
/** Buffer de memória para absorver picos de eventos (Burst traffic). */
private final BlockingQueue<LogEntry> logQueue; private final BlockingQueue<LogEntry> logQueue;
/** Thread dedicada (Consumer) para escrita em ficheiro. */
private final Thread writerThread; private final Thread writerThread;
private final AtomicBoolean running; private final AtomicBoolean running;
private final SimpleDateFormat timestampFormat; private final SimpleDateFormat timestampFormat;
private final long simulationStartMillis; private final long simulationStartMillis;
/** Construtor privado para padrão singleton */ /**
* Inicializa o sistema de logs.
* Abre o ficheiro, escreve o cabeçalho e inicia a thread consumidora.
*
* @param logFilePath Caminho relativo ou absoluto do ficheiro de log.
* @throws IOException Se não for possível criar ou escrever no ficheiro.
*/
private EventLogger(String logFilePath) throws IOException { private EventLogger(String logFilePath) throws IOException {
// Auto-flush ativado para garantir persistência, mas gerido pelo buffer do BufferedWriter
this.writer = new PrintWriter(new BufferedWriter(new FileWriter(logFilePath, false)), true); this.writer = new PrintWriter(new BufferedWriter(new FileWriter(logFilePath, false)), true);
this.logQueue = new LinkedBlockingQueue<>(10000); this.logQueue = new LinkedBlockingQueue<>(10000); // Backpressure: limita a 10k eventos pendentes
this.running = new AtomicBoolean(true); this.running = new AtomicBoolean(true);
this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
this.simulationStartMillis = System.currentTimeMillis(); this.simulationStartMillis = System.currentTimeMillis();
// Header inicial do log
writer.println("=".repeat(80)); writer.println("=".repeat(80));
writer.println("SIMULATION EVENT LOG"); writer.println("SIMULATION EVENT LOG");
writer.println("Started: " + timestampFormat.format(new Date())); writer.println("Started: " + timestampFormat.format(new Date()));
@@ -47,11 +69,16 @@ public class EventLogger {
writer.flush(); writer.flush();
this.writerThread = new Thread(this::processLogQueue, "EventLogger-Writer"); this.writerThread = new Thread(this::processLogQueue, "EventLogger-Writer");
this.writerThread.setDaemon(true); this.writerThread.setDaemon(true); // Permite que a JVM termine se apenas esta thread sobrar
this.writerThread.start(); this.writerThread.start();
} }
/** Obtém ou cria a instância singleton */ /**
* Obtém a instância única do logger (Lazy Initialization).
* Se não existir, cria uma predefinida em "logs/simulation-events.log".
*
* @return A instância singleton.
*/
public static EventLogger getInstance() { public static EventLogger getInstance() {
if (instance == null) { if (instance == null) {
synchronized (instanceLock) { synchronized (instanceLock) {
@@ -72,7 +99,8 @@ public class EventLogger {
} }
/** /**
* Initialize with custom log file path. * Reinicializa o logger com um ficheiro específico.
* Útil para testes ou configurações personalizadas.
*/ */
public static void initialize(String logFilePath) throws IOException { public static void initialize(String logFilePath) throws IOException {
synchronized (instanceLock) { synchronized (instanceLock) {
@@ -84,7 +112,13 @@ public class EventLogger {
} }
/** /**
* Logs an event (non-blocking). * Regista um evento genérico.
* Esta operação é não-bloqueante (retorna imediatamente após colocar na fila),
* exceto se a fila estiver cheia (backpressure).
*
* @param eventType Categoria do evento.
* @param component Nome do componente (ex: "Coordinator", "IntersectionProcess").
* @param description Detalhes do evento.
*/ */
public void log(EventType eventType, String component, String description) { public void log(EventType eventType, String component, String description) {
if (!running.get()) return; if (!running.get()) return;
@@ -96,7 +130,7 @@ public class EventLogger {
description description
); );
// Non-blocking offer - if queue is full, drop oldest // Non-blocking offer - if queue is full, drop oldest or warn
if (!logQueue.offer(entry)) { if (!logQueue.offer(entry)) {
// Queue full - this shouldn't happen with 10k buffer, but handle gracefully // Queue full - this shouldn't happen with 10k buffer, but handle gracefully
System.err.println("EventLogger queue full - dropping event: " + eventType); System.err.println("EventLogger queue full - dropping event: " + eventType);
@@ -104,14 +138,14 @@ public class EventLogger {
} }
/** /**
* Logs an event with vehicle context. * Regista um evento associado a um veículo específico (Helper method).
*/ */
public void logVehicle(EventType eventType, String component, String vehicleId, String description) { public void logVehicle(EventType eventType, String component, String vehicleId, String description) {
log(eventType, component, "[" + vehicleId + "] " + description); log(eventType, component, "[" + vehicleId + "] " + description);
} }
/** /**
* Logs an error event. * Regista um erro ou exceção com formatação apropriada.
*/ */
public void logError(String component, String description, Exception e) { public void logError(String component, String description, Exception e) {
String fullDescription = description + (e != null ? ": " + e.getMessage() : ""); String fullDescription = description + (e != null ? ": " + e.getMessage() : "");
@@ -119,11 +153,13 @@ public class EventLogger {
} }
/** /**
* Background thread that writes log entries to file. * Lógica da thread consumidora (Worker Thread).
* Retira eventos da fila e escreve no disco continuamente.
*/ */
private void processLogQueue() { private void processLogQueue() {
while (running.get() || !logQueue.isEmpty()) { while (running.get() || !logQueue.isEmpty()) {
try { try {
// Poll com timeout para permitir verificar a flag 'running' periodicamente
LogEntry entry = logQueue.poll(100, java.util.concurrent.TimeUnit.MILLISECONDS); LogEntry entry = logQueue.poll(100, java.util.concurrent.TimeUnit.MILLISECONDS);
if (entry != null) { if (entry != null) {
writeEntry(entry); writeEntry(entry);
@@ -134,7 +170,7 @@ public class EventLogger {
} }
} }
// Flush remaining entries // Flush final: garantir que eventos restantes na fila são escritos antes de morrer
while (!logQueue.isEmpty()) { while (!logQueue.isEmpty()) {
LogEntry entry = logQueue.poll(); LogEntry entry = logQueue.poll();
if (entry != null) { if (entry != null) {
@@ -144,7 +180,7 @@ public class EventLogger {
} }
/** /**
* Writes a single log entry to file. * Formata e escreve uma entrada de log no PrintWriter.
*/ */
private void writeEntry(LogEntry entry) { private void writeEntry(LogEntry entry) {
String timestamp = timestampFormat.format(new Date(entry.timestampMillis)); String timestamp = timestampFormat.format(new Date(entry.timestampMillis));
@@ -158,7 +194,7 @@ public class EventLogger {
entry.description entry.description
); );
// Flush periodically for real-time viewing // Flush periódico inteligente: se a carga for baixa, garante que vemos logs em tempo real
if (logQueue.size() < 10) { if (logQueue.size() < 10) {
writer.flush(); writer.flush();
} }
@@ -170,15 +206,17 @@ public class EventLogger {
} }
/** /**
* Shuts down the logger and flushes all pending entries. * Encerra o logger de forma segura.
* Desativa a aceitação de novos eventos, aguarda que a fila esvazie (flush)
* e fecha o ficheiro.
*/ */
public void shutdown() { public void shutdown() {
if (!running.compareAndSet(true, false)) { if (!running.compareAndSet(true, false)) {
return; // Already shut down return; // Já encerrado
} }
try { try {
// Wait for writer thread to finish // Wait for writer thread to finish flushing
writerThread.join(5000); // Wait up to 5 seconds writerThread.join(5000); // Wait up to 5 seconds
// Write footer // Write footer
@@ -195,7 +233,7 @@ public class EventLogger {
} }
/** /**
* Internal class to represent a log entry. * DTO interno imutável para armazenar dados do evento na fila.
*/ */
private static class LogEntry { private static class LogEntry {
final long timestampMillis; final long timestampMillis;

View File

@@ -1,34 +1,46 @@
package sd.logging; package sd.logging;
/** /**
* Tipos de eventos que podem ocorrer na simulação. * Taxonomia oficial de eventos para o subsistema de logging centralizado.
* Usados para categorizar e filtrar logs. * <p>
* Este enumerado padroniza a categorização de todas as ocorrências na simulação, permitindo:
* <ul>
* <li>Filtragem granular de logs (ex: ver apenas erros ou apenas tráfego de rede).</li>
* <li>Análise estatística post-mortem (parsear logs para calcular latências).</li>
* <li>Correlação de eventos distribuídos (seguir o rastro de um veículo através de vários nós).</li>
* </ul>
*/ */
public enum EventType { public enum EventType {
// --- Ciclo de Vida do Veículo ---
VEHICLE_GENERATED("Vehicle Generated"), VEHICLE_GENERATED("Vehicle Generated"),
VEHICLE_ARRIVED("Vehicle Arrived"), VEHICLE_ARRIVED("Vehicle Arrived"),
VEHICLE_QUEUED("Vehicle Queued"), VEHICLE_QUEUED("Vehicle Queued"),
VEHICLE_DEPARTED("Vehicle Departed"), VEHICLE_DEPARTED("Vehicle Departed"),
VEHICLE_EXITED("Vehicle Exited"), VEHICLE_EXITED("Vehicle Exited"),
// --- Controlo de Semáforos e Exclusão Mútua ---
LIGHT_CHANGED_GREEN("Light Changed to Green"), LIGHT_CHANGED_GREEN("Light Changed to Green"),
LIGHT_CHANGED_RED("Light Changed to Red"), LIGHT_CHANGED_RED("Light Changed to Red"),
LIGHT_REQUEST_GREEN("Light Requested Green"), LIGHT_REQUEST_GREEN("Light Requested Green"),
LIGHT_RELEASE_GREEN("Light Released Green"), LIGHT_RELEASE_GREEN("Light Released Green"),
// --- Ciclo de Vida da Simulação/Processos ---
SIMULATION_STARTED("Simulation Started"), SIMULATION_STARTED("Simulation Started"),
SIMULATION_STOPPED("Simulation Stopped"), SIMULATION_STOPPED("Simulation Stopped"),
PROCESS_STARTED("Process Started"), PROCESS_STARTED("Process Started"),
PROCESS_STOPPED("Process Stopped"), PROCESS_STOPPED("Process Stopped"),
// --- Configuração e Telemetria ---
STATS_UPDATE("Statistics Update"), STATS_UPDATE("Statistics Update"),
CONFIG_CHANGED("Configuration Changed"), CONFIG_CHANGED("Configuration Changed"),
// --- Camada de Rede (TCP/Sockets) ---
CONNECTION_ESTABLISHED("Connection Established"), CONNECTION_ESTABLISHED("Connection Established"),
CONNECTION_LOST("Connection Lost"), CONNECTION_LOST("Connection Lost"),
MESSAGE_SENT("Message Sent"), MESSAGE_SENT("Message Sent"),
MESSAGE_RECEIVED("Message Received"), MESSAGE_RECEIVED("Message Received"),
// --- Tratamento de Exceções ---
ERROR("Error"); ERROR("Error");
private final String displayName; private final String displayName;

View File

@@ -12,15 +12,18 @@ import java.util.concurrent.ConcurrentHashMap;
import sd.model.Vehicle; import sd.model.Vehicle;
/** /**
* Rastreia e regista a viagem completa de veículos individuais. * Subsistema de auditoria granular responsável pelo rastreio detalhado (Tracing) de veículos individuais.
* * <p>
* <p>Cria ficheiros de trace detalhados com: * Diferente do {@link EventLogger} (que regista eventos globais do sistema), esta classe foca-se
* na perspetiva do <b>agente</b>. Cria um ficheiro de rastro dedicado (`.trace`) para cada veículo
* monitorizado, registando cronologicamente cada interação com a infraestrutura (interseções,
* filas, semáforos).
* <p>
* <b>Funcionalidades:</b>
* <ul> * <ul>
* <li>Timestamps de todos os eventos * <li>Análise forense de percursos individuais.</li>
* <li>Localizões (interseções) * <li>Validão de tempos de espera e travessia por nó.</li>
* <li>Tempos de espera em cada semáforo * <li>Cálculo de eficiência de rota (tempo em movimento vs. tempo parado).</li>
* <li>Tempos de travessia
* <li>Tempo total no sistema
* </ul> * </ul>
*/ */
public class VehicleTracer { public class VehicleTracer {
@@ -28,12 +31,18 @@ public class VehicleTracer {
private static VehicleTracer instance; private static VehicleTracer instance;
private static final Object instanceLock = new Object(); private static final Object instanceLock = new Object();
/** Mapa thread-safe de sessões de trace ativas (VehicleID -> TraceHandler). */
private final Map<String, VehicleTrace> trackedVehicles; private final Map<String, VehicleTrace> trackedVehicles;
private final SimpleDateFormat timestampFormat; private final SimpleDateFormat timestampFormat;
private final long simulationStartMillis; private final long simulationStartMillis;
private final String traceDirectory; private final String traceDirectory;
/** Construtor privado (singleton) */ /**
* Inicializa o tracer e prepara o diretório de saída.
*
* @param traceDirectory Caminho para armazenamento dos ficheiros .trace.
*/
private VehicleTracer(String traceDirectory) { private VehicleTracer(String traceDirectory) {
this.trackedVehicles = new ConcurrentHashMap<>(); this.trackedVehicles = new ConcurrentHashMap<>();
this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
@@ -47,7 +56,10 @@ public class VehicleTracer {
} }
} }
/** Obtém ou cria a instância singleton */ /**
* Obtém a instância única do tracer (Singleton).
* @return A instância global.
*/
public static VehicleTracer getInstance() { public static VehicleTracer getInstance() {
if (instance == null) { if (instance == null) {
synchronized (instanceLock) { synchronized (instanceLock) {
@@ -59,7 +71,10 @@ public class VehicleTracer {
return instance; return instance;
} }
/** Inicializa com diretório de trace customizado */ /**
* Reinicializa o tracer com um diretório personalizado.
* Útil para isolar logs de diferentes execuções em lote.
*/
public static void initialize(String traceDirectory) { public static void initialize(String traceDirectory) {
synchronized (instanceLock) { synchronized (instanceLock) {
if (instance != null) { if (instance != null) {
@@ -70,12 +85,14 @@ public class VehicleTracer {
} }
/** /**
* Começa a rastrear um veículo específico. * Inicia a sessão de rastreio para um veículo específico.
* Cria ficheiro de trace para este veículo. * Cria o ficheiro {@code logs/traces/vehicle-{id}.trace} e escreve o cabeçalho.
*
* @param vehicleId O identificador único do veículo.
*/ */
public void startTracking(String vehicleId) { public void startTracking(String vehicleId) {
if (trackedVehicles.containsKey(vehicleId)) { if (trackedVehicles.containsKey(vehicleId)) {
return; // Already tracking return; // Já está a ser rastreado
} }
VehicleTrace trace = new VehicleTrace(vehicleId, traceDirectory); VehicleTrace trace = new VehicleTrace(vehicleId, traceDirectory);
@@ -85,7 +102,7 @@ public class VehicleTracer {
} }
/** /**
* Stops tracking a vehicle and closes its trace file. * Encerra a sessão de rastreio, fecha o descritor de ficheiro e remove da memória.
*/ */
public void stopTracking(String vehicleId) { public void stopTracking(String vehicleId) {
VehicleTrace trace = trackedVehicles.remove(vehicleId); VehicleTrace trace = trackedVehicles.remove(vehicleId);
@@ -96,17 +113,18 @@ public class VehicleTracer {
} }
/** /**
* Checks if a vehicle is being tracked. * Verifica se um veículo está atualmente sob auditoria.
*/ */
public boolean isTracking(String vehicleId) { public boolean isTracking(String vehicleId) {
return trackedVehicles.containsKey(vehicleId); return trackedVehicles.containsKey(vehicleId);
} }
/** /**
* Logs when a vehicle is generated. * Regista o evento de instanciação do veículo pelo Coordenador.
*/ */
public void logGenerated(Vehicle vehicle) { public void logGenerated(Vehicle vehicle) {
if (!isTracking(vehicle.getId())) return; if (!isTracking(vehicle.getId()))
return;
VehicleTrace trace = trackedVehicles.get(vehicle.getId()); VehicleTrace trace = trackedVehicles.get(vehicle.getId());
if (trace != null) { if (trace != null) {
@@ -117,10 +135,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle arrives at an intersection. * Regista a chegada física do veículo à zona de deteção de uma interseção.
*/ */
public void logArrival(String vehicleId, String intersection, double simulationTime) { public void logArrival(String vehicleId, String intersection, double simulationTime) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -130,10 +149,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle is queued at a traffic light. * Regista a entrada do veículo na estrutura de fila de um semáforo.
*/ */
public void logQueued(String vehicleId, String intersection, String direction, int queuePosition) { public void logQueued(String vehicleId, String intersection, String direction, int queuePosition) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -143,10 +163,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle starts waiting at a red light. * Regista o início da espera ativa (veículo parado no Vermelho).
*/ */
public void logWaitingStart(String vehicleId, String intersection, String direction) { public void logWaitingStart(String vehicleId, String intersection, String direction) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -156,10 +177,12 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle finishes waiting (light turns green). * Regista o fim da espera (Sinal Verde).
* @param waitTime Duração total da paragem nesta instância.
*/ */
public void logWaitingEnd(String vehicleId, String intersection, String direction, double waitTime) { public void logWaitingEnd(String vehicleId, String intersection, String direction, double waitTime) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -169,10 +192,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle starts crossing an intersection. * Regista o início da travessia da interseção (ocupação da zona crítica).
*/ */
public void logCrossingStart(String vehicleId, String intersection, String direction) { public void logCrossingStart(String vehicleId, String intersection, String direction) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -182,10 +206,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle finishes crossing an intersection. * Regista a libertação da zona crítica da interseção.
*/ */
public void logCrossingEnd(String vehicleId, String intersection, double crossingTime) { public void logCrossingEnd(String vehicleId, String intersection, double crossingTime) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -195,10 +220,11 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle departs from an intersection. * Regista a partida da interseção em direção ao próximo nó.
*/ */
public void logDeparture(String vehicleId, String intersection, String nextDestination) { public void logDeparture(String vehicleId, String intersection, String nextDestination) {
if (!isTracking(vehicleId)) return; if (!isTracking(vehicleId))
return;
VehicleTrace trace = trackedVehicles.get(vehicleId); VehicleTrace trace = trackedVehicles.get(vehicleId);
if (trace != null) { if (trace != null) {
@@ -208,10 +234,14 @@ public class VehicleTracer {
} }
/** /**
* Logs when a vehicle exits the system. * Regista a saída do sistema (no Exit Node).
* <p>
* Este método também desencadeia a escrita do <b>Sumário de Viagem</b> no final do log
* e fecha o ficheiro automaticamente.
*/ */
public void logExit(Vehicle vehicle, double systemTime) { public void logExit(Vehicle vehicle, double systemTime) {
if (!isTracking(vehicle.getId())) return; if (!isTracking(vehicle.getId()))
return;
VehicleTrace trace = trackedVehicles.get(vehicle.getId()); VehicleTrace trace = trackedVehicles.get(vehicle.getId());
if (trace != null) { if (trace != null) {
@@ -219,7 +249,7 @@ public class VehicleTracer {
String.format("Exited system - Total time: %.2fs, Waiting: %.2fs, Crossing: %.2fs", String.format("Exited system - Total time: %.2fs, Waiting: %.2fs, Crossing: %.2fs",
systemTime, vehicle.getTotalWaitingTime(), vehicle.getTotalCrossingTime())); systemTime, vehicle.getTotalWaitingTime(), vehicle.getTotalCrossingTime()));
// Write summary // Escreve estatísticas sumarizadas
trace.writeSummary(vehicle, systemTime); trace.writeSummary(vehicle, systemTime);
// Stop tracking and close file // Stop tracking and close file
@@ -228,7 +258,8 @@ public class VehicleTracer {
} }
/** /**
* Shuts down the tracer and closes all trace files. * Fecha forçosamente todos os traces abertos.
* Deve ser chamado no shutdown da simulação para evitar corrupção de logs.
*/ */
public void shutdown() { public void shutdown() {
for (VehicleTrace trace : trackedVehicles.values()) { for (VehicleTrace trace : trackedVehicles.values()) {
@@ -238,7 +269,7 @@ public class VehicleTracer {
} }
/** /**
* Internal class to handle tracing for a single vehicle. * Classe interna auxiliar que gere o descritor de ficheiro e a formatação para um único veículo.
*/ */
private class VehicleTrace { private class VehicleTrace {
private final String vehicleId; private final String vehicleId;
@@ -272,7 +303,8 @@ public class VehicleTracer {
} }
void logEvent(String eventType, String location, String description) { void logEvent(String eventType, String location, String description) {
if (writer == null) return; if (writer == null)
return;
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
String timestamp = timestampFormat.format(new Date(now)); String timestamp = timestampFormat.format(new Date(now));
@@ -283,13 +315,13 @@ public class VehicleTracer {
relativeTime, relativeTime,
truncate(eventType, 15), truncate(eventType, 15),
truncate(location, 15), truncate(location, 15),
description description);
);
writer.flush(); writer.flush();
} }
void writeSummary(Vehicle vehicle, double systemTime) { void writeSummary(Vehicle vehicle, double systemTime) {
if (writer == null) return; if (writer == null)
return;
writer.println(); writer.println();
writer.println("=".repeat(80)); writer.println("=".repeat(80));
@@ -324,7 +356,8 @@ public class VehicleTracer {
} }
private String truncate(String str, int maxLength) { private String truncate(String str, int maxLength) {
if (str == null) return ""; if (str == null)
return "";
return str.length() <= maxLength ? str : str.substring(0, maxLength); return str.length() <= maxLength ? str : str.substring(0, maxLength);
} }
} }

View File

@@ -95,9 +95,6 @@ public class Intersection {
public void receiveVehicle(Vehicle vehicle, double simulationTime) { public void receiveVehicle(Vehicle vehicle, double simulationTime) {
totalVehiclesReceived++; totalVehiclesReceived++;
// Note: Route advancement is handled by SimulationEngine.handleVehicleArrival()
// before calling this method, so we don't advance here.
String nextDestination = vehicle.getCurrentDestination(); String nextDestination = vehicle.getCurrentDestination();
// Check if vehicle reached final destination // Check if vehicle reached final destination

View File

@@ -5,41 +5,52 @@ import java.util.UUID;
import sd.protocol.MessageProtocol; import sd.protocol.MessageProtocol;
/** /**
* Representa uma mensagem trocada entre processos na simulação distribuída. * Envelope fundamental do protocolo de comunicação entre processos distribuídos (IPC).
* * <p>
* <p>Cada mensagem tem um ID único, tipo, remetente, destino e payload. * Esta classe atua como a Unidade de Dados de Aplicação (ADU), encapsulando tanto
* Implementa {@link MessageProtocol} que estende Serializable para transmissão pela rede.</p> * os metadados de roteamento (origem, destino, tipo) quanto a carga útil (payload)
* polimórfica. É agnóstica ao conteúdo, servindo como contentor genérico para
* transferência de estado (Veículos, Estatísticas) ou sinais de controlo (Semáforos).
* <p>
* A imutabilidade dos campos (exceto via serialização) garante a integridade da mensagem
* durante o trânsito na rede.
*/ */
public class Message implements MessageProtocol { public class Message implements MessageProtocol {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** Identificador único desta mensagem */ /** * Identificador único universal (UUID).
* Essencial para rastreabilidade (tracing), logs de auditoria e mecanismos de deduplicação.
*/
private final String messageId; private final String messageId;
/** Tipo desta mensagem (ex: VEHICLE_TRANSFER, STATS_UPDATE) */ /** Discriminador semântico que define como o recetor deve processar o payload. */
private final MessageType type; private final MessageType type;
/** Identificador do processo que enviou esta mensagem */ /** Identificador lógico do nó emissor (ex: "Cr1", "Coordinator"). */
private final String senderId; private final String senderId;
/** Identificador do processo de destino (pode ser null para broadcast) */ /** * Identificador lógico do nó recetor.
* Se {@code null}, a mensagem deve ser tratada como <b>Broadcast</b>.
*/
private final String destinationId; private final String destinationId;
/** Dados a serem transmitidos (o tipo depende do tipo de mensagem) */ /** * Carga útil polimórfica.
* Deve implementar {@link java.io.Serializable} para garantir transmissão correta.
*/
private final Object payload; private final Object payload;
/** Timestamp de criação da mensagem (tempo de simulação ou real) */ /** Marca temporal da criação da mensagem (Unix Timestamp), usada para cálculo de latência de rede. */
private final long timestamp; private final long timestamp;
/** /**
* Cria uma nova mensagem com todos os parâmetros. * Construtor completo para reconstrução de mensagens ou envio com timestamp manual.
* *
* @param type tipo da mensagem * @param type Classificação semântica da mensagem.
* @param senderId ID do processo remetente * @param senderId ID do processo origem.
* @param destinationId ID do processo de destino (null para broadcast) * @param destinationId ID do processo destino (ou null para broadcast).
* @param payload conteúdo da mensagem * @param payload Objeto de domínio a ser transportado.
* @param timestamp timestamp de criação da mensagem * @param timestamp Instante de criação (ms).
*/ */
public Message(MessageType type, String senderId, String destinationId, public Message(MessageType type, String senderId, String destinationId,
Object payload, long timestamp) { Object payload, long timestamp) {
@@ -52,23 +63,24 @@ public class Message implements MessageProtocol {
} }
/** /**
* Cria uma nova mensagem usando o tempo atual do sistema como timestamp. * Construtor de conveniência que atribui automaticamente o timestamp atual do sistema.
* *
* @param type tipo da mensagem * @param type Classificação semântica.
* @param senderId ID do processo remetente * @param senderId ID do processo origem.
* @param destinationId ID do processo de destino * @param destinationId ID do processo destino.
* @param payload conteúdo da mensagem * @param payload Objeto de domínio.
*/ */
public Message(MessageType type, String senderId, String destinationId, Object payload) { public Message(MessageType type, String senderId, String destinationId, Object payload) {
this(type, senderId, destinationId, payload, System.currentTimeMillis()); this(type, senderId, destinationId, payload, System.currentTimeMillis());
} }
/** /**
* Cria uma mensagem de broadcast (sem destino específico). * Construtor de conveniência para mensagens de difusão (Broadcast).
* Define {@code destinationId} como null.
* *
* @param type tipo da mensagem * @param type Classificação semântica.
* @param senderId ID do processo remetente * @param senderId ID do processo origem.
* @param payload conteúdo da mensagem * @param payload Objeto de domínio.
*/ */
public Message(MessageType type, String senderId, Object payload) { public Message(MessageType type, String senderId, Object payload) {
this(type, senderId, null, payload, System.currentTimeMillis()); this(type, senderId, null, payload, System.currentTimeMillis());
@@ -101,21 +113,23 @@ public class Message implements MessageProtocol {
} }
/** /**
* Checks if this is a broadcast message (no specific destination). * Verifica se a mensagem se destina a todos os nós da rede.
* *
* @return true if destinationId is null, false otherwise * @return {@code true} se o destinationId for nulo.
*/ */
public boolean isBroadcast() { public boolean isBroadcast() {
return destinationId == null; return destinationId == null;
} }
/** /**
* Gets the payload cast to a specific type. * Utilitário para casting seguro e fluente do payload.
* Use with caution and ensure type safety. * <p>
* Evita a necessidade de casts explícitos e supressão de warnings no código cliente.
* *
* @param <T> The expected payload type * @param <T> O tipo esperado do payload.
* @return The payload cast to type T * @param clazz A classe do tipo esperado para verificação em runtime (opcional no uso, mas boa prática).
* @throws ClassCastException if the payload is not of type T * @return O payload convertido para o tipo T.
* @throws ClassCastException Se o payload não for compatível com o tipo solicitado.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> T getPayloadAs(Class<T> clazz) { public <T> T getPayloadAs(Class<T> clazz) {

View File

@@ -7,16 +7,20 @@ import java.util.List;
/** /**
* Representa um veículo que se move pela rede de interseções. * Representa um veículo que se move pela rede de interseções.
* *
* <p>Esta classe é o "gémeo digital" de um carro, mota ou camião. * <p>
* Mantém toda a informação necessária:</p> * Esta classe é o "gémeo digital" de um carro, mota ou camião.
* Mantém toda a informação necessária:
* </p>
* <ul> * <ul>
* <li>Identificação e tipo do veículo</li> * <li>Identificação e tipo do veículo</li>
* <li>Rota completa a percorrer</li> * <li>Rota completa a percorrer</li>
* <li>Métricas de tempo (espera, travessia, total)</li> * <li>Métricas de tempo (espera, travessia, total)</li>
* </ul> * </ul>
* *
* <p>O objeto é serializado e enviado pela rede à medida que o veículo * <p>
* se move entre processos distribuídos.</p> * O objeto é serializado e enviado pela rede à medida que o veículo
* se move entre processos distribuídos.
* </p>
*/ */
public class Vehicle implements Serializable { public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@@ -42,10 +46,16 @@ public class Vehicle implements Serializable {
*/ */
private int currentRouteIndex; private int currentRouteIndex;
/** Tempo total acumulado (segundos) que o veículo passou à espera em semáforos vermelhos */ /**
* Tempo total acumulado (segundos) que o veículo passou à espera em semáforos
* vermelhos
*/
private double totalWaitingTime; private double totalWaitingTime;
/** Tempo total acumulado (segundos) que o veículo passou a atravessar interseções */ /**
* Tempo total acumulado (segundos) que o veículo passou a atravessar
* interseções
*/
private double totalCrossingTime; private double totalCrossingTime;
/** /**
@@ -80,7 +90,8 @@ public class Vehicle implements Serializable {
} }
/** /**
* Obtém o destino atual (próxima interseção ou saída) para onde o veículo se dirige. * Obtém o destino atual (próxima interseção ou saída) para onde o veículo se
* dirige.
* *
* @return ID do destino atual (ex: "Cr1"), ou {@code null} se a rota terminou * @return ID do destino atual (ex: "Cr1"), ou {@code null} se a rota terminou
*/ */

View File

@@ -2,7 +2,7 @@ package sd.protocol;
import java.io.Serializable; import java.io.Serializable;
import sd.model.MessageType; // Assuming MessageType is in sd.model or sd.protocol import sd.model.MessageType;
/** /**
* Contrato para todas as mensagens trocadas no simulador. * Contrato para todas as mensagens trocadas no simulador.

View File

@@ -16,32 +16,64 @@ import sd.serialization.MessageSerializer;
import sd.serialization.SerializationException; import sd.serialization.SerializationException;
import sd.serialization.SerializerFactory; import sd.serialization.SerializerFactory;
/** /**
* Simplifica comunicação via sockets. * Wrapper de alto nível para gestão robusta de conexões TCP.
* Inclui lógica de retry para robustez. * <p>
* Esta classe abstrai a complexidade da API nativa {@link java.net.Socket}, oferecendo:
* <ol>
* <li><b>Resiliência:</b> Lógica de reconexão automática (Retry Loop) no arranque, crucial para sistemas
* distribuídos onde a ordem de inicialização dos nós não é garantida.</li>
* <li><b>Framing:</b> Implementação transparente do protocolo "Length-Prefix" (4 bytes de tamanho + payload),
* resolvendo o problema de fragmentação de stream TCP.</li>
* <li><b>Serialização:</b> Integração direta com a camada de serialização JSON.</li>
* </ol>
*/ */
public class SocketConnection implements Closeable { public class SocketConnection implements Closeable {
// --- Network Resources ---
/**
* The underlying TCP socket used for network communication.
*/
private final Socket socket; private final Socket socket;
/**
* The raw output stream for writing bytes to the network.
* Wrapped by {@link DataOutputStream} during message sending.
*/
private final OutputStream outputStream; private final OutputStream outputStream;
/**
* The raw input stream for reading bytes from the network.
* Wrapped by {@link DataInputStream} during message reception.
*/
private final InputStream inputStream; private final InputStream inputStream;
// --- Serialization ---
/**
* The serializer strategy used to convert objects to/from byte arrays (e.g., JSON).
*/
private final MessageSerializer serializer; private final MessageSerializer serializer;
/** Número máximo de tentativas de ligação */ /** Número máximo de tentativas de ligação antes de desistir (Fail-fast). */
private static final int MAX_RETRIES = 5; private static final int MAX_RETRIES = 5;
/** Atraso entre tentativas (milissegundos) */
/** Janela de espera (backoff) linear entre tentativas (em milissegundos). */
private static final long RETRY_DELAY_MS = 1000; private static final long RETRY_DELAY_MS = 1000;
/** /**
* Construtor do cliente que inicia a ligação. * Construtor para clientes (Active Open).
* Tenta ligar a um servidor já em escuta, com retry. * Tenta estabelecer uma conexão TCP com um servidor, aplicando lógica de retry.
* <p>
* Este comportamento é vital quando o processo Coordenador inicia antes das Interseções estarem
* prontas para aceitar conexões ({@code accept()}).
* *
* @param host endereço do host (ex: "localhost") * @param host Endereço do nó de destino (ex: "localhost").
* @param port número da porta * @param port Porta de serviço.
* @throws IOException se falhar após todas as tentativas * @throws IOException Se a conexão falhar após todas as {@code MAX_RETRIES} tentativas.
* @throws UnknownHostException se o host não for encontrado * @throws UnknownHostException Se o DNS não resolver o hostname.
* @throws InterruptedException se a thread for interrompida * @throws InterruptedException Se a thread for interrompida durante o sleep de retry.
*/ */
public SocketConnection(String host, int port) throws IOException, UnknownHostException, InterruptedException { public SocketConnection(String host, int port) throws IOException, UnknownHostException, InterruptedException {
Socket tempSocket = null; Socket tempSocket = null;
@@ -52,7 +84,7 @@ public class SocketConnection implements Closeable {
// --- Retry Loop --- // --- Retry Loop ---
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) { for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try { try {
// Try to establish the connection // Try to establish the connection (SYN -> SYN-ACK -> ACK)
tempSocket = new Socket(host, port); tempSocket = new Socket(host, port);
// If successful, break out of the retry loop // If successful, break out of the retry loop
@@ -61,17 +93,17 @@ public class SocketConnection implements Closeable {
break; break;
} catch (ConnectException | SocketTimeoutException e) { } catch (ConnectException | SocketTimeoutException e) {
// These are common errors indicating the server might not be ready. // Common errors: "Connection refused" (server not up) or "Timeout" (firewall/network)
lastException = e; lastException = e;
System.out.printf("[SocketConnection] Attempt %d/%d failed: %s. Retrying in %d ms...%n", System.out.printf("[SocketConnection] Attempt %d/%d failed: %s. Retrying in %d ms...%n",
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS); attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
if (attempt < MAX_RETRIES) { if (attempt < MAX_RETRIES) {
// Wait before the next attempt // Blocking wait before next attempt
TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS); TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
} }
} catch (IOException e) { } catch (IOException e) {
// Other IOExceptions might be more permanent, but we retry anyway. // Other IO errors
lastException = e; lastException = e;
System.out.printf("[SocketConnection] Attempt %d/%d failed with IOException: %s. Retrying in %d ms...%n", System.out.printf("[SocketConnection] Attempt %d/%d failed with IOException: %s. Retrying in %d ms...%n",
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS); attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
@@ -81,48 +113,46 @@ public class SocketConnection implements Closeable {
} }
} // --- End of Retry Loop --- } // --- End of Retry Loop ---
// If after all retries tempSocket is still null, it means connection failed permanently. // Final validation
if (tempSocket == null) { if (tempSocket == null) {
System.err.printf("[SocketConnection] Failed to connect to %s:%d after %d attempts.%n", host, port, MAX_RETRIES); System.err.printf("[SocketConnection] Failed to connect to %s:%d after %d attempts.%n", host, port, MAX_RETRIES);
if (lastException != null) { if (lastException != null) {
throw lastException; // Throw the last exception encountered throw lastException; // Propagate the root cause
} else { } else {
// Should not happen if loop ran, but as a fallback
throw new IOException("Failed to connect after " + MAX_RETRIES + " attempts, reason unknown."); throw new IOException("Failed to connect after " + MAX_RETRIES + " attempts, reason unknown.");
} }
} }
// If connection was successful, assign to final variable and create streams // Initialize streams
this.socket = tempSocket; this.socket = tempSocket;
this.outputStream = socket.getOutputStream(); this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream(); this.inputStream = socket.getInputStream();
this.serializer = SerializerFactory.createDefault(); this.serializer = SerializerFactory.createDefault();
} }
/** /**
* Constructor for the "Server" (who accepts the connection). * Construtor para servidores (Passive Open).
* Receives a Socket that has already been accepted by a ServerSocket. * Envolve um socket já conectado (retornado por {@code serverSocket.accept()}).
* No retry logic needed here as the connection is already established. * Não necessita de retry logic pois a conexão física já existe.
* *
* @param acceptedSocket The Socket returned by serverSocket.accept(). * @param acceptedSocket O socket ativo retornado pelo SO.
* @throws IOException If stream creation fails. * @throws IOException Se falhar a obtenção dos streams de I/O.
*/ */
public SocketConnection(Socket acceptedSocket) throws IOException { public SocketConnection(Socket acceptedSocket) throws IOException {
this.socket = acceptedSocket; this.socket = acceptedSocket;
this.outputStream = socket.getOutputStream(); this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream(); this.inputStream = socket.getInputStream();
this.serializer = SerializerFactory.createDefault(); this.serializer = SerializerFactory.createDefault();
} }
/** /**
* Sends (serializes) a MessageProtocol object over the socket. * Serializa e transmite uma mensagem através do canal.
* <p>
* Utiliza sincronização ({@code synchronized}) para garantir que escritas concorrentes
* na mesma conexão não corrompem a stream de bytes (thread-safety).
* *
* @param message The "envelope" (which contains the Vehicle) to be sent. * @param message O objeto de protocolo a enviar.
* @throws IOException If writing to the stream fails or socket is not connected. * @throws IOException Se o socket estiver fechado ou ocorrer erro de escrita.
*/ */
public synchronized void sendMessage(MessageProtocol message) throws IOException { public synchronized void sendMessage(MessageProtocol message) throws IOException {
if (socket == null || !socket.isConnected()) { if (socket == null || !socket.isConnected()) {
@@ -133,11 +163,11 @@ public class SocketConnection implements Closeable {
// Serializa para bytes JSON // Serializa para bytes JSON
byte[] data = serializer.serialize(message); byte[] data = serializer.serialize(message);
// Write 4-byte length prefix // Write 4-byte length prefix (Framing)
DataOutputStream dataOut = new DataOutputStream(outputStream); DataOutputStream dataOut = new DataOutputStream(outputStream);
dataOut.writeInt(data.length); dataOut.writeInt(data.length);
dataOut.write(data); dataOut.write(data);
dataOut.flush(); dataOut.flush(); // Force transmission immediately
} catch (SerializationException e) { } catch (SerializationException e) {
throw new IOException("Failed to serialize message", e); throw new IOException("Failed to serialize message", e);
@@ -145,11 +175,14 @@ public class SocketConnection implements Closeable {
} }
/** /**
* Tries to read (deserialize) a MessageProtocol object from the socket. * Bloqueia à espera de uma mensagem completa do socket.
* <p>
* Lê primeiro o cabeçalho de tamanho (4 bytes) e depois o payload exato,
* garantindo que processa mensagens completas mesmo se chegarem fragmentadas em múltiplos pacotes TCP.
* *
* @return The "envelope" (MessageProtocol) that was received. * @return O objeto {@link MessageProtocol} reconstruído.
* @throws IOException If the connection is lost, the stream is corrupted, or socket is not connected. * @throws IOException Se a conexão for perdida (EOF) ou o stream corrompido.
* @throws ClassNotFoundException If the received object is unknown. * @throws ClassNotFoundException Se o tipo desserializado não for encontrado no classpath.
*/ */
public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException { public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException {
if (socket == null || !socket.isConnected()) { if (socket == null || !socket.isConnected()) {
@@ -157,19 +190,20 @@ public class SocketConnection implements Closeable {
} }
try { try {
// Lê um prefixo de 4 bytes - indicador de tamanho
DataInputStream dataIn = new DataInputStream(inputStream); DataInputStream dataIn = new DataInputStream(inputStream);
int length = dataIn.readInt(); int length = dataIn.readInt();
if (length <= 0 || length > 10_000_000) { // Sanity check (10MB max) // Sanity check para evitar OutOfMemory em caso de corrupção de stream
if (length <= 0 || length > 10_000_000) { // Max 10MB payload
throw new IOException("Invalid message length: " + length); throw new IOException("Invalid message length: " + length);
} }
// Ler dados da mensagem // Ler dados exatos da mensagem
byte[] data = new byte[length]; byte[] data = new byte[length];
dataIn.readFully(data); dataIn.readFully(data);
// Deserialize do JSON - use concrete Message class, not interface // Deserialize do JSON - força o tipo concreto Message
return serializer.deserialize(data, sd.model.Message.class); return serializer.deserialize(data, sd.model.Message.class);
} catch (SerializationException e) { } catch (SerializationException e) {
@@ -178,7 +212,8 @@ public class SocketConnection implements Closeable {
} }
/** /**
* Closes the socket and all streams (Input and Output). * Encerra a conexão e liberta os descritores de ficheiro.
* Operação idempotente.
*/ */
@Override @Override
public void close() throws IOException { public void close() throws IOException {
@@ -188,7 +223,8 @@ public class SocketConnection implements Closeable {
} }
/** /**
* @return true if the socket is still connected and not closed. * Verifica o estado operacional da conexão.
* @return true se o socket está aberto e conectado.
*/ */
public boolean isConnected() { public boolean isConnected() {
return socket != null && socket.isConnected() && !socket.isClosed(); return socket != null && socket.isConnected() && !socket.isClosed();

View File

@@ -1,26 +1,25 @@
package sd.serialization; package sd.serialization;
import java.nio.charset.StandardCharsets;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import java.nio.charset.StandardCharsets;
/** /**
* JSON-based implementation of {@link MessageSerializer} using Google's Gson library. * Implementação baseada em JSON da estratégia {@link MessageSerializer}, utilizando a biblioteca Gson.
* * <p>
* This serializer converts objects to JSON format for transmission, providing: * Este serializador converte objetos Java para o formato de texto JSON antes da transmissão.
* - Human-readable message format (easy debugging) * Oferece várias vantagens técnicas sobre a serialização nativa do Java:
* - Cross-platform compatibility * <ul>
* - Smaller message sizes compared to Java native serialization * <li><b>Legibilidade:</b> O formato de texto facilita a depuração (sniffing de rede) sem ferramentas especializadas.</li>
* - Better security (no code execution during deserialization) * <li><b>Interoperabilidade:</b> Permite futura integração com componentes não-Java (ex: Dashboards web em JS).</li>
* * <li><b>Segurança:</b> Reduz a superfície de ataque para execução remota de código (RCE), pois não desserializa classes arbitrárias, apenas dados.</li>
* The serializer is configured with pretty printing disabled by default for * </ul>
* production use, but can be enabled for debugging purposes. * <p>
* * <b>Thread-Safety:</b> A instância interna do {@code Gson} é imutável e thread-safe, permitindo
* Thread-safety: This class is thread-safe as Gson instances are thread-safe. * que este serializador seja partilhado entre múltiplas threads (ex: no pool do DashboardServer).
* * * @see MessageSerializer
* @see MessageSerializer
*/ */
public class JsonMessageSerializer implements MessageSerializer { public class JsonMessageSerializer implements MessageSerializer {
@@ -28,16 +27,16 @@ public class JsonMessageSerializer implements MessageSerializer {
private final boolean prettyPrint; private final boolean prettyPrint;
/** /**
* Creates a new JSON serializer with default configuration (no pretty printing). * Cria um novo serializador JSON com configuração otimizada para produção (compacto).
*/ */
public JsonMessageSerializer() { public JsonMessageSerializer() {
this(false); this(false);
} }
/** /**
* Creates a new JSON serializer with optional pretty printing. * Cria um novo serializador JSON com formatação opcional.
* * * @param prettyPrint Se {@code true}, o JSON gerado incluirá indentação e quebras de linha.
* @param prettyPrint If true, JSON output will be formatted with indentation * Útil para debug, mas aumenta significativamente o tamanho do payload.
*/ */
public JsonMessageSerializer(boolean prettyPrint) { public JsonMessageSerializer(boolean prettyPrint) {
this.prettyPrint = prettyPrint; this.prettyPrint = prettyPrint;
@@ -53,6 +52,13 @@ public class JsonMessageSerializer implements MessageSerializer {
this.gson = builder.create(); this.gson = builder.create();
} }
/**
* Converte um objeto em memória para um array de bytes JSON (UTF-8).
*
* @param object O objeto a ser serializado.
* @return O payload em bytes pronto para transmissão TCP.
* @throws SerializationException Se o objeto não for compatível com JSON ou ocorrer erro de encoding.
*/
@Override @Override
public byte[] serialize(Object object) throws SerializationException { public byte[] serialize(Object object) throws SerializationException {
if (object == null) { if (object == null) {
@@ -68,6 +74,16 @@ public class JsonMessageSerializer implements MessageSerializer {
} }
} }
/**
* Reconstrói um objeto Java a partir de um array de bytes JSON.
* <p>
* Realiza a validação sintática do JSON e a validação de tipo baseada na classe alvo.
*
* @param data O array de bytes recebido da rede.
* @param clazz A classe do objeto esperado (Type Token).
* @return A instância do objeto reconstruído.
* @throws SerializationException Se o JSON for malformado ou incompatível com a classe alvo.
*/
@Override @Override
public <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException { public <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException {
if (data == null) { if (data == null) {
@@ -95,18 +111,16 @@ public class JsonMessageSerializer implements MessageSerializer {
} }
/** /**
* Returns the underlying Gson instance for advanced usage. * Retorna a instância subjacente do Gson para configurações avançadas.
* * * @return A instância Gson configurada.
* @return The Gson instance
*/ */
public Gson getGson() { public Gson getGson() {
return gson; return gson;
} }
/** /**
* Checks if pretty printing is enabled. * Verifica se a formatação "pretty print" está ativa.
* * * @return true se a indentação estiver habilitada.
* @return true if pretty printing is enabled
*/ */
public boolean isPrettyPrint() { public boolean isPrettyPrint() {
return prettyPrint; return prettyPrint;

View File

@@ -1,47 +1,48 @@
package sd.serialization; package sd.serialization;
/** /**
* Interface for serializing and deserializing objects for network transmission. * Interface que define o contrato para estratégias de serialização e desserialização de objetos.
* * <p>
* This interface provides a common abstraction for different serialization strategies * Esta abstração permite desacoplar a camada de transporte (Sockets TCP) da camada de
* allowing the system to switch between implementations without changing the communication layer. * apresentação de dados. Ao implementar o padrão <b>Strategy</b>, o sistema ganha flexibilidade
* * para alternar entre diferentes formatos de codificação (JSON, Binário Nativo, XML, Protobuf)
* Implementations must ensure: * sem necessidade de refatorização da lógica de rede.
* - Thread-safety if used in concurrent contexts * <p>
* - Proper exception handling with meaningful error messages * <b>Requisitos para Implementações:</b>
* - Preservation of object state during round-trip serialization * <ul>
* * <li><b>Thread-Safety:</b> As implementações devem ser seguras para uso concorrente, dado que
* @see JsonMessageSerializer * instâncias únicas podem ser partilhadas por múltiplos <i>ClientHandlers</i>.</li>
* <li><b>Robustez:</b> Falhas de parsing devem resultar em exceções tipificadas ({@link SerializationException}),
* nunca em falhas silenciosas ou estados inconsistentes.</li>
* </ul>
* * @see JsonMessageSerializer
*/ */
public interface MessageSerializer { public interface MessageSerializer {
/** /**
* Serializes an object into a byte array for transmission. * Converte (Marshals) um objeto em memória para uma sequência de bytes para transmissão.
* * * @param object O objeto de domínio a ser serializado (não pode ser nulo).
* @param object The object to serialize (must not be null) * @return Um array de bytes contendo a representação codificada do objeto.
* @return A byte array containing the serialized representation * @throws SerializationException Se ocorrer um erro durante a codificação (ex: ciclo de referências).
* @throws SerializationException If serialization fails * @throws IllegalArgumentException Se o objeto fornecido for nulo.
* @throws IllegalArgumentException If object is null
*/ */
byte[] serialize(Object object) throws SerializationException; byte[] serialize(Object object) throws SerializationException;
/** /**
* Deserializes a byte array back into an object of the specified type. * Reconstrói (Unmarshals) um objeto a partir de uma sequência de bytes.
* * * @param <T> O tipo genérico do objeto esperado.
* @param <T> The expected type of the deserialized object * @param data O array de bytes contendo os dados serializados (não pode ser nulo).
* @param data The byte array containing serialized data (must not be null) * @param clazz A classe do tipo esperado para verificação e instancialização.
* @param clazz The class of the expected object type (must not be null) * @return A instância do objeto reconstruído com o seu estado restaurado.
* @return The deserialized object * @throws SerializationException Se os dados estiverem corrompidos ou incompatíveis com a classe alvo.
* @throws SerializationException If deserialization fails * @throws IllegalArgumentException Se os dados ou a classe forem nulos.
* @throws IllegalArgumentException If data or clazz is null
*/ */
<T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException; <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException;
/** /**
* Gets the name of this serialization strategy (e.g., "JSON", "Java Native"). * Obtém o identificador legível desta estratégia de serialização (ex: "JSON (Gson)", "Native").
* Useful for logging and debugging. * Utilizado primariamente para logging, auditoria e negociação de conteúdo.
* * * @return O nome descritivo do serializador.
* @return The serializer name
*/ */
String getName(); String getName();

View File

@@ -1,39 +1,38 @@
package sd.serialization; package sd.serialization;
/** /**
* Exception thrown when serialization or deserialization operations fail. * Exceção verificada (Checked Exception) que sinaliza falhas no processo de transformação de dados.
* * <p>
* This exception wraps underlying errors (I/O exceptions, parsing errors, etc.) * Esta classe atua como um wrapper unificador para erros ocorridos na camada de serialização,
* and provides context about what went wrong during the serialization process. * abstraindo falhas de baixo nível (como erros de I/O, sintaxe JSON inválida ou incompatibilidade
* de tipos) numa única exceção de domínio. Permite que o código cliente trate falhas de
* protocolo de forma consistente, independentemente da implementação subjacente (Gson, Nativa, etc.).
*/ */
public class SerializationException extends Exception { public class SerializationException extends Exception {
private static final long serialVersionUID = 1L; // Long(64bits) instead of int(32bits) private static final long serialVersionUID = 1L; // Long(64bits) instead of int(32bits)
/** /**
* Constructs a new serialization exception with the specified detail message. * Constrói uma nova exceção de serialização com uma mensagem descritiva.
* * * @param message A mensagem detalhando o erro.
* @param message The detail message
*/ */
public SerializationException(String message) { public SerializationException(String message) {
super(message); super(message);
} }
/** /**
* Constructs a new serialization exception with the specified detail message * Constrói uma nova exceção encapsulando a causa raiz do problema.
* and cause. * Útil para preservar a stack trace original de erros de bibliotecas terceiras (ex: Gson).
* * * @param message A mensagem detalhando o erro.
* @param message The detail message * @param cause A exceção original que causou a falha.
* @param cause The cause of this exception
*/ */
public SerializationException(String message, Throwable cause) { public SerializationException(String message, Throwable cause) {
super(message, cause); super(message, cause);
} }
/** /**
* Constructs a new serialization exception with the specified cause. * Constrói uma nova exceção baseada apenas na causa raiz.
* * * @param cause A exceção original.
* @param cause The cause of this exception
*/ */
public SerializationException(Throwable cause) { public SerializationException(Throwable cause) {
super(cause); super(cause);

View File

@@ -1,14 +1,14 @@
package sd.serialization; package sd.serialization;
/** /**
* Factory for creating {@link MessageSerializer} instances. * Fábrica estática (Factory Pattern) para instanciação controlada de {@link MessageSerializer}.
* * <p>
* This factory provides a centralized way to create and configure JSON serializers * Esta classe centraliza a criação de estratégias de serialização, garantindo consistência
* using Gson, making it easy to configure serialization throughout the application. * de configuração em todo o sistema distribuído. Permite a injeção de configurações via
* * Propriedades de Sistema (System Properties), facilitando a alternância entre modos de
* The factory can be configured via system properties for easy deployment configuration. * depuração (Pretty Print) e produção (Compacto) sem recompilação.
* * <p>
* Example usage: * <b>Exemplo de Uso:</b>
* <pre> * <pre>
* MessageSerializer serializer = SerializerFactory.createDefault(); * MessageSerializer serializer = SerializerFactory.createDefault();
* byte[] data = serializer.serialize(myObject); * byte[] data = serializer.serialize(myObject);
@@ -17,28 +17,27 @@ package sd.serialization;
public class SerializerFactory { public class SerializerFactory {
/** /**
* System property key for enabling pretty-print in JSON serialization. * Chave da propriedade de sistema para ativar a formatação JSON legível (Pretty Print).
* Set to "true" for debugging, "false" for production. * Defina {@code -Dsd.serialization.json.prettyPrint=true} na JVM para ativar.
*/ */
public static final String JSON_PRETTY_PRINT_PROPERTY = "sd.serialization.json.prettyPrint"; public static final String JSON_PRETTY_PRINT_PROPERTY = "sd.serialization.json.prettyPrint";
// Default configuration // Default configuration (Production-ready)
private static final boolean DEFAULT_JSON_PRETTY_PRINT = false; private static final boolean DEFAULT_JSON_PRETTY_PRINT = false;
/** /**
* Private constructor to prevent instantiation. * Construtor privado para prevenir instanciação acidental desta classe utilitária.
*/ */
private SerializerFactory() { private SerializerFactory() {
throw new UnsupportedOperationException("Factory class cannot be instantiated"); throw new UnsupportedOperationException("Factory class cannot be instantiated");
} }
/** /**
* Creates a JSON serializer based on system configuration. * Cria um serializador JSON configurado dinamicamente pelo ambiente.
* * <p>
* Pretty-print is determined by checking the system property * Verifica a propriedade de sistema {@value #JSON_PRETTY_PRINT_PROPERTY}.
* {@value #JSON_PRETTY_PRINT_PROPERTY}. If not set, defaults to false. * Se não definida, assume o padrão de produção (falso/compacto).
* * * @return Uma instância configurada de {@link JsonMessageSerializer}.
* @return A configured JsonMessageSerializer instance
*/ */
public static MessageSerializer createDefault() { public static MessageSerializer createDefault() {
boolean prettyPrint = Boolean.getBoolean(JSON_PRETTY_PRINT_PROPERTY); boolean prettyPrint = Boolean.getBoolean(JSON_PRETTY_PRINT_PROPERTY);
@@ -46,19 +45,18 @@ public class SerializerFactory {
} }
/** /**
* Creates a JSON serializer with default configuration (no pretty printing). * Cria um serializador JSON com configuração padrão otimizada (sem indentação).
* * Ideal para ambientes de produção onde a largura de banda é prioritária.
* @return A JsonMessageSerializer instance * * @return Uma instância compacta de {@link JsonMessageSerializer}.
*/ */
public static MessageSerializer createSerializer() { public static MessageSerializer createSerializer() {
return createSerializer(DEFAULT_JSON_PRETTY_PRINT); return createSerializer(DEFAULT_JSON_PRETTY_PRINT);
} }
/** /**
* Creates a JSON serializer with specified pretty-print setting. * Cria um serializador JSON com configuração explícita de formatação.
* * * @param prettyPrint {@code true} para ativar indentação (Debug), {@code false} para compacto.
* @param prettyPrint Whether to enable pretty printing * @return Uma instância personalizada de {@link JsonMessageSerializer}.
* @return A JsonMessageSerializer instance
*/ */
public static MessageSerializer createSerializer(boolean prettyPrint) { public static MessageSerializer createSerializer(boolean prettyPrint) {
return new JsonMessageSerializer(prettyPrint); return new JsonMessageSerializer(prettyPrint);

View File

@@ -3,82 +3,88 @@ package sd.util;
import java.util.Random; import java.util.Random;
/** /**
* Utilitário para gerar valores aleatórios usados na simulação. * Utilitário central de geração estocástica para a simulação.
* * <p>
* <p>Fornece métodos estáticos para:</p> * Esta classe fornece primitivas para geração de números pseudo-aleatórios, abstraindo
* a complexidade de distribuições estatísticas.
* <p>
* <b>Funcionalidades Principais:</b>
* <ul> * <ul>
* <li>Gerar intervalos exponencialmente distribuídos (processos de Poisson)</li> * <li><b>Modelagem de Poisson:</b> Geração de tempos entre chegadas usando distribuição exponencial inversa.</li>
* <li>Gerar inteiros e doubles aleatórios num intervalo</li> * <li><b>Amostragem Uniforme:</b> Geração de inteiros e doubles em intervalos fechados/abertos.</li>
* <li>Tomar decisões baseadas em probabilidade</li> * <li><b>Decisão Probabilística:</b> Avaliação de eventos booleanos baseados em pesos (Bernoulli trials).</li>
* <li>Escolher elementos aleatórios de um array</li> * <li><b>Determinismo:</b> Suporte a sementes (seeds) manuais para reprodutibilidade exata de cenários de teste.</li>
* </ul> * </ul>
*
* <p>Usa uma única instância estática de {@link Random}.</p>
*/ */
public class RandomGenerator { public class RandomGenerator {
/** Instância partilhada de Random para toda a simulação */ /** * Instância singleton estática do gerador PRNG (Pseudo-Random Number Generator).
* Thread-safe (java.util.Random é sincronizado), embora possa haver contenção em alta concorrência.
*/
private static final Random random = new Random(); private static final Random random = new Random();
/** /**
* Retorna um intervalo de tempo que segue uma distribuição exponencial. * Gera um intervalo de tempo seguindo uma Distribuição Exponencial.
* <p>
* Este método implementa o algoritmo de <i>Inverse Transform Sampling</i> para simular
* um Processo de Poisson homogêneo. É fundamental para modelar a chegada natural de
* veículos, onde eventos independentes ocorrem a uma taxa média constante.
* <p>
* <b>Fórmula Matemática:</b> {@code T = -ln(1 - U) / λ}
* <br>Onde:
* <ul>
* <li>{@code U}: Variável aleatória uniforme no intervalo [0, 1).</li>
* <li>{@code λ (lambda)}: Taxa média de eventos por unidade de tempo (ex: veículos/segundo).</li>
* </ul>
* *
* <p>Componente essencial para modelar processos de Poisson, onde os * @param lambda A taxa média de chegada (λ > 0).
* tempos entre chegadas seguem uma distribuição exponencial.</p> * @return O intervalo de tempo (delta t) até o próximo evento, em segundos.
*
* <p>Fórmula: {@code Time = -ln(1 - U) / λ}<br>
* onde U é um número aleatório uniforme [0, 1) e λ (lambda) é a taxa média de chegada.</p>
*
* @param lambda taxa média de chegada λ (ex: 0.5 veículos por segundo)
* @return intervalo de tempo (segundos) até à próxima chegada
*/ */
public static double generateExponentialInterval(double lambda) { public static double generateExponentialInterval(double lambda) {
return Math.log(1 - random.nextDouble()) / -lambda; return Math.log(1 - random.nextDouble()) / -lambda;
} }
/** /**
* Retorna um inteiro aleatório entre {@code min} e {@code max}, inclusive. * Gera um número inteiro uniformemente distribuído no intervalo fechado {@code [min, max]}.
* *
* @param min valor mínimo possível * @param min Limite inferior (inclusivo).
* @param max valor máximo possível * @param max Limite superior (inclusivo).
* @return inteiro aleatório no intervalo [min, max] * @return Um inteiro aleatório I tal que {@code min <= I <= max}.
*/ */
public static int generateRandomInt(int min, int max) { public static int generateRandomInt(int min, int max) {
return random.nextInt(max - min + 1) + min; return random.nextInt(max - min + 1) + min;
} }
/** /**
* Retorna um double aleatório entre {@code min} (inclusive) e {@code max} (exclusivo). * Gera um número de ponto flutuante uniformemente distribuído no intervalo semi-aberto {@code [min, max)}.
* *
* @param min valor mínimo possível * @param min Limite inferior (inclusivo).
* @param max valor máximo possível * @param max Limite superior (exclusivo).
* @return double aleatório no intervalo [min, max) * @return Um double aleatório D tal que {@code min <= D < max}.
*/ */
public static double generateRandomDouble(double min, double max) { public static double generateRandomDouble(double min, double max) {
return min + (max - min) * random.nextDouble(); return min + (max - min) * random.nextDouble();
} }
/** /**
* Retorna {@code true} com uma dada probabilidade. * Realiza um teste de Bernoulli (Sim/Não) com uma probabilidade de sucesso especificada.
* <p>
* Utilizado para decisões de ramificação estocástica (ex: "Este veículo é um camião?").
* *
* <p>Útil para tomar decisões ponderadas. Por exemplo, * @param probability A probabilidade de retorno {@code true} (0.0 a 1.0).
* {@code occursWithProbability(0.3)} retorna {@code true} * @return {@code true} se o evento ocorrer, {@code false} caso contrário.
* aproximadamente 30% das vezes.</p>
*
* @param probability valor entre 0.0 (nunca) e 1.0 (sempre)
* @return {@code true} ou {@code false}, baseado na probabilidade
*/ */
public static boolean occursWithProbability(double probability) { public static boolean occursWithProbability(double probability) {
return random.nextDouble() < probability; return random.nextDouble() < probability;
} }
/** /**
* Escolhe um elemento aleatório do array fornecido. * Seleciona aleatoriamente um elemento de um array genérico (Amostragem Uniforme Discreta).
* *
* @param <T> tipo genérico do array * @param <T> O tipo dos elementos no array.
* @param array array de onde escolher * @param array A população de onde escolher.
* @return elemento selecionado aleatoriamente * @return O elemento selecionado.
* @throws IllegalArgumentException se o array for null ou vazio * @throws IllegalArgumentException Se o array for nulo ou vazio.
*/ */
public static <T> T chooseRandom(T[] array) { public static <T> T chooseRandom(T[] array) {
if (array == null || array.length == 0) { if (array == null || array.length == 0) {
@@ -88,13 +94,13 @@ public class RandomGenerator {
} }
/** /**
* Define a seed do gerador de números aleatórios partilhado. * Reinicializa a semente (seed) do gerador global.
* <p>
* <b>Importância Crítica:</b> Permite tornar a simulação determinística. Ao fixar a seed,
* a sequência de números "aleatórios" gerada será idêntica em execuções subsequentes,
* facilitando a depuração de race conditions ou lógica complexa.
* *
* <p>Extremamente útil para debugging e testes, pois permite executar * @param seed O valor da semente inicial (ex: timestamp ou constante).
* a simulação múltiplas vezes com a mesma sequência de eventos "aleatórios",
* tornando os resultados reproduzíveis.</p>
*
* @param seed seed a usar
*/ */
public static void setSeed(long seed) { public static void setSeed(long seed) {
random.setSeed(seed); random.setSeed(seed);

View File

@@ -9,55 +9,58 @@ import sd.model.VehicleType;
import sd.routing.RouteSelector; import sd.routing.RouteSelector;
/** /**
* Gera veículos para a simulação. * Motor de injeção de carga (Load Injector) para a simulação de tráfego.
* * <p>
* <p>Esta classe é responsável por duas tarefas principais:</p> * Esta classe atua como uma fábrica estocástica de veículos, sendo responsável por:
* <ol> * <ol>
* <li>Determinar <em>quando</em> o próximo veículo deve chegar, baseado no * <li><b>Modelagem Temporal:</b> Determinar os instantes de chegada (Inter-arrival times)
* modelo de chegada (POISSON ou FIXED) da {@link SimulationConfig}</li> * usando processos de Poisson (estocástico) ou intervalos determinísticos.</li>
* <li>Criar um novo objeto {@link Vehicle} com tipo e rota selecionados pela * <li><b>Caracterização da Entidade:</b> Atribuir tipos de veículo (Bike, Light, Heavy)
* política de roteamento configurada ({@link RouteSelector})</li> * baseado numa Distribuição de Probabilidade Cumulativa (CDF).</li>
* <li><b>Inicialização Espacial:</b> Distribuir a carga uniformemente entre os pontos de entrada (E1-E3).</li>
* <li><b>Atribuição de Rota:</b> Delegar a escolha do percurso à estratégia {@link RouteSelector} ativa.</li>
* </ol> * </ol>
*
* <p>As rotas são selecionadas usando uma política de roteamento que pode ser:
* aleatória, caminho mais curto, menor congestionamento, etc.</p>
*/ */
public class VehicleGenerator { public class VehicleGenerator {
private final SimulationConfig config; private final SimulationConfig config;
private final String arrivalModel; private final String arrivalModel;
/** Lambda (λ) para modelo POISSON */
/** Parâmetro Lambda (λ) para a distribuição de Poisson (taxa de chegada). */
private final double arrivalRate; private final double arrivalRate;
/** Intervalo para modelo FIXED */
/** Intervalo determinístico para geração constante (modo debug/teste). */
private final double fixedInterval; private final double fixedInterval;
/** Política de roteamento usada para selecionar rotas */ /** * Estratégia de roteamento atual.
* Não é final para permitir Hot-Swapping durante a execução.
*/
private RouteSelector routeSelector; private RouteSelector routeSelector;
/** /**
* Cria um novo gerador de veículos com a política de roteamento especificada. * Inicializa o gerador com as configurações de simulação e estratégia de roteamento.
* Lê a configuração necessária.
* *
* @param config objeto de {@link SimulationConfig} * @param config A configuração global contendo as taxas e probabilidades.
* @param routeSelector política de roteamento a usar para selecionar rotas * @param routeSelector A estratégia inicial de seleção de rotas.
*/ */
public VehicleGenerator(SimulationConfig config, RouteSelector routeSelector) { public VehicleGenerator(SimulationConfig config, RouteSelector routeSelector) {
this.config = config; this.config = config;
this.routeSelector = routeSelector; this.routeSelector = routeSelector;
// Cache configuration values for performance // Cache de valores de configuração para evitar lookups repetitivos em hot-path
this.arrivalModel = config.getArrivalModel(); this.arrivalModel = config.getArrivalModel();
this.arrivalRate = config.getArrivalRate(); this.arrivalRate = config.getArrivalRate();
this.fixedInterval = config.getFixedArrivalInterval(); this.fixedInterval = config.getFixedArrivalInterval();
} }
/** /**
* Calcula o tempo <em>absoluto</em> da próxima chegada de veículo * Calcula o timestamp absoluto para a próxima injeção de veículo.
* baseado no modelo configurado. * <p>
* * Se o modelo for "POISSON", utiliza a técnica de <i>Inverse Transform Sampling</i>
* @param currentTime tempo atual da simulação, usado como base * (via {@link RandomGenerator}) para gerar intervalos exponencialmente distribuídos,
* @return tempo absoluto (ex: {@code currentTime + intervalo}) * simulando a aleatoriedade natural do tráfego.
* em que o próximo veículo deve ser gerado * * @param currentTime O tempo atual da simulação (base de cálculo).
* @return O instante futuro (t + delta) para agendamento do evento de geração.
*/ */
public double getNextArrivalTime(double currentTime) { public double getNextArrivalTime(double currentTime) {
if ("POISSON".equalsIgnoreCase(arrivalModel)) { if ("POISSON".equalsIgnoreCase(arrivalModel)) {
@@ -69,19 +72,19 @@ public class VehicleGenerator {
} }
/** /**
* Gera um novo objeto {@link Vehicle}. * Instancia (Spawn) um novo veículo configurado e roteado.
* * <p>
* <p>Passos executados:</p> * O processo de criação segue um pipeline:
* <ol> * <ol>
* <li>Seleciona um {@link VehicleType} aleatório baseado em probabilidades</li> * <li>Seleção de Tipo (Roda da Fortuna / CDF).</li>
* <li>Seleciona um ponto de entrada aleatório (E1, E2, E3)</li> * <li>Seleção de Entrada (Uniforme).</li>
* <li>Usa a política de roteamento para escolher a rota</li> * <li>Cálculo de Rota (Delegado ao Strategy).</li>
* </ol> * </ol>
* *
* @param vehicleId identificador único do novo veículo (ex: "V123") * @param vehicleId O identificador único sequencial (ex: "V104").
* @param entryTime tempo de simulação em que o veículo é criado * @param entryTime O timestamp de criação.
* @param queueSizes mapa com tamanho das filas (opcional, pode ser null) * @param queueSizes Snapshot atual das filas (usado apenas por estratégias dinâmicas como LEAST_CONGESTED).
* @return novo objeto {@link Vehicle} configurado * @return A entidade {@link Vehicle} pronta para inserção na malha.
*/ */
public Vehicle generateVehicle(String vehicleId, double entryTime, Map<String, Integer> queueSizes) { public Vehicle generateVehicle(String vehicleId, double entryTime, Map<String, Integer> queueSizes) {
VehicleType type = selectVehicleType(); VehicleType type = selectVehicleType();
@@ -92,18 +95,12 @@ public class VehicleGenerator {
} }
/** /**
* Seleciona um {@link VehicleType} (BIKE, LIGHT, HEAVY) baseado nas * Seleciona o tipo de veículo usando Amostragem por Probabilidade Cumulativa.
* probabilidades definidas na {@link SimulationConfig}. * <p>
* Normaliza as probabilidades configuradas e mapeia um número aleatório [0, 1)
* para o intervalo correspondente ao tipo de veículo.
* *
* <p>Usa técnica de "probabilidade cumulativa":</p> * @return O tipo enumerado {@link VehicleType} selecionado.
* <ol>
* <li>Obtém número aleatório {@code rand} de [0, 1)</li>
* <li>Se {@code rand < P(Bike)}, retorna BIKE</li>
* <li>Senão se {@code rand < P(Bike) + P(Light)}, retorna LIGHT</li>
* <li>Caso contrário, retorna HEAVY</li>
* </ol>
*
* @return tipo de veículo selecionado
*/ */
private VehicleType selectVehicleType() { private VehicleType selectVehicleType() {
double bikeProbability = config.getBikeVehicleProbability(); double bikeProbability = config.getBikeVehicleProbability();
@@ -111,7 +108,9 @@ public class VehicleGenerator {
double heavyProbability = config.getHeavyVehicleProbability(); double heavyProbability = config.getHeavyVehicleProbability();
double total = bikeProbability + lightProbability + heavyProbability; double total = bikeProbability + lightProbability + heavyProbability;
if (total == 0) return VehicleType.LIGHT; // Avoid division by zero if (total == 0) return VehicleType.LIGHT; // Fallback de segurança
// Normalização
bikeProbability /= total; bikeProbability /= total;
lightProbability /= total; lightProbability /= total;
@@ -127,10 +126,10 @@ public class VehicleGenerator {
} }
/** /**
* Seleciona aleatoriamente um ponto de entrada (E1, E2 ou E3). * Seleciona um ponto de injeção na borda da rede (Edge Node).
* Cada ponto tem probabilidade igual (1/3). * Distribuição Uniforme: ~33.3% para cada entrada (E1, E2, E3).
* *
* @return ponto de entrada selecionado ("E1", "E2" ou "E3") * @return O ID da interseção de entrada.
*/ */
private String selectRandomEntryPoint() { private String selectRandomEntryPoint() {
double rand = Math.random(); double rand = Math.random();
@@ -145,23 +144,19 @@ public class VehicleGenerator {
} }
/** /**
* Altera dinamicamente o RouteSelector usado para gerar rotas. * Atualiza a estratégia de roteamento em tempo de execução (Hot-Swap).
* Permite mudar a política de roteamento durante a simulação. * <p>
* * Permite que o Coordenador altere o comportamento da frota (ex: de RANDOM para SHORTEST_PATH)
* @param newRouteSelector novo seletor de rotas * sem necessidade de reiniciar a simulação.
* * @param newRouteSelector A nova implementação de estratégia a utilizar.
*/ */
public void setRouteSelector(RouteSelector newRouteSelector) { public void setRouteSelector(RouteSelector newRouteSelector) {
// Note: In Java, we can't directly modify the 'final' field, this.routeSelector = newRouteSelector;
// but we can create a new VehicleGenerator with the new selector.
// For this implementation, we'll need to remove 'final' from routeSelector.
// This is acceptable since we want dynamic policy changes.
throw new UnsupportedOperationException(
"VehicleGenerator is immutable. Use CoordinatorProcess.changeRoutingPolicy() instead."
);
} }
/** /**
* @return A string providing information about the generator's configuration. * Retorna uma representação textual do estado interno do gerador.
* Útil para logs de auditoria e debugging.
*/ */
public String getInfo() { public String getInfo() {
return String.format( return String.format(

View File

@@ -1,6 +1,6 @@
/* Global Styles */ /* Global Styles */
.root { .root {
-fx-background-color: #f4f7f6; -fx-background-color: #2b2b2b;
-fx-font-family: 'Segoe UI', sans-serif; -fx-font-family: 'Segoe UI', sans-serif;
} }
@@ -63,24 +63,24 @@
/* Cards / Panels */ /* Cards / Panels */
.card { .card {
-fx-background-color: white; -fx-background-color: #1e1e1e;
-fx-background-radius: 8; -fx-background-radius: 8;
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.05), 10, 0, 0, 2); -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 10, 0, 0, 2);
-fx-padding: 0; -fx-padding: 0;
} }
.card-header { .card-header {
-fx-background-color: #ecf0f1; -fx-background-color: #3a3a3a;
-fx-background-radius: 8 8 0 0; -fx-background-radius: 8 8 0 0;
-fx-padding: 10 15; -fx-padding: 10 15;
-fx-border-color: #bdc3c7; -fx-border-color: #555555;
-fx-border-width: 0 0 1 0; -fx-border-width: 0 0 1 0;
} }
.card-title { .card-title {
-fx-font-size: 16px; -fx-font-size: 16px;
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-text-fill: #2c3e50; -fx-text-fill: white;
} }
.card-content { .card-content {
@@ -90,43 +90,48 @@
/* Statistics Grid */ /* Statistics Grid */
.stat-label { .stat-label {
-fx-font-size: 14px; -fx-font-size: 14px;
-fx-text-fill: #7f8c8d; -fx-text-fill: #cccccc;
} }
.stat-value { .stat-value {
-fx-font-size: 20px; -fx-font-size: 20px;
-fx-font-weight: bold; -fx-font-weight: bold;
-fx-text-fill: #2980b9; -fx-text-fill: #4ca1af;
} }
/* Tables */ /* Tables */
.table-view { .table-view {
-fx-background-color: transparent; -fx-background-color: #1e1e1e;
-fx-border-color: transparent; -fx-border-color: transparent;
} }
.table-view .column-header-background { .table-view .column-header-background {
-fx-background-color: #ecf0f1; -fx-background-color: #3a3a3a;
-fx-border-color: #bdc3c7; -fx-border-color: #555555;
-fx-border-width: 0 0 1 0; -fx-border-width: 0 0 1 0;
} }
.table-view .column-header .label { .table-view .column-header .label {
-fx-text-fill: #2c3e50; -fx-text-fill: white;
-fx-font-weight: bold; -fx-font-weight: bold;
} }
.table-row-cell { .table-row-cell {
-fx-background-color: white; -fx-background-color: #1e1e1e;
-fx-border-color: transparent; -fx-border-color: transparent;
-fx-text-fill: white;
} }
.table-row-cell:odd { .table-row-cell:odd {
-fx-background-color: #f9f9f9; -fx-background-color: #252525;
} }
.table-row-cell:selected { .table-row-cell:selected {
-fx-background-color: #3498db; -fx-background-color: #4ca1af;
-fx-text-fill: white;
}
.table-cell {
-fx-text-fill: white; -fx-text-fill: white;
} }

View File

@@ -1,13 +1,4 @@
# ========================================================= # Configuração de rede
# Traffic Simulation Configuration - HIGH LOAD SCENARIO
# ---------------------------------------------------------
# High traffic scenario for testing system under heavy load.
# Expected: Significant congestion, large queues, system stress test
# =========================================================
# === NETWORK CONFIGURATION ===
# Intersections (each with its host and port)
intersection.Cr1.host=localhost intersection.Cr1.host=localhost
intersection.Cr1.port=8001 intersection.Cr1.port=8001
intersection.Cr2.host=localhost intersection.Cr2.host=localhost
@@ -19,67 +10,48 @@ intersection.Cr4.port=8004
intersection.Cr5.host=localhost intersection.Cr5.host=localhost
intersection.Cr5.port=8005 intersection.Cr5.port=8005
# Exit node
exit.host=localhost exit.host=localhost
exit.port=9001 exit.port=9001
# Dashboard server
dashboard.host=localhost dashboard.host=localhost
dashboard.port=9000 dashboard.port=9000
# Configuração da simulação
# === SIMULATION CONFIGURATION === # Cenário de carga alta - tráfego pesado, teste de stress do sistema
# Total duration in seconds (1800 = 30 minutes)
simulation.duration=1800 simulation.duration=1800
# Vehicle arrival model: FIXED or POISSON
simulation.arrival.model=POISSON simulation.arrival.model=POISSON
# λ (lambda): HIGH LOAD = 1.0 vehicle per second (60 vehicles/minute, 3600 vehicles/hour)
# This is 2x medium load - tests system capacity limits
simulation.arrival.rate=1.0 simulation.arrival.rate=1.0
# Fixed interval between arrivals (only used if model=FIXED)
simulation.arrival.fixed.interval=2.0 simulation.arrival.fixed.interval=2.0
# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED
simulation.routing.policy=LEAST_CONGESTED simulation.routing.policy=LEAST_CONGESTED
# Tempos dos semáforos (tempos realistas do mundo real, sem fase amarela)
# Cruzamento 1 - ponto de entrada, verde mais longo
trafficlight.Cr1.South.green=45.0
trafficlight.Cr1.South.red=45.0
trafficlight.Cr1.East.green=45.0
trafficlight.Cr1.East.red=45.0
# === TRAFFIC LIGHT TIMINGS === # Cruzamento 2 - hub principal, gargalo crítico, tempos máximos de verde
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds> trafficlight.Cr2.South.green=50.0
# Aggressive timings to maximize throughput under high load trafficlight.Cr2.South.red=50.0
trafficlight.Cr2.East.green=60.0
trafficlight.Cr2.East.red=40.0
trafficlight.Cr2.West.green=50.0
trafficlight.Cr2.West.red=50.0
# Intersection 1 (Entry point - longer greens to prevent early backup) # Cruzamento 3 - caminho para a saída
trafficlight.Cr1.South.green=60.0 trafficlight.Cr3.South.green=40.0
trafficlight.Cr1.South.red=3.0 trafficlight.Cr3.South.red=45.0
trafficlight.Cr1.East.green=60.0 trafficlight.Cr3.West.green=45.0
trafficlight.Cr1.East.red=3.0 trafficlight.Cr3.West.red=40.0
# Intersection 2 (Main hub - CRITICAL BOTTLENECK, maximum green times) # Cruzamento 4
# This is the most critical intersection - all routes converge here trafficlight.Cr4.East.green=45.0
trafficlight.Cr2.South.green=70.0 trafficlight.Cr4.East.red=45.0
trafficlight.Cr2.South.red=3.0 trafficlight.Cr4.North.green=45.0
trafficlight.Cr2.East.green=80.0 trafficlight.Cr4.North.red=45.0
trafficlight.Cr2.East.red=3.0
trafficlight.Cr2.West.green=70.0
trafficlight.Cr2.West.red=3.0
# Intersection 3 (Path to exit - maximize East throughput to exit) # Cruzamento 5 - perto da saída, gargalo principal
trafficlight.Cr3.South.green=50.0
trafficlight.Cr3.South.red=3.0
trafficlight.Cr3.West.green=40.0
trafficlight.Cr3.West.red=3.0
# Intersection 4 (High throughput needed toward Cr5)
trafficlight.Cr4.East.green=70.0
trafficlight.Cr4.East.red=3.0
trafficlight.Cr4.North.green=70.0
trafficlight.Cr4.North.red=3.0
# Intersection 5 (Near exit - MAJOR BOTTLENECK, longest green time)
# All routes funnel through here before exit
trafficlight.Cr5.East.green=90.0 trafficlight.Cr5.East.green=90.0
trafficlight.Cr5.East.red=3.0 trafficlight.Cr5.East.red=3.0
trafficlight.Cr5.West.green=70.0 trafficlight.Cr5.West.green=70.0
@@ -87,40 +59,17 @@ trafficlight.Cr5.West.red=3.0
trafficlight.Cr5.North.green=70.0 trafficlight.Cr5.North.green=70.0
trafficlight.Cr5.North.red=3.0 trafficlight.Cr5.North.red=3.0
# Configuração de veículos
# === VEHICLE CONFIGURATION ===
# Probability distribution for vehicle types (must sum to 1.0)
vehicle.probability.bike=0.2 vehicle.probability.bike=0.2
vehicle.probability.light=0.6 vehicle.probability.light=0.6
vehicle.probability.heavy=0.2 vehicle.probability.heavy=0.2
# Average crossing times (in seconds)
vehicle.crossing.time.bike=1.0 vehicle.crossing.time.bike=1.0
vehicle.crossing.time.light=2.0 vehicle.crossing.time.light=2.0
vehicle.crossing.time.heavy=4.0 vehicle.crossing.time.heavy=4.0
# Travel times between intersections (in seconds)
# Base time for light vehicles (cars)
vehicle.travel.time.base=1.0 vehicle.travel.time.base=1.0
# Bike travel time = 0.5 × car travel time
vehicle.travel.time.bike.multiplier=0.5 vehicle.travel.time.bike.multiplier=0.5
# Heavy vehicle travel time = 4.0 x base travel time
vehicle.travel.time.heavy.multiplier=4.0 vehicle.travel.time.heavy.multiplier=4.0
# === STATISTICS ===
# Interval between dashboard updates (seconds)
statistics.update.interval=10.0 statistics.update.interval=10.0
# === EXPECTED BEHAVIOR - HIGH LOAD ===
# - Average system time: 200-400+ seconds (3-7+ minutes)
# - Maximum queue sizes: 15-30+ vehicles at Cr2 and Cr5
# - Average queue sizes: 8-15+ vehicles
# - Severe congestion at Cr2 (main convergence point)
# - Severe congestion at Cr5 (pre-exit bottleneck)
# - System utilization: ~80-95%
# - Many vehicles will remain in system at simulation end
# - Queue growth may be unbounded if arrival rate exceeds service rate
# - Primary bottlenecks: Cr2 (3-way convergence) and Cr5 (final funnel)
# - This scenario tests maximum system capacity and traffic light optimization
# - Expected to demonstrate need for adaptive traffic light policies

View File

@@ -1,13 +1,4 @@
# ========================================================= # Configuração de rede
# Traffic Simulation Configuration - LOW LOAD SCENARIO
# ---------------------------------------------------------
# Low traffic scenario for testing system under light load.
# Expected: No congestion, minimal queues, fast vehicle throughput
# =========================================================
# === NETWORK CONFIGURATION ===
# Intersections (each with its host and port)
intersection.Cr1.host=localhost intersection.Cr1.host=localhost
intersection.Cr1.port=8001 intersection.Cr1.port=8001
intersection.Cr2.host=localhost intersection.Cr2.host=localhost
@@ -19,65 +10,48 @@ intersection.Cr4.port=8004
intersection.Cr5.host=localhost intersection.Cr5.host=localhost
intersection.Cr5.port=8005 intersection.Cr5.port=8005
# Exit node
exit.host=localhost exit.host=localhost
exit.port=9001 exit.port=9001
# Dashboard server
dashboard.host=localhost dashboard.host=localhost
dashboard.port=9000 dashboard.port=9000
# Configuração da simulação
# === SIMULATION CONFIGURATION === # Cenário de carga baixa - tráfego leve para testar o sistema sem congestionamento
# Total duration in seconds (1800 = 30 minutes)
simulation.duration=1800 simulation.duration=1800
# Vehicle arrival model: FIXED or POISSON
simulation.arrival.model=POISSON simulation.arrival.model=POISSON
# λ (lambda): LOW LOAD = 0.2 vehicles per second (12 vehicles/minute, 720 vehicles/hour)
# This is approximately 40% of medium load
simulation.arrival.rate=0.2 simulation.arrival.rate=0.2
# Fixed interval between arrivals (only used if model=FIXED)
simulation.arrival.fixed.interval=2.0 simulation.arrival.fixed.interval=2.0
# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED
simulation.routing.policy=LEAST_CONGESTED simulation.routing.policy=LEAST_CONGESTED
# Tempos dos semáforos (tempos realistas do mundo real, sem fase amarela)
# === TRAFFIC LIGHT TIMINGS === # Cruzamento 1 - ponto de entrada, equilibrado
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds>
# Standard timings - should be more than adequate for low load
# Intersection 1 (Entry point - balanced)
trafficlight.Cr1.South.green=30.0 trafficlight.Cr1.South.green=30.0
trafficlight.Cr1.South.red=5.0 trafficlight.Cr1.South.red=30.0
trafficlight.Cr1.East.green=30.0 trafficlight.Cr1.East.green=30.0
trafficlight.Cr1.East.red=5.0 trafficlight.Cr1.East.red=30.0
# Intersection 2 (Main hub - shorter cycles, favor East-West) # Cruzamento 2 - hub principal
trafficlight.Cr2.South.green=30.0 trafficlight.Cr2.South.green=30.0
trafficlight.Cr2.South.red=5.0 trafficlight.Cr2.South.red=30.0
trafficlight.Cr2.East.green=30.0 trafficlight.Cr2.East.green=30.0
trafficlight.Cr2.East.red=5.0 trafficlight.Cr2.East.red=30.0
trafficlight.Cr2.West.green=30.0 trafficlight.Cr2.West.green=30.0
trafficlight.Cr2.West.red=5.0 trafficlight.Cr2.West.red=30.0
# Intersection 3 (Path to exit - favor East) # Cruzamento 3 - caminho para a saída
trafficlight.Cr3.South.green=30.0 trafficlight.Cr3.South.green=30.0
trafficlight.Cr3.South.red=5.0 trafficlight.Cr3.South.red=30.0
trafficlight.Cr3.West.green=30.0 trafficlight.Cr3.West.green=30.0
trafficlight.Cr3.West.red=5.0 trafficlight.Cr3.West.red=30.0
# Intersection 4 (Favor East toward Cr5) # Cruzamento 4
trafficlight.Cr4.East.green=30.0 trafficlight.Cr4.East.green=30.0
trafficlight.Cr4.East.red=5.0 trafficlight.Cr4.East.red=30.0
trafficlight.Cr4.North.green=30.0 trafficlight.Cr4.North.green=30.0
trafficlight.Cr4.North.red=5.0 trafficlight.Cr4.North.red=30.0
# Intersection 5 (Near exit - favor East) # Cruzamento 5 - perto da saída
trafficlight.Cr5.East.green=30.0 trafficlight.Cr5.East.green=30.0
trafficlight.Cr5.East.red=5.0 trafficlight.Cr5.East.red=5.0
trafficlight.Cr5.West.green=30.0 trafficlight.Cr5.West.green=30.0
@@ -85,36 +59,17 @@ trafficlight.Cr5.West.red=5.0
trafficlight.Cr5.North.green=30.0 trafficlight.Cr5.North.green=30.0
trafficlight.Cr5.North.red=5.0 trafficlight.Cr5.North.red=5.0
# Configuração de veículos
# === VEHICLE CONFIGURATION ===
# Probability distribution for vehicle types (must sum to 1.0)
vehicle.probability.bike=0.2 vehicle.probability.bike=0.2
vehicle.probability.light=0.6 vehicle.probability.light=0.6
vehicle.probability.heavy=0.2 vehicle.probability.heavy=0.2
# Average crossing times (in seconds)
vehicle.crossing.time.bike=1.0 vehicle.crossing.time.bike=1.0
vehicle.crossing.time.light=2.0 vehicle.crossing.time.light=2.0
vehicle.crossing.time.heavy=4.0 vehicle.crossing.time.heavy=4.0
# Travel times between intersections (in seconds)
# Base time for light vehicles (cars)
vehicle.travel.time.base=1.0 vehicle.travel.time.base=1.0
# Bike travel time = 0.5 × car travel time
vehicle.travel.time.bike.multiplier=0.5 vehicle.travel.time.bike.multiplier=0.5
# Heavy vehicle travel time = 4.0 x base travel time
vehicle.travel.time.heavy.multiplier=4.0 vehicle.travel.time.heavy.multiplier=4.0
# === STATISTICS ===
# Interval between dashboard updates (seconds)
statistics.update.interval=10.0 statistics.update.interval=10.0
# === EXPECTED BEHAVIOR - LOW LOAD ===
# - Average system time: 40-80 seconds
# - Maximum queue sizes: 1-3 vehicles
# - Average queue sizes: < 1 vehicle
# - Vehicles should flow smoothly through the system
# - Minimal waiting at traffic lights (mostly travel time)
# - System utilization: ~20-30%
# - All vehicles should exit within simulation time

View File

@@ -1,13 +1,4 @@
# ========================================================= # Configuração de rede
# Traffic Simulation Configuration - MEDIUM LOAD SCENARIO
# ---------------------------------------------------------
# Medium traffic scenario for testing system under normal load.
# Expected: Moderate queues, some congestion at peak intersections
# =========================================================
# === NETWORK CONFIGURATION ===
# Intersections (each with its host and port)
intersection.Cr1.host=localhost intersection.Cr1.host=localhost
intersection.Cr1.port=8001 intersection.Cr1.port=8001
intersection.Cr2.host=localhost intersection.Cr2.host=localhost
@@ -19,65 +10,48 @@ intersection.Cr4.port=8004
intersection.Cr5.host=localhost intersection.Cr5.host=localhost
intersection.Cr5.port=8005 intersection.Cr5.port=8005
# Exit node
exit.host=localhost exit.host=localhost
exit.port=9001 exit.port=9001
# Dashboard server
dashboard.host=localhost dashboard.host=localhost
dashboard.port=9000 dashboard.port=9000
# Configuração da simulação
# === SIMULATION CONFIGURATION === # Cenário de carga média - tráfego normal com algum congestionamento
# Total duration in seconds (1800 = 30 minutes)
simulation.duration=1800 simulation.duration=1800
# Vehicle arrival model: FIXED or POISSON
simulation.arrival.model=POISSON simulation.arrival.model=POISSON
# λ (lambda): MEDIUM LOAD = 0.5 vehicles per second (30 vehicles/minute, 1800 vehicles/hour)
# This represents normal traffic conditions
simulation.arrival.rate=0.5 simulation.arrival.rate=0.5
# Fixed interval between arrivals (only used if model=FIXED)
simulation.arrival.fixed.interval=2.0 simulation.arrival.fixed.interval=2.0
# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED
simulation.routing.policy=LEAST_CONGESTED simulation.routing.policy=LEAST_CONGESTED
# Tempos dos semáforos (tempos realistas do mundo real, sem fase amarela)
# Cruzamento 1 - ponto de entrada, equilibrado
trafficlight.Cr1.South.green=35.0
trafficlight.Cr1.South.red=35.0
trafficlight.Cr1.East.green=35.0
trafficlight.Cr1.East.red=35.0
# === TRAFFIC LIGHT TIMINGS === # Cruzamento 2 - hub principal, gargalo crítico
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds> trafficlight.Cr2.South.green=40.0
# Optimized timings for medium load trafficlight.Cr2.South.red=40.0
trafficlight.Cr2.East.green=45.0
trafficlight.Cr2.East.red=35.0
trafficlight.Cr2.West.green=40.0
trafficlight.Cr2.West.red=40.0
# Intersection 1 (Entry point - balanced) # Cruzamento 3 - caminho para a saída
trafficlight.Cr1.South.green=40.0 trafficlight.Cr3.South.green=35.0
trafficlight.Cr1.South.red=5.0 trafficlight.Cr3.South.red=40.0
trafficlight.Cr1.East.green=40.0 trafficlight.Cr3.West.green=40.0
trafficlight.Cr1.East.red=5.0 trafficlight.Cr3.West.red=35.0
# Intersection 2 (Main hub - CRITICAL BOTTLENECK, longer green times) # Cruzamento 4
trafficlight.Cr2.South.green=45.0 trafficlight.Cr4.East.green=35.0
trafficlight.Cr2.South.red=5.0 trafficlight.Cr4.East.red=35.0
trafficlight.Cr2.East.green=50.0 trafficlight.Cr4.North.green=35.0
trafficlight.Cr2.East.red=5.0 trafficlight.Cr4.North.red=35.0
trafficlight.Cr2.West.green=45.0
trafficlight.Cr2.West.red=5.0
# Intersection 3 (Path to exit - favor East toward exit) # Cruzamento 5 - perto da saída, possível gargalo
trafficlight.Cr3.South.green=40.0
trafficlight.Cr3.South.red=5.0
trafficlight.Cr3.West.green=35.0
trafficlight.Cr3.West.red=5.0
# Intersection 4 (Favor East toward Cr5)
trafficlight.Cr4.East.green=40.0
trafficlight.Cr4.East.red=5.0
trafficlight.Cr4.North.green=40.0
trafficlight.Cr4.North.red=5.0
# Intersection 5 (Near exit - POTENTIAL BOTTLENECK, longer green)
trafficlight.Cr5.East.green=50.0 trafficlight.Cr5.East.green=50.0
trafficlight.Cr5.East.red=5.0 trafficlight.Cr5.East.red=5.0
trafficlight.Cr5.West.green=45.0 trafficlight.Cr5.West.green=45.0
@@ -85,37 +59,17 @@ trafficlight.Cr5.West.red=5.0
trafficlight.Cr5.North.green=45.0 trafficlight.Cr5.North.green=45.0
trafficlight.Cr5.North.red=5.0 trafficlight.Cr5.North.red=5.0
# Configuração de veículos
# === VEHICLE CONFIGURATION ===
# Probability distribution for vehicle types (must sum to 1.0)
vehicle.probability.bike=0.2 vehicle.probability.bike=0.2
vehicle.probability.light=0.6 vehicle.probability.light=0.6
vehicle.probability.heavy=0.2 vehicle.probability.heavy=0.2
# Average crossing times (in seconds)
vehicle.crossing.time.bike=1.0 vehicle.crossing.time.bike=1.0
vehicle.crossing.time.light=2.0 vehicle.crossing.time.light=2.0
vehicle.crossing.time.heavy=4.0 vehicle.crossing.time.heavy=4.0
# Travel times between intersections (in seconds)
# Base time for light vehicles (cars)
vehicle.travel.time.base=1.0 vehicle.travel.time.base=1.0
# Bike travel time = 0.5 × car travel time
vehicle.travel.time.bike.multiplier=0.5 vehicle.travel.time.bike.multiplier=0.5
# Heavy vehicle travel time = 4.0 x base travel time
vehicle.travel.time.heavy.multiplier=4.0 vehicle.travel.time.heavy.multiplier=4.0
# === STATISTICS ===
# Interval between dashboard updates (seconds)
statistics.update.interval=10.0 statistics.update.interval=10.0
# === EXPECTED BEHAVIOR - MEDIUM LOAD ===
# - Average system time: 80-150 seconds
# - Maximum queue sizes: 5-10 vehicles at Cr2 and Cr5
# - Average queue sizes: 2-5 vehicles
# - Moderate congestion at Cr2 (main hub) and Cr5 (pre-exit)
# - System utilization: ~50-60%
# - Most vehicles should exit, some may remain at simulation end
# - Cr2 is the primary bottleneck (3 directions converge)
# - Cr5 is secondary bottleneck (all routes pass through)

View File

@@ -1,13 +1,4 @@
# ========================================================= # Configuração de rede
# Traffic Simulation Configuration
# ---------------------------------------------------------
# All parameters controlling network layout, timing,
# and simulation behavior.
# =========================================================
# === NETWORK CONFIGURATION ===
# Intersections (each with its host and port)
intersection.Cr1.host=localhost intersection.Cr1.host=localhost
intersection.Cr1.port=8001 intersection.Cr1.port=8001
intersection.Cr2.host=localhost intersection.Cr2.host=localhost
@@ -19,92 +10,60 @@ intersection.Cr4.port=8004
intersection.Cr5.host=localhost intersection.Cr5.host=localhost
intersection.Cr5.port=8005 intersection.Cr5.port=8005
# Exit node
exit.host=localhost exit.host=localhost
exit.port=9001 exit.port=9001
# Dashboard server
dashboard.host=localhost dashboard.host=localhost
dashboard.port=9000 dashboard.port=9000
# Configuração da simulação
# === SIMULATION CONFIGURATION ===
# Total duration in seconds (3600 = 1 hour)
simulation.duration=300 simulation.duration=300
# Time scaling factor for visualization (real_seconds = sim_seconds * scale)
# 0 = instant (pure DES), 0.01 = 100x speed, 0.1 = 10x speed, 1.0 = real-time
simulation.time.scale=0.01 simulation.time.scale=0.01
# Vehicle arrival model: FIXED or POISSON
simulation.arrival.model=POISSON simulation.arrival.model=POISSON
# λ (lambda): average arrival rate (vehicles per second)
simulation.arrival.rate=0.5 simulation.arrival.rate=0.5
# Fixed interval between arrivals (only used if model=FIXED)
simulation.arrival.fixed.interval=2.0 simulation.arrival.fixed.interval=2.0
# Routing policy: RANDOM, SHORTEST_PATH, LEAST_CONGESTED
# RANDOM: selects routes with predefined probabilities (baseline)
# SHORTEST_PATH: always chooses the route with fewest intersections
# LEAST_CONGESTED: dynamically chooses routes to avoid congested areas
simulation.routing.policy=RANDOM simulation.routing.policy=RANDOM
# Tempos dos semáforos (tempos realistas do mundo real, sem fase amarela)
# Cruzamento 1 - ponto de entrada, equilibrado
trafficlight.Cr1.South.green=35.0
trafficlight.Cr1.South.red=35.0
trafficlight.Cr1.East.green=35.0
trafficlight.Cr1.East.red=35.0
# === TRAFFIC LIGHT TIMINGS === # Cruzamento 2 - hub principal
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds> trafficlight.Cr2.South.green=40.0
trafficlight.Cr2.South.red=40.0
trafficlight.Cr2.East.green=40.0
trafficlight.Cr2.East.red=40.0
trafficlight.Cr2.West.green=40.0
trafficlight.Cr2.West.red=40.0
# Intersection 1 (Entry point - balanced) # Cruzamento 3 - caminho para a saída
trafficlight.Cr1.South.green=60.0 trafficlight.Cr3.South.green=35.0
trafficlight.Cr1.South.red=5.0 trafficlight.Cr3.South.red=40.0
trafficlight.Cr1.East.green=60.0 trafficlight.Cr3.West.green=40.0
trafficlight.Cr1.East.red=5.0 trafficlight.Cr3.West.red=35.0
# Intersection 2 (Main hub - shorter cycles, favor East-West) # Cruzamento 4
trafficlight.Cr2.South.green=60.0 trafficlight.Cr4.East.green=35.0
trafficlight.Cr2.South.red=5.0 trafficlight.Cr4.East.red=35.0
trafficlight.Cr2.East.green=60.0
trafficlight.Cr2.East.red=5.0
trafficlight.Cr2.West.green=60.0
trafficlight.Cr2.West.red=5.0
# Intersection 3 (Path to exit - favor East) # Cruzamento 5 - perto da saída
trafficlight.Cr3.South.green=60.0 trafficlight.Cr5.East.green=35.0
trafficlight.Cr3.South.red=5.0 trafficlight.Cr5.East.red=35.0
trafficlight.Cr3.West.green=60.0
trafficlight.Cr3.West.red=5.0
# Intersection 4 (Favor East toward Cr5) # Configuração de veículos
trafficlight.Cr4.East.green=60.0
trafficlight.Cr4.East.red=5.0
# Intersection 5 (Near exit - favor East)
trafficlight.Cr5.East.green=60.0
trafficlight.Cr5.East.red=5.0
# === VEHICLE CONFIGURATION ===
# Probability distribution for vehicle types (must sum to 1.0)
vehicle.probability.bike=0.2 vehicle.probability.bike=0.2
vehicle.probability.light=0.6 vehicle.probability.light=0.6
vehicle.probability.heavy=0.2 vehicle.probability.heavy=0.2
# Average crossing times (in seconds)
vehicle.crossing.time.bike=1.0 vehicle.crossing.time.bike=1.0
vehicle.crossing.time.light=2.0 vehicle.crossing.time.light=2.0
vehicle.crossing.time.heavy=4.0 vehicle.crossing.time.heavy=4.0
# Travel times between intersections (in seconds)
# Base time for light vehicles (cars)
vehicle.travel.time.base=1.0 vehicle.travel.time.base=1.0
# Bike travel time = 0.5 × car travel time
vehicle.travel.time.bike.multiplier=0.5 vehicle.travel.time.bike.multiplier=0.5
# Heavy vehicle travel time = 4.0 x base travel time
vehicle.travel.time.heavy.multiplier=4.0 vehicle.travel.time.heavy.multiplier=4.0
# === STATISTICS ===
# Interval between dashboard updates (seconds)
statistics.update.interval=0.1 statistics.update.interval=0.1

File diff suppressed because it is too large Load Diff