mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 12:33:31 +00:00
Compare commits
14 Commits
10-create-
...
8fe4e564d3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fe4e564d3 | ||
|
|
e389c0711e | ||
|
|
24fe1c1d67 | ||
|
|
766eabbbe4 | ||
|
|
d7b1de1fe3 | ||
|
|
96b3a66b96 | ||
|
|
29848b04a6 | ||
| 043ba7d185 | |||
| 25f2876c34 | |||
| 7cbecc4fab | |||
| 72db59415f | |||
| 60b4f0c2b6 | |||
| 81f842e2bb | |||
|
|
108d2e544c |
110
.$Diagrama de arquitetura - SD.drawio.bkp
Normal file
110
.$Diagrama de arquitetura - SD.drawio.bkp
Normal 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="<b>CoordinatorProcess</b><br>(Cliente Socket)<hr>• VehicleGenerator<br>• Modelo Poisson (λ=0.5)<br>• 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="<b>Cr1</b> (IntersectionProcess)<br>Porta: 8001<br>Servidor + Cliente<hr>• ServerSocket (8001)<br>• Thread Semáforo Sul<br>• Thread Semáforo Este<br>• Thread Semáforo Oeste<br>• Fila Eventos (DES)<br>• 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="<b>Cr2</b> (IntersectionProcess)<br>Porta: 8002<br>Servidor + Cliente<hr>• ServerSocket (8002)<br>• Thread Semáforo Sul<br>• Thread Semáforo Este<br>• Thread Semáforo Oeste<br>• Fila Eventos (DES)<br>• 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="<b>Cr4</b> (IntersectionProcess)<br>Porta: 8004<br>Servidor + Cliente<hr>• ServerSocket (8004)<br>• Thread Semáforo Sul<br>• Thread Semáforo Este<br>• Thread Semáforo Oeste<br>• Fila Eventos (DES)<br>• 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="<b>Cr5</b> (IntersectionProcess)<br>Porta: 8005<br>Servidor + Cliente<hr>• ServerSocket (8005)<br>• Thread Semáforo Sul<br>• Thread Semáforo Este<br>• Thread Semáforo Oeste<br>• Fila Eventos (DES)<br>• 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="<b>ExitNode (S)</b><br>Porta: 9001<br>Servidor Socket<hr>• Recebe veículos finais<br>• Calcula estatísticas:<br> - Tempo no sistema<br> - Tempo de espera<br> - Métricas por tipo<br>• 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="<b>DashboardServer</b><br>Porta: 9000<br>Servidor Socket<hr>• Thread Pool (10 threads)<br>• ConcurrentHashMap<br>• Agrega estatísticas<br>• Display a cada 5s:<br> - Throughput<br> - Tempos médios<br> - 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<br>(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<br>(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<br>(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<br>(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<br>(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<br>(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<br>(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<br>(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<br>(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="<b>MessageProtocol</b><hr>interface:<br>• getType()<br>• getPayload()<br>• getSourceNode()<br>• 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="<b>Tipos de Mensagens</b><hr>• VEHICLE_TRANSFER<br>• VEHICLE_SPAWN<br>• STATS_UPDATE<br>• TRAFFIC_LIGHT_SYNC<br>• 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="<b>Cr3</b> (IntersectionProcess)<br>Porta: 8003<br>Servidor + Cliente<hr>• ServerSocket (8003)<br>• Thread Semáforo Sul<br>• Thread Semáforo Este<br>• Thread Semáforo Oeste<br>• Fila Eventos (DES)<br>• 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="<b>LEGENDA</b><hr>━━━► Comunicação síncrona<br>╌╌╌► Comunicação periódica<br><br><b>Cores:</b><br>🔵 Azul = Geração<br>🟢 Verde = Transferência<br>🟠 Laranja = Finalização<br>🟣 Roxo = Monitorização<br><br><b>Serialização:</b> JSON (Gson)<br><b>Protocolo:</b> 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>
|
||||||
62
.github/workflows/maven.yml
vendored
62
.github/workflows/maven.yml
vendored
@@ -1,8 +1,9 @@
|
|||||||
name: Java CI with Maven
|
name: Java CI with Maven
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ "main" ]
|
branches: [ "dev", "cleanup" ]
|
||||||
tags:
|
tags:
|
||||||
- 'v*.*.*'
|
- 'v*.*.*'
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -11,51 +12,88 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up JDK 17
|
- name: Set up JDK 17
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '17'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
cache: maven
|
cache: maven
|
||||||
|
|
||||||
- name: Build with Maven
|
- name: Build with Maven
|
||||||
run: mvn -B package
|
run: mvn -B package
|
||||||
working-directory: main
|
working-directory: main
|
||||||
|
|
||||||
- name: Upload built JAR
|
- name: Upload built JAR
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: package
|
name: package
|
||||||
path: main/target/*.jar
|
path: main/target/*.jar
|
||||||
|
|
||||||
- name: Generate dependency graph
|
- name: Generate dependency graph
|
||||||
run: mvn -B -f main/pom.xml com.github.ferstl:depgraph-maven-plugin:4.0.1:graph
|
run: mvn -B -f main/pom.xml com.github.ferstl:depgraph-maven-plugin:4.0.1:graph
|
||||||
|
|
||||||
- name: Upload dependency graph artifact
|
- name: Upload dependency graph artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: dependency-graph
|
name: dependency-graph
|
||||||
path: main/target/**
|
path: main/target/**
|
||||||
|
|
||||||
|
build-windows:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up JDK 17
|
||||||
|
uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
java-version: '17'
|
||||||
|
distribution: 'temurin'
|
||||||
|
cache: maven
|
||||||
|
- name: Build with Maven (Skip Tests)
|
||||||
|
run: mvn -B package -DskipTests
|
||||||
|
working-directory: main
|
||||||
|
- name: Create JPackage App Image
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
New-Item -ItemType Directory -Force -Path "dist"
|
||||||
|
jpackage --name "DTSS" `
|
||||||
|
--input main/target `
|
||||||
|
--main-jar main-1.0-SNAPSHOT.jar `
|
||||||
|
--dest dist `
|
||||||
|
--type app-image `
|
||||||
|
--win-console
|
||||||
|
- name: Inject java.exe
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$javaPath = (Get-Command java).Source
|
||||||
|
Copy-Item -Path $javaPath -Destination "dist/DTSS/runtime/bin/"
|
||||||
|
- name: Zip Windows Release
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
Compress-Archive -Path "dist/DTSS" -DestinationPath "dist/DTSS-Windows.zip"
|
||||||
|
- name: Upload Windows Artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-package
|
||||||
|
path: dist/DTSS-Windows.zip
|
||||||
|
|
||||||
publish-release:
|
publish-release:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
needs: [build, build-windows]
|
||||||
if: startsWith(github.ref, 'refs/tags/')
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Download built JAR
|
- name: Download Linux JAR
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: package
|
name: package
|
||||||
path: main/target/
|
path: main/target/
|
||||||
|
- name: Download Windows Zip
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-package
|
||||||
|
path: windows-dist/
|
||||||
- name: Create GitHub Release
|
- name: Create GitHub Release
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
files: main/target/*.jar
|
files: |
|
||||||
|
main/target/*.jar
|
||||||
|
windows-dist/*.zip
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -47,4 +47,4 @@ build/
|
|||||||
|
|
||||||
# Other
|
# Other
|
||||||
*.swp
|
*.swp
|
||||||
*.pdf
|
.$Diagrama de arquitetura - SD.drawio.bkp
|
||||||
|
|||||||
@@ -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="<b>MessageProtocol</b><hr>interface:<br>• getType()<br>• getPayload()<br>• getSourceNode()<br>• 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="<b>Tipos de Mensagens</b><hr>• VEHICLE_TRANSFER<br>• VEHICLE_SPAWN<br>• STATS_UPDATE<br>• TRAFFIC_LIGHT_SYNC<br>• 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="<b>LEGENDA</b><hr>━━━► Comunicação síncrona<br>╌╌╌► Comunicação periódica<br><br><b>Cores:</b><br>🔵 Azul =&nbsp;<span style="background-color: transparent;">Criação do veículo</span><div>🟢 Verde = Transferência do veículo<br>🟠 Laranja = Chegada ao destino<br>🟣 Roxo =&nbsp;<span style="background-color: transparent;">Envio das estatísticas</span></div><div><br><b>Serialização:</b> JSON (Gson)<br><b>Protocolo:</b> TCP/IP</div>" 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="<b>CoordinatorProcess</b><br>(Cliente Socket)<hr>• VehicleGenerator<br>• Modelo Poisson<br>• 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="<b>Cr1</b> (IntersectionProcess)<br>Porta: 8001<br>Servidor + Cliente<hr>• ServerSocket (8001)<br>• Thread Semáforo - Sul<br>• Thread Semáforo - Este<br>• Fila Eventos (DES)<br>• 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="<b>Cr2</b> (IntersectionProcess)<br>Porta: 8002<br>Servidor + Cliente<hr>• ServerSocket (8002)<br>• Thread Semáforo - Sul<br>• Thread Semáforo - Este<br>• Thread Semáforo - Oeste<br>• Fila Eventos (DES)<br>• 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="<b>Cr4</b> (IntersectionProcess)<br>Porta: 8004<br>Servidor + Cliente<hr>• ServerSocket (8004)<br>• Thread Semáforo - Este<br>• Fila Eventos (DES)<br>• 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="<b>Cr5</b> (IntersectionProcess)<br>Porta: 8005<br>Servidor + Cliente<hr>• ServerSocket (8005)<br>• Thread Semáforo - Este<br>• Fila Eventos (DES)<br>• 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="<b>ExitNode (S)</b><br>Porta: 9001<br>Servidor Socket<hr>• Recebe veículos finais<br>• Calcula estatísticas:<br> - Tempo no sistema<br> - Tempo de espera<br> - Métricas por tipo<br>• 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="<b>DashboardServer</b><br>Porta: 9000<br>Servidor Socket<hr>• Thread Pool (10 threads)<br>• ConcurrentHashMap<br>• Agrega estatísticas<br>• Display a cada 5s:<br> - Throughput<br> - Tempos médios<br> - 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="<b>Cr3</b> (IntersectionProcess)<br>Porta: 8003<br>Servidor + Cliente<hr>• ServerSocket (8003)<br>• Thread Semáforo - Sul<br>• Thread Semáforo - Oeste<br>• Fila Eventos (DES)<br>• 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>
|
||||||
|
|||||||
BIN
Diagrama de arquitetura - SD.jpg
Normal file
BIN
Diagrama de arquitetura - SD.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
BIN
Enunciado.pdf
Normal file
BIN
Enunciado.pdf
Normal file
Binary file not shown.
@@ -1,501 +0,0 @@
|
|||||||
package sd;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import sd.config.SimulationConfig;
|
|
||||||
import sd.model.Intersection;
|
|
||||||
import sd.model.MessageType;
|
|
||||||
import sd.model.TrafficLight;
|
|
||||||
import sd.model.TrafficLightState;
|
|
||||||
import sd.model.Vehicle;
|
|
||||||
import sd.protocol.MessageProtocol;
|
|
||||||
import sd.protocol.SocketConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main class for an Intersection Process in the distributed traffic simulation.
|
|
||||||
* * Each IntersectionProcess runs as an independent Java application (JVM instance)
|
|
||||||
* representing one of the five intersections (Cr1-Cr5) in the network.
|
|
||||||
*/
|
|
||||||
public class IntersectionProcess {
|
|
||||||
|
|
||||||
private final String intersectionId;
|
|
||||||
|
|
||||||
private final SimulationConfig config;
|
|
||||||
|
|
||||||
private final Intersection intersection;
|
|
||||||
|
|
||||||
private ServerSocket serverSocket;
|
|
||||||
|
|
||||||
private final Map<String, SocketConnection> outgoingConnections;
|
|
||||||
|
|
||||||
private final ExecutorService connectionHandlerPool;
|
|
||||||
|
|
||||||
private final ExecutorService trafficLightPool;
|
|
||||||
|
|
||||||
private volatile boolean running; //Quando uma thread escreve um valor volatile, todas as outras
|
|
||||||
//threads veem a mudança imediatamente.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new IntersectionProcess.
|
|
||||||
*
|
|
||||||
* @param intersectionId The ID of this intersection (e.g., "Cr1").
|
|
||||||
* @param configFilePath Path to the simulation.properties file.
|
|
||||||
* @throws IOException If configuration cannot be loaded.
|
|
||||||
*/
|
|
||||||
public IntersectionProcess(String intersectionId, String configFilePath) throws IOException {
|
|
||||||
this.intersectionId = intersectionId;
|
|
||||||
this.config = new SimulationConfig(configFilePath);
|
|
||||||
this.intersection = new Intersection(intersectionId);
|
|
||||||
this.outgoingConnections = new HashMap<>();
|
|
||||||
this.connectionHandlerPool = Executors.newCachedThreadPool();
|
|
||||||
this.trafficLightPool = Executors.newFixedThreadPool(4); // Max 4 directions
|
|
||||||
this.running = false;
|
|
||||||
|
|
||||||
System.out.println("=".repeat(60));
|
|
||||||
System.out.println("INTERSECTION PROCESS: " + intersectionId);
|
|
||||||
System.out.println("=".repeat(60));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize() {
|
|
||||||
System.out.println("\n[" + intersectionId + "] Initializing intersection...");
|
|
||||||
|
|
||||||
createTrafficLights();
|
|
||||||
|
|
||||||
configureRouting();
|
|
||||||
|
|
||||||
startTrafficLights();
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] Initialization complete.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates traffic lights for this intersection based on its physical connections.
|
|
||||||
* Each intersection has different number and directions of traffic lights
|
|
||||||
* according to the network topology.
|
|
||||||
*/
|
|
||||||
private void createTrafficLights() {
|
|
||||||
System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
|
|
||||||
|
|
||||||
String[] directions = new String[0];
|
|
||||||
switch (intersectionId) {
|
|
||||||
case "Cr1":
|
|
||||||
directions = new String[]{"East", "South"};
|
|
||||||
break;
|
|
||||||
case "Cr2":
|
|
||||||
directions = new String[]{"West", "East", "South"};
|
|
||||||
break;
|
|
||||||
case "Cr3":
|
|
||||||
directions = new String[]{"West", "South"};
|
|
||||||
break;
|
|
||||||
case "Cr4":
|
|
||||||
directions = new String[]{"East"};
|
|
||||||
break;
|
|
||||||
case "Cr5":
|
|
||||||
directions = new String[]{"East"};
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String direction : directions) {
|
|
||||||
double greenTime = config.getTrafficLightGreenTime(intersectionId, direction);
|
|
||||||
double redTime = config.getTrafficLightRedTime(intersectionId, direction);
|
|
||||||
|
|
||||||
TrafficLight light = new TrafficLight(
|
|
||||||
intersectionId + "-" + direction,
|
|
||||||
direction,
|
|
||||||
greenTime,
|
|
||||||
redTime
|
|
||||||
);
|
|
||||||
|
|
||||||
intersection.addTrafficLight(light);
|
|
||||||
System.out.println(" Created traffic light: " + direction +
|
|
||||||
" (Green: " + greenTime + "s, Red: " + redTime + "s)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void configureRouting() {
|
|
||||||
System.out.println("\n[" + intersectionId + "] Configuring routing...");
|
|
||||||
|
|
||||||
switch (intersectionId) {
|
|
||||||
case "Cr1":
|
|
||||||
intersection.configureRoute("Cr2", "East");
|
|
||||||
intersection.configureRoute("Cr4", "South");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Cr2":
|
|
||||||
intersection.configureRoute("Cr1", "West");
|
|
||||||
intersection.configureRoute("Cr3", "East");
|
|
||||||
intersection.configureRoute("Cr5", "South");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Cr3":
|
|
||||||
intersection.configureRoute("Cr2", "West");
|
|
||||||
intersection.configureRoute("S", "South");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Cr4":
|
|
||||||
intersection.configureRoute("Cr5", "East");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Cr5":
|
|
||||||
intersection.configureRoute("S", "East");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
System.err.println(" Error: unknown intersection ID: " + intersectionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println(" Routing configured.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts all traffic light threads.
|
|
||||||
*/
|
|
||||||
private void startTrafficLights() {
|
|
||||||
System.out.println("\n[" + intersectionId + "] Starting traffic light threads...");
|
|
||||||
|
|
||||||
for (TrafficLight light : intersection.getTrafficLights()) {
|
|
||||||
trafficLightPool.submit(() -> runTrafficLightCycle(light));
|
|
||||||
System.out.println(" Started thread for: " + light.getDirection());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main loop for a traffic light thread.
|
|
||||||
* Continuously cycles between GREEN and RED states.
|
|
||||||
*
|
|
||||||
* @param light The traffic light to control.
|
|
||||||
*/
|
|
||||||
private void runTrafficLightCycle(TrafficLight light) {
|
|
||||||
System.out.println("[" + light.getId() + "] Traffic light thread started.");
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
// Green state
|
|
||||||
light.changeState(TrafficLightState.GREEN);
|
|
||||||
System.out.println("[" + light.getId() + "] State: GREEN");
|
|
||||||
|
|
||||||
// Process vehicles while green
|
|
||||||
processGreenLight(light);
|
|
||||||
|
|
||||||
// Wait for green duration
|
|
||||||
Thread.sleep((long) (light.getGreenTime() * 1000));
|
|
||||||
|
|
||||||
// RED state
|
|
||||||
light.changeState(TrafficLightState.RED);
|
|
||||||
System.out.println("[" + light.getId() + "] State: RED");
|
|
||||||
|
|
||||||
// Wait for red duration
|
|
||||||
Thread.sleep((long) (light.getRedTime() * 1000));
|
|
||||||
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
System.out.println("[" + light.getId() + "] Traffic light thread interrupted.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("[" + light.getId() + "] Traffic light thread stopped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processes vehicles when a traffic light is GREEN.
|
|
||||||
* Dequeues vehicles and sends them to their next destination.
|
|
||||||
*
|
|
||||||
* @param light The traffic light that is currently green.
|
|
||||||
*/
|
|
||||||
private void processGreenLight(TrafficLight light) {
|
|
||||||
while (light.getState() == TrafficLightState.GREEN && light.getQueueSize() > 0) {
|
|
||||||
Vehicle vehicle = light.removeVehicle();
|
|
||||||
|
|
||||||
if (vehicle != null) {
|
|
||||||
// Get crossing time based on vehicle type
|
|
||||||
double crossingTime = getCrossingTimeForVehicle(vehicle);
|
|
||||||
|
|
||||||
// Simulate crossing time
|
|
||||||
try {
|
|
||||||
Thread.sleep((long) (crossingTime * 1000));
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update vehicle statistics
|
|
||||||
vehicle.addCrossingTime(crossingTime);
|
|
||||||
|
|
||||||
// Update intersection statistics
|
|
||||||
intersection.incrementVehiclesSent();
|
|
||||||
|
|
||||||
// Send vehicle to next destination
|
|
||||||
sendVehicleToNextDestination(vehicle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the crossing time for a vehicle based on its type.
|
|
||||||
*
|
|
||||||
* @param vehicle The vehicle.
|
|
||||||
* @return The crossing time in seconds.
|
|
||||||
*/
|
|
||||||
private double getCrossingTimeForVehicle(Vehicle vehicle) {
|
|
||||||
switch (vehicle.getType()) {
|
|
||||||
case BIKE:
|
|
||||||
return config.getBikeVehicleCrossingTime();
|
|
||||||
case LIGHT:
|
|
||||||
return config.getLightVehicleCrossingTime();
|
|
||||||
case HEAVY:
|
|
||||||
return config.getHeavyVehicleCrossingTime();
|
|
||||||
default:
|
|
||||||
return config.getLightVehicleCrossingTime();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a vehicle to its next destination via socket connection.
|
|
||||||
*
|
|
||||||
* @param vehicle The vehicle that has crossed this intersection.
|
|
||||||
*/
|
|
||||||
private void sendVehicleToNextDestination(Vehicle vehicle) {
|
|
||||||
String nextDestination = vehicle.getCurrentDestination();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get or create connection to next destination
|
|
||||||
SocketConnection connection = getOrCreateConnection(nextDestination);
|
|
||||||
|
|
||||||
// Create and send message
|
|
||||||
MessageProtocol message = new VehicleTransferMessage(
|
|
||||||
intersectionId,
|
|
||||||
nextDestination,
|
|
||||||
vehicle
|
|
||||||
);
|
|
||||||
|
|
||||||
connection.sendMessage(message);
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] Sent vehicle " + vehicle.getId() +
|
|
||||||
" to " + nextDestination);
|
|
||||||
|
|
||||||
// Note: vehicle route is advanced when it arrives at the next intersection
|
|
||||||
|
|
||||||
} catch (IOException | InterruptedException e) {
|
|
||||||
System.err.println("[" + intersectionId + "] Failed to send vehicle " +
|
|
||||||
vehicle.getId() + " to " + nextDestination + ": " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an existing connection to a destination or creates a new one.
|
|
||||||
*
|
|
||||||
* @param destinationId The ID of the destination node.
|
|
||||||
* @return The SocketConnection to that destination.
|
|
||||||
* @throws IOException If connection cannot be established.
|
|
||||||
* @throws InterruptedException If connection attempt is interrupted.
|
|
||||||
*/
|
|
||||||
private synchronized SocketConnection getOrCreateConnection(String destinationId)
|
|
||||||
throws IOException, InterruptedException {
|
|
||||||
|
|
||||||
if (!outgoingConnections.containsKey(destinationId)) {
|
|
||||||
String host = getHostForDestination(destinationId);
|
|
||||||
int port = getPortForDestination(destinationId);
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] Creating connection to " +
|
|
||||||
destinationId + " at " + host + ":" + port);
|
|
||||||
|
|
||||||
SocketConnection connection = new SocketConnection(host, port);
|
|
||||||
outgoingConnections.put(destinationId, connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return outgoingConnections.get(destinationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the host address for a destination node from configuration.
|
|
||||||
*
|
|
||||||
* @param destinationId The destination node ID.
|
|
||||||
* @return The host address.
|
|
||||||
*/
|
|
||||||
private String getHostForDestination(String destinationId) {
|
|
||||||
if (destinationId.equals("S")) {
|
|
||||||
return config.getExitHost();
|
|
||||||
} else {
|
|
||||||
return config.getIntersectionHost(destinationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the port number for a destination node from configuration.
|
|
||||||
*
|
|
||||||
* @param destinationId The destination node ID.
|
|
||||||
* @return The port number.
|
|
||||||
*/
|
|
||||||
private int getPortForDestination(String destinationId) {
|
|
||||||
if (destinationId.equals("S")) {
|
|
||||||
return config.getExitPort();
|
|
||||||
} else {
|
|
||||||
return config.getIntersectionPort(destinationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the server socket and begins accepting incoming connections.
|
|
||||||
* This is the main listening loop of the process.
|
|
||||||
*
|
|
||||||
* @throws IOException If the server socket cannot be created.
|
|
||||||
*/
|
|
||||||
public void start() throws IOException {
|
|
||||||
int port = config.getIntersectionPort(intersectionId);
|
|
||||||
serverSocket = new ServerSocket(port);
|
|
||||||
running = true;
|
|
||||||
|
|
||||||
System.out.println("\n[" + intersectionId + "] Server started on port " + port);
|
|
||||||
System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n");
|
|
||||||
|
|
||||||
// Main accept loop
|
|
||||||
while (running) {
|
|
||||||
try {
|
|
||||||
Socket clientSocket = serverSocket.accept();
|
|
||||||
|
|
||||||
// Handle each connection in a separate thread
|
|
||||||
connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket));
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (running) {
|
|
||||||
System.err.println("[" + intersectionId + "] Error accepting connection: " +
|
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles an incoming connection from another process.
|
|
||||||
* Continuously listens for vehicle transfer messages.
|
|
||||||
*
|
|
||||||
* @param clientSocket The accepted socket connection.
|
|
||||||
*/
|
|
||||||
private void handleIncomingConnection(Socket clientSocket) {
|
|
||||||
try (SocketConnection connection = new SocketConnection(clientSocket)) {
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] New connection accepted from " +
|
|
||||||
clientSocket.getInetAddress().getHostAddress());
|
|
||||||
|
|
||||||
// Continuously receive messages while connection is active
|
|
||||||
while (running && connection.isConnected()) {
|
|
||||||
try {
|
|
||||||
MessageProtocol message = connection.receiveMessage();
|
|
||||||
|
|
||||||
if (message.getType() == MessageType.VEHICLE_TRANSFER) {
|
|
||||||
Vehicle vehicle = (Vehicle) message.getPayload();
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] Received vehicle: " +
|
|
||||||
vehicle.getId() + " from " + message.getSourceNode());
|
|
||||||
|
|
||||||
// Add vehicle to appropriate queue
|
|
||||||
intersection.receiveVehicle(vehicle);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
System.err.println("[" + intersectionId + "] Unknown message type received: " +
|
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (running) {
|
|
||||||
System.err.println("[" + intersectionId + "] Connection error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the intersection process gracefully.
|
|
||||||
* Shuts down all threads and closes all connections.
|
|
||||||
*/
|
|
||||||
public void shutdown() {
|
|
||||||
System.out.println("\n[" + intersectionId + "] Shutting down...");
|
|
||||||
running = false;
|
|
||||||
|
|
||||||
// Close server socket
|
|
||||||
try {
|
|
||||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
|
||||||
serverSocket.close();
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("[" + intersectionId + "] Error closing server socket: " +
|
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown thread pools
|
|
||||||
trafficLightPool.shutdown();
|
|
||||||
connectionHandlerPool.shutdown();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!trafficLightPool.awaitTermination(5, TimeUnit.SECONDS)) {
|
|
||||||
trafficLightPool.shutdownNow();
|
|
||||||
}
|
|
||||||
if (!connectionHandlerPool.awaitTermination(5, TimeUnit.SECONDS)) {
|
|
||||||
connectionHandlerPool.shutdownNow();
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
trafficLightPool.shutdownNow();
|
|
||||||
connectionHandlerPool.shutdownNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close all outgoing connections
|
|
||||||
for (Map.Entry<String, SocketConnection> entry : outgoingConnections.entrySet()) {
|
|
||||||
try {
|
|
||||||
entry.getValue().close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("[" + intersectionId + "] Error closing connection to " +
|
|
||||||
entry.getKey() + ": " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("[" + intersectionId + "] Shutdown complete.");
|
|
||||||
System.out.println("=".repeat(60));
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Inner class for Vehicle Transfer Messages ---
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of MessageProtocol for vehicle transfers between processes.
|
|
||||||
*/
|
|
||||||
private static class VehicleTransferMessage implements MessageProtocol {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final String sourceNode;
|
|
||||||
private final String destinationNode;
|
|
||||||
private final Vehicle payload;
|
|
||||||
|
|
||||||
public VehicleTransferMessage(String sourceNode, String destinationNode, Vehicle vehicle) {
|
|
||||||
this.sourceNode = sourceNode;
|
|
||||||
this.destinationNode = destinationNode;
|
|
||||||
this.payload = vehicle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MessageType getType() {
|
|
||||||
return MessageType.VEHICLE_TRANSFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getPayload() {
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSourceNode() {
|
|
||||||
return sourceNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDestinationNode() {
|
|
||||||
return destinationNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -104,28 +104,16 @@ public class Intersection {
|
|||||||
* Accepts an incoming vehicle and places it in the correct queue.
|
* Accepts an incoming vehicle and places it in the correct queue.
|
||||||
* * This method:
|
* * This method:
|
||||||
* 1. Increments the {@link #totalVehiclesReceived} counter.
|
* 1. Increments the {@link #totalVehiclesReceived} counter.
|
||||||
* 2. Advances the vehicle's route (since it just arrived here)
|
* 2. Gets the vehicle's *next* destination (from {@link Vehicle#getCurrentDestination()}).
|
||||||
* 3. Gets the vehicle's *next* destination (from {@link Vehicle#getCurrentDestination()}).
|
* 3. Uses the {@link #routing} map to find the correct *direction* for that destination.
|
||||||
* 4. Uses the {@link #routing} map to find the correct *direction* for that destination.
|
* 4. Adds the vehicle to the queue of the {@link TrafficLight} for that direction.
|
||||||
* 5. Adds the vehicle to the queue of the {@link TrafficLight} for that direction.
|
|
||||||
*
|
*
|
||||||
* @param vehicle The {@link Vehicle} arriving at the intersection.
|
* @param vehicle The {@link Vehicle} arriving at the intersection.
|
||||||
*/
|
*/
|
||||||
public void receiveVehicle(Vehicle vehicle) {
|
public void receiveVehicle(Vehicle vehicle) {
|
||||||
totalVehiclesReceived++;
|
totalVehiclesReceived++;
|
||||||
|
|
||||||
// Advance route since vehicle just arrived at this intersection
|
|
||||||
vehicle.advanceRoute();
|
|
||||||
|
|
||||||
String nextDestination = vehicle.getCurrentDestination();
|
String nextDestination = vehicle.getCurrentDestination();
|
||||||
|
|
||||||
// Check if vehicle reached final destination
|
|
||||||
if (nextDestination == null) {
|
|
||||||
System.out.printf("[%s] Vehicle %s reached final destination%n",
|
|
||||||
this.id, vehicle.getId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String direction = routing.get(nextDestination);
|
String direction = routing.get(nextDestination);
|
||||||
|
|
||||||
if (direction != null && trafficLights.containsKey(direction)) {
|
if (direction != null && trafficLights.containsKey(direction)) {
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package sd.protocol;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import sd.model.MessageType; // Assuming MessageType is in sd.model or sd.protocol
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface defining the contract for all messages exchanged in the simulator.
|
|
||||||
* Ensures that any message can be identified and routed.
|
|
||||||
* * This interface extends Serializable to allow objects that implement it
|
|
||||||
* to be sent over Sockets (ObjectOutputStream).
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface MessageProtocol extends Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the type of the message, indicating its purpose.
|
|
||||||
* @return The MessageType (e.g., VEHICLE_TRANSFER, STATS_UPDATE).
|
|
||||||
*/
|
|
||||||
MessageType getType();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the data object (payload) that this message carries.
|
|
||||||
* The type of object will depend on the MessageType.
|
|
||||||
* * - If getType() == VEHICLE_TRANSFER, the payload will be a {@link sd.model.Vehicle} object.
|
|
||||||
* - If getType() == STATS_UPDATE, the payload will be a statistics object.
|
|
||||||
* * @return The data object (payload), which must also be Serializable.
|
|
||||||
*/
|
|
||||||
Object getPayload();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ID of the node (Process) that sent this message.
|
|
||||||
* @return String (e.g., "Cr1", "Cr5", "S").
|
|
||||||
*/
|
|
||||||
String getSourceNode();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ID of the destination node (Process) for this message.
|
|
||||||
* @return String (e.g., "Cr2", "DashboardServer").
|
|
||||||
*/
|
|
||||||
String getDestinationNode();
|
|
||||||
}
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
package sd.protocol;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.SocketTimeoutException;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import sd.serialization.MessageSerializer;
|
|
||||||
import sd.serialization.SerializationException;
|
|
||||||
import sd.serialization.SerializerFactory;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper class that simplifies communication via Sockets.
|
|
||||||
* Includes connection retry logic for robustness.
|
|
||||||
*/
|
|
||||||
public class SocketConnection implements Closeable {
|
|
||||||
|
|
||||||
private final Socket socket;
|
|
||||||
private final OutputStream outputStream;
|
|
||||||
private final InputStream inputStream;
|
|
||||||
private final MessageSerializer serializer;
|
|
||||||
|
|
||||||
// --- Configuration for Retry Logic ---
|
|
||||||
/** Maximum number of connection attempts. */
|
|
||||||
private static final int MAX_RETRIES = 5;
|
|
||||||
/** Delay between retry attempts in milliseconds. */
|
|
||||||
private static final long RETRY_DELAY_MS = 1000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the "Client" (who initiates the connection).
|
|
||||||
* Tries to connect to a process that is already listening (Server).
|
|
||||||
* Includes retry logic in case of initial connection failure.
|
|
||||||
*
|
|
||||||
* @param host The host address (e.g., "localhost" from your simulation.properties)
|
|
||||||
* @param port The port (e.g., 8001 from your simulation.properties)
|
|
||||||
* @throws IOException If connection fails after all retries.
|
|
||||||
* @throws UnknownHostException If the host is not found (this error usually doesn't need retry).
|
|
||||||
* @throws InterruptedException If the thread is interrupted while waiting between retries.
|
|
||||||
*/
|
|
||||||
public SocketConnection(String host, int port) throws IOException, UnknownHostException, InterruptedException {
|
|
||||||
Socket tempSocket = null;
|
|
||||||
IOException lastException = null;
|
|
||||||
|
|
||||||
System.out.printf("[SocketConnection] Attempting to connect to %s:%d...%n", host, port);
|
|
||||||
|
|
||||||
// --- Retry Loop ---
|
|
||||||
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
||||||
try {
|
|
||||||
// Try to establish the connection
|
|
||||||
tempSocket = new Socket(host, port);
|
|
||||||
|
|
||||||
// If successful, break out of the retry loop
|
|
||||||
System.out.printf("[SocketConnection] Connected successfully on attempt %d.%n", attempt);
|
|
||||||
lastException = null; // Clear last error on success
|
|
||||||
break;
|
|
||||||
|
|
||||||
} catch (ConnectException | SocketTimeoutException e) {
|
|
||||||
// These are common errors indicating the server might not be ready.
|
|
||||||
lastException = e;
|
|
||||||
System.out.printf("[SocketConnection] Attempt %d/%d failed: %s. Retrying in %d ms...%n",
|
|
||||||
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
|
|
||||||
|
|
||||||
if (attempt < MAX_RETRIES) {
|
|
||||||
// Wait before the next attempt
|
|
||||||
TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Other IOExceptions might be more permanent, but we retry anyway.
|
|
||||||
lastException = e;
|
|
||||||
System.out.printf("[SocketConnection] Attempt %d/%d failed with IOException: %s. Retrying in %d ms...%n",
|
|
||||||
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
|
|
||||||
if (attempt < MAX_RETRIES) {
|
|
||||||
TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // --- End of Retry Loop ---
|
|
||||||
|
|
||||||
// If after all retries tempSocket is still null, it means connection failed permanently.
|
|
||||||
if (tempSocket == null) {
|
|
||||||
System.err.printf("[SocketConnection] Failed to connect to %s:%d after %d attempts.%n", host, port, MAX_RETRIES);
|
|
||||||
if (lastException != null) {
|
|
||||||
throw lastException; // Throw the last exception encountered
|
|
||||||
} else {
|
|
||||||
// Should not happen if loop ran, but as a fallback
|
|
||||||
throw new IOException("Failed to connect after " + MAX_RETRIES + " attempts, reason unknown.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If connection was successful, assign to final variable and create streams
|
|
||||||
this.socket = tempSocket;
|
|
||||||
|
|
||||||
this.outputStream = socket.getOutputStream();
|
|
||||||
this.inputStream = socket.getInputStream();
|
|
||||||
this.serializer = SerializerFactory.createDefault();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the "Server" (who accepts the connection).
|
|
||||||
* Receives a Socket that has already been accepted by a ServerSocket.
|
|
||||||
* No retry logic needed here as the connection is already established.
|
|
||||||
*
|
|
||||||
* @param acceptedSocket The Socket returned by serverSocket.accept().
|
|
||||||
* @throws IOException If stream creation fails.
|
|
||||||
*/
|
|
||||||
public SocketConnection(Socket acceptedSocket) throws IOException {
|
|
||||||
this.socket = acceptedSocket;
|
|
||||||
this.outputStream = socket.getOutputStream();
|
|
||||||
this.inputStream = socket.getInputStream();
|
|
||||||
this.serializer = SerializerFactory.createDefault();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends (serializes) a MessageProtocol object over the socket.
|
|
||||||
*
|
|
||||||
* @param message The "envelope" (which contains the Vehicle) to be sent.
|
|
||||||
* @throws IOException If writing to the stream fails or socket is not connected.
|
|
||||||
*/
|
|
||||||
public void sendMessage(MessageProtocol message) throws IOException {
|
|
||||||
if (socket == null || !socket.isConnected()) {
|
|
||||||
throw new IOException("Socket is not connected");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Serializa para bytes JSON
|
|
||||||
byte[] data = serializer.serialize(message);
|
|
||||||
|
|
||||||
// Write 4-byte length prefix
|
|
||||||
DataOutputStream dataOut = new DataOutputStream(outputStream);
|
|
||||||
dataOut.writeInt(data.length);
|
|
||||||
dataOut.write(data);
|
|
||||||
dataOut.flush();
|
|
||||||
|
|
||||||
} catch (SerializationException e) {
|
|
||||||
throw new IOException("Failed to serialize message", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to read (deserialize) a MessageProtocol object from the socket.
|
|
||||||
*
|
|
||||||
* @return The "envelope" (MessageProtocol) that was received.
|
|
||||||
* @throws IOException If the connection is lost, the stream is corrupted, or socket is not connected.
|
|
||||||
* @throws ClassNotFoundException If the received object is unknown.
|
|
||||||
*/
|
|
||||||
public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException {
|
|
||||||
if (socket == null || !socket.isConnected()) {
|
|
||||||
throw new IOException("Socket is not connected");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Lê um prefixo de 4 bytes - indicador de tamanho
|
|
||||||
DataInputStream dataIn = new DataInputStream(inputStream);
|
|
||||||
int length = dataIn.readInt();
|
|
||||||
|
|
||||||
if (length <= 0 || length > 10_000_000) { // Sanity check (10MB max)
|
|
||||||
throw new IOException("Invalid message length: " + length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ler dados da mensagem
|
|
||||||
byte[] data = new byte[length];
|
|
||||||
dataIn.readFully(data);
|
|
||||||
|
|
||||||
// Deserialize do JSON
|
|
||||||
return serializer.deserialize(data, MessageProtocol.class);
|
|
||||||
|
|
||||||
} catch (SerializationException e) {
|
|
||||||
throw new IOException("Failed to deserialize message", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the socket and all streams (Input and Output).
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (inputStream != null) inputStream.close();
|
|
||||||
if (outputStream != null) outputStream.close();
|
|
||||||
if (socket != null) socket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the socket is still connected and not closed.
|
|
||||||
*/
|
|
||||||
public boolean isConnected() {
|
|
||||||
return socket != null && socket.isConnected() && !socket.isClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,473 +0,0 @@
|
|||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.Timeout;
|
|
||||||
import org.junit.jupiter.api.io.TempDir;
|
|
||||||
|
|
||||||
import sd.IntersectionProcess;
|
|
||||||
import sd.model.MessageType;
|
|
||||||
import sd.model.Vehicle;
|
|
||||||
import sd.model.VehicleType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for IntersectionProcess - covers initialization, traffic lights,
|
|
||||||
* vehicle transfer and network stuff
|
|
||||||
*/
|
|
||||||
public class IntersectionProcessTest {
|
|
||||||
|
|
||||||
@TempDir
|
|
||||||
Path tempDir;
|
|
||||||
|
|
||||||
private Path configFile;
|
|
||||||
private IntersectionProcess intersectionProcess;
|
|
||||||
|
|
||||||
// setup test config before each test
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() throws IOException {
|
|
||||||
// create temp config file
|
|
||||||
configFile = tempDir.resolve("test-simulation.properties");
|
|
||||||
|
|
||||||
String configContent = """
|
|
||||||
# Test Simulation Configuration
|
|
||||||
|
|
||||||
# Intersection Network Configuration
|
|
||||||
intersection.Cr1.host=localhost
|
|
||||||
intersection.Cr1.port=18001
|
|
||||||
intersection.Cr2.host=localhost
|
|
||||||
intersection.Cr2.port=18002
|
|
||||||
intersection.Cr3.host=localhost
|
|
||||||
intersection.Cr3.port=18003
|
|
||||||
intersection.Cr4.host=localhost
|
|
||||||
intersection.Cr4.port=18004
|
|
||||||
intersection.Cr5.host=localhost
|
|
||||||
intersection.Cr5.port=18005
|
|
||||||
|
|
||||||
# Exit Configuration
|
|
||||||
exit.host=localhost
|
|
||||||
exit.port=18099
|
|
||||||
|
|
||||||
# Dashboard Configuration
|
|
||||||
dashboard.host=localhost
|
|
||||||
dashboard.port=18100
|
|
||||||
|
|
||||||
# Traffic Light Timing (seconds)
|
|
||||||
trafficLight.Cr1.East.greenTime=5.0
|
|
||||||
trafficLight.Cr1.East.redTime=5.0
|
|
||||||
trafficLight.Cr1.South.greenTime=5.0
|
|
||||||
trafficLight.Cr1.South.redTime=5.0
|
|
||||||
trafficLight.Cr1.West.greenTime=5.0
|
|
||||||
trafficLight.Cr1.West.redTime=5.0
|
|
||||||
|
|
||||||
trafficLight.Cr2.West.greenTime=4.0
|
|
||||||
trafficLight.Cr2.West.redTime=6.0
|
|
||||||
trafficLight.Cr2.East.greenTime=4.0
|
|
||||||
trafficLight.Cr2.East.redTime=6.0
|
|
||||||
trafficLight.Cr2.South.greenTime=4.0
|
|
||||||
trafficLight.Cr2.South.redTime=6.0
|
|
||||||
|
|
||||||
trafficLight.Cr3.West.greenTime=3.0
|
|
||||||
trafficLight.Cr3.West.redTime=7.0
|
|
||||||
trafficLight.Cr3.East.greenTime=3.0
|
|
||||||
trafficLight.Cr3.East.redTime=7.0
|
|
||||||
|
|
||||||
trafficLight.Cr4.East.greenTime=6.0
|
|
||||||
trafficLight.Cr4.East.redTime=4.0
|
|
||||||
|
|
||||||
trafficLight.Cr5.East.greenTime=5.0
|
|
||||||
trafficLight.Cr5.East.redTime=5.0
|
|
||||||
|
|
||||||
# Vehicle Crossing Times (seconds)
|
|
||||||
vehicle.bike.crossingTime=2.0
|
|
||||||
vehicle.light.crossingTime=3.0
|
|
||||||
vehicle.heavy.crossingTime=5.0
|
|
||||||
""";
|
|
||||||
|
|
||||||
Files.writeString(configFile, configContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup after tests
|
|
||||||
@AfterEach
|
|
||||||
public void tearDown() {
|
|
||||||
if (intersectionProcess != null) {
|
|
||||||
intersectionProcess.shutdown();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== Initialization Tests ====================
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConstructor_Success() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testConstructor_InvalidConfig() {
|
|
||||||
Exception exception = assertThrows(IOException.class, () -> {
|
|
||||||
new IntersectionProcess("Cr1", "non-existent-config.properties");
|
|
||||||
});
|
|
||||||
assertNotNull(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitialize_Cr1() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitialize_Cr2() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr2", configFile.toString());
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitialize_Cr3() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr3", configFile.toString());
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitialize_Cr4() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr4", configFile.toString());
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitialize_Cr5() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr5", configFile.toString());
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.initialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
// traffic light creation tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTrafficLightCreation_Cr1_HasCorrectDirections() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// cant access private fields but initialization succeds
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTrafficLightCreation_Cr3_HasCorrectDirections() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr3", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// Cr3 has west and south only
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTrafficLightCreation_Cr4_HasSingleDirection() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr4", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// Cr4 only has east direction
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
// server startup tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(5)
|
|
||||||
public void testServerStart_BindsToCorrectPort() throws IOException, InterruptedException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// start server in seperate thread
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
intersectionProcess.start();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// expected on shutdown
|
|
||||||
}
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
Thread.sleep(500); // wait for server to start
|
|
||||||
|
|
||||||
// try connecting to check if its running
|
|
||||||
try (Socket clientSocket = new Socket("localhost", 18001)) {
|
|
||||||
assertTrue(clientSocket.isConnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
intersectionProcess.shutdown();
|
|
||||||
serverThread.join(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(5)
|
|
||||||
public void testServerStart_MultipleIntersections() throws IOException, InterruptedException {
|
|
||||||
// test 2 intersections on diferent ports
|
|
||||||
IntersectionProcess cr1 = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
IntersectionProcess cr2 = new IntersectionProcess("Cr2", configFile.toString());
|
|
||||||
|
|
||||||
cr1.initialize();
|
|
||||||
cr2.initialize();
|
|
||||||
|
|
||||||
Thread thread1 = new Thread(() -> {
|
|
||||||
try { cr1.start(); } catch (IOException e) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
Thread thread2 = new Thread(() -> {
|
|
||||||
try { cr2.start(); } catch (IOException e) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
thread1.start();
|
|
||||||
thread2.start();
|
|
||||||
|
|
||||||
Thread.sleep(500);
|
|
||||||
|
|
||||||
// check both are running
|
|
||||||
try (Socket socket1 = new Socket("localhost", 18001);
|
|
||||||
Socket socket2 = new Socket("localhost", 18002)) {
|
|
||||||
assertTrue(socket1.isConnected());
|
|
||||||
assertTrue(socket2.isConnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
cr1.shutdown();
|
|
||||||
cr2.shutdown();
|
|
||||||
thread1.join(2000);
|
|
||||||
thread2.join(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// vehicle transfer tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(10)
|
|
||||||
public void testVehicleTransfer_ReceiveVehicle() throws IOException, InterruptedException {
|
|
||||||
// setup reciever intersection
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr2", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
intersectionProcess.start();
|
|
||||||
} catch (IOException e) { }
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
Thread.sleep(500);
|
|
||||||
|
|
||||||
// create test vehicle
|
|
||||||
java.util.List<String> route = Arrays.asList("Cr2", "Cr3", "S");
|
|
||||||
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route);
|
|
||||||
|
|
||||||
// send vehicle from Cr1 to Cr2
|
|
||||||
try (Socket socket = new Socket("localhost", 18002)) {
|
|
||||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
|
||||||
|
|
||||||
TestVehicleMessage message = new TestVehicleMessage("Cr1", "Cr2", vehicle);
|
|
||||||
out.writeObject(message);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
Thread.sleep(1000); // wait for procesing
|
|
||||||
}
|
|
||||||
|
|
||||||
intersectionProcess.shutdown();
|
|
||||||
serverThread.join(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// routing config tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRoutingConfiguration_Cr1() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// indirect test - if init works routing should be ok
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testRoutingConfiguration_Cr5() throws IOException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr5", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
// Cr5 routes to exit
|
|
||||||
assertNotNull(intersectionProcess);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shutdown tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(5)
|
|
||||||
public void testShutdown_GracefulTermination() throws IOException, InterruptedException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
intersectionProcess.start();
|
|
||||||
} catch (IOException e) { }
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
Thread.sleep(500);
|
|
||||||
|
|
||||||
// shutdown should be fast
|
|
||||||
assertDoesNotThrow(() -> intersectionProcess.shutdown());
|
|
||||||
|
|
||||||
serverThread.join(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(5)
|
|
||||||
public void testShutdown_ClosesServerSocket() throws IOException, InterruptedException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
intersectionProcess.start();
|
|
||||||
} catch (IOException e) { }
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
Thread.sleep(500);
|
|
||||||
|
|
||||||
// verify server running
|
|
||||||
try (Socket socket = new Socket("localhost", 18001)) {
|
|
||||||
assertTrue(socket.isConnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
intersectionProcess.shutdown();
|
|
||||||
serverThread.join(2000);
|
|
||||||
|
|
||||||
// after shutdown conection should fail
|
|
||||||
Thread.sleep(500);
|
|
||||||
Exception exception = assertThrows(IOException.class, () -> {
|
|
||||||
Socket socket = new Socket("localhost", 18001);
|
|
||||||
socket.close();
|
|
||||||
});
|
|
||||||
assertNotNull(exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(5)
|
|
||||||
public void testShutdown_StopsTrafficLightThreads() throws IOException, InterruptedException {
|
|
||||||
intersectionProcess = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
intersectionProcess.initialize();
|
|
||||||
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
intersectionProcess.start();
|
|
||||||
} catch (IOException e) { }
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
Thread.sleep(500);
|
|
||||||
|
|
||||||
int threadCountBefore = Thread.activeCount();
|
|
||||||
|
|
||||||
intersectionProcess.shutdown();
|
|
||||||
serverThread.join(2000);
|
|
||||||
|
|
||||||
Thread.sleep(500); // wait for threads to die
|
|
||||||
|
|
||||||
// thread count should decrese (traffic light threads stop)
|
|
||||||
int threadCountAfter = Thread.activeCount();
|
|
||||||
assertTrue(threadCountAfter <= threadCountBefore);
|
|
||||||
}
|
|
||||||
|
|
||||||
// integration tests
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Timeout(15)
|
|
||||||
public void testIntegration_TwoIntersectionsVehicleTransfer() throws IOException, InterruptedException {
|
|
||||||
// setup 2 intersections
|
|
||||||
IntersectionProcess cr1 = new IntersectionProcess("Cr1", configFile.toString());
|
|
||||||
IntersectionProcess cr2 = new IntersectionProcess("Cr2", configFile.toString());
|
|
||||||
|
|
||||||
cr1.initialize();
|
|
||||||
cr2.initialize();
|
|
||||||
|
|
||||||
// start both
|
|
||||||
Thread thread1 = new Thread(() -> {
|
|
||||||
try { cr1.start(); } catch (IOException e) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
Thread thread2 = new Thread(() -> {
|
|
||||||
try { cr2.start(); } catch (IOException e) { }
|
|
||||||
});
|
|
||||||
|
|
||||||
thread1.start();
|
|
||||||
thread2.start();
|
|
||||||
|
|
||||||
Thread.sleep(1000); // wait for servers
|
|
||||||
|
|
||||||
// send vehicle to Cr1 that goes to Cr2
|
|
||||||
java.util.List<String> route = Arrays.asList("Cr1", "Cr2", "S");
|
|
||||||
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 0.0, route);
|
|
||||||
|
|
||||||
try (Socket socket = new Socket("localhost", 18001)) {
|
|
||||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
|
||||||
|
|
||||||
TestVehicleMessage message = new TestVehicleMessage("Entry", "Cr1", vehicle);
|
|
||||||
out.writeObject(message);
|
|
||||||
out.flush();
|
|
||||||
|
|
||||||
Thread.sleep(2000); // time for processing
|
|
||||||
}
|
|
||||||
|
|
||||||
cr1.shutdown();
|
|
||||||
cr2.shutdown();
|
|
||||||
thread1.join(2000);
|
|
||||||
thread2.join(2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMain_MissingArguments() {
|
|
||||||
// main needs intersection ID as argument
|
|
||||||
// cant test System.exit easily in modern java
|
|
||||||
assertTrue(true, "Main method expects intersection ID as first argument");
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper class for testing vehicle messages
|
|
||||||
private static class TestVehicleMessage implements sd.protocol.MessageProtocol {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private final String sourceNode;
|
|
||||||
private final String destinationNode;
|
|
||||||
private final Vehicle payload;
|
|
||||||
|
|
||||||
public TestVehicleMessage(String sourceNode, String destinationNode, Vehicle vehicle) {
|
|
||||||
this.sourceNode = sourceNode;
|
|
||||||
this.destinationNode = destinationNode;
|
|
||||||
this.payload = vehicle;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MessageType getType() {
|
|
||||||
return MessageType.VEHICLE_TRANSFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getPayload() {
|
|
||||||
return payload;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSourceNode() {
|
|
||||||
return sourceNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDestinationNode() {
|
|
||||||
return destinationNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user