mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 20:43:32 +00:00
Compare commits
26 Commits
dash_test
...
snapshot-b
| Author | SHA1 | Date | |
|---|---|---|---|
| 043ba7d185 | |||
| 25f2876c34 | |||
| 7cbecc4fab | |||
| 72db59415f | |||
| 60b4f0c2b6 | |||
| 81f842e2bb | |||
|
|
108d2e544c | ||
| 23f7a74798 | |||
| d7dec0d73e | |||
|
|
534a880e3e | ||
|
|
ba3233eae1 | ||
|
|
d20040835c | ||
|
|
2399b4b472 | ||
|
|
974debf7db | ||
| 8e95bc4c01 | |||
| 33ed84b0c2 | |||
| 9093b13c5d | |||
| 12b7aabe87 | |||
| c30aa25de0 | |||
| 3689f7a207 | |||
| bb18c1119e | |||
| f0dbdb551d | |||
| f519c9aba7 | |||
| 1216089e80 | |||
| 211ea25ca5 | |||
|
|
af9b091e76 |
99
.github/workflows/maven.yml
vendored
Normal file
99
.github/workflows/maven.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
name: Java CI with Maven
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ "dev", "cleanup" ]
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-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
|
||||||
|
run: mvn -B package
|
||||||
|
working-directory: main
|
||||||
|
- name: Upload built JAR
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: package
|
||||||
|
path: main/target/*.jar
|
||||||
|
- name: Generate dependency graph
|
||||||
|
run: mvn -B -f main/pom.xml com.github.ferstl:depgraph-maven-plugin:4.0.1:graph
|
||||||
|
- name: Upload dependency graph artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: dependency-graph
|
||||||
|
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:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build, build-windows]
|
||||||
|
if: startsWith(github.ref, 'refs/tags/')
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- name: Download Linux JAR
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: package
|
||||||
|
path: main/target/
|
||||||
|
- name: Download Windows Zip
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: windows-package
|
||||||
|
path: windows-dist/
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: |
|
||||||
|
main/target/*.jar
|
||||||
|
windows-dist/*.zip
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -47,4 +47,3 @@ build/
|
|||||||
|
|
||||||
# Other
|
# Other
|
||||||
*.swp
|
*.swp
|
||||||
*.pdf
|
|
||||||
BIN
Enunciado.pdf
Normal file
BIN
Enunciado.pdf
Normal file
Binary file not shown.
620
README.md
Normal file
620
README.md
Normal file
@@ -0,0 +1,620 @@
|
|||||||
|
# Sistema de Simulação de Tráfego Distribuído
|
||||||
|
|
||||||
|
Sistema distribuído de simulação de tráfego.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Índice
|
||||||
|
|
||||||
|
- [Visão Geral](#visão-geral)
|
||||||
|
- [Arquitetura](#arquitetura)
|
||||||
|
- [Protocolo de Comunicação](#protocolo-de-comunicação)
|
||||||
|
- [Estrutura do Projeto](#estrutura-do-projeto)
|
||||||
|
- [Instalação e Execução](#instalação-e-execução)
|
||||||
|
- [Documentação](#documentação)
|
||||||
|
- [Desenvolvimento](#desenvolvimento)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Visão Geral
|
||||||
|
|
||||||
|
Este projeto implementa uma simulação distribuída de tráfego veicular numa rede de cruzamentos. O sistema utiliza:
|
||||||
|
|
||||||
|
- **Processos independentes** para cada cruzamento
|
||||||
|
- **Threads** para controlar os semáforos dentro de cada cruzamento
|
||||||
|
- **Comunicação via sockets** para transferência de veículos entre cruzamentos
|
||||||
|
- **Simulação de eventos discretos** (DES) para gerir o tempo de simulação
|
||||||
|
|
||||||
|
### Características Principais
|
||||||
|
|
||||||
|
- Simulação determinística e reproduzível
|
||||||
|
- Comunicação assíncrona entre processos
|
||||||
|
- Protocolo de mensagens baseado em JSON
|
||||||
|
- Dashboard em tempo real (planeado)
|
||||||
|
- Estatísticas detalhadas de desempenho
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Arquitetura
|
||||||
|
|
||||||
|
### Visão Geral do Sistema
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ SISTEMA DISTRIBUÍDO │
|
||||||
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
|
│ │
|
||||||
|
│ ┌──────────────┐ ┌──────────────┐ │
|
||||||
|
│ │ Coordenador │ ────────────────────────>│ Dashboard │ │
|
||||||
|
│ │ / Gerador │ │ │
|
||||||
|
│ └──────┬───────┘ └──────▲───────┘ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ Gera veículos Stats │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ▼ │ │
|
||||||
|
│ ┌─────────────────────────────────────────────────┴──────┐ │
|
||||||
|
│ │ Rede de Cruzamentos (Processos) │ │
|
||||||
|
│ │ │ │
|
||||||
|
│ │ ┌────┐ ┌────┐ ┌────┐ │ │
|
||||||
|
│ │ │Cr1 │◄───────►│Cr2 │◄───────►│Cr3 │ │ │
|
||||||
|
│ │ └─┬──┘ └─┬──┘ └─┬──┘ │ │
|
||||||
|
│ │ │ │ │ │ │
|
||||||
|
│ │ │ ┌────▼────┐ │ │ │
|
||||||
|
│ │ └────────►│ Cr4 │◄────────┘ │ │
|
||||||
|
│ │ └────┬────┘ │ │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ │ ┌────▼────┐ │ │
|
||||||
|
│ │ │ Cr5 │ │ │
|
||||||
|
│ │ └────┬────┘ │ │
|
||||||
|
│ └───────────────────┼─────────────────────────────────────┤ │
|
||||||
|
│ │ │ │
|
||||||
|
│ ▼ │ │
|
||||||
|
│ ┌──────────────┐ │ │
|
||||||
|
│ │ Nó de Saída │ │ │
|
||||||
|
│ │ (S) │ │ │
|
||||||
|
│ └──────────────┘ │ │
|
||||||
|
│ │ │
|
||||||
|
└────────────────────────────────────────────────────────────┘ │
|
||||||
|
```
|
||||||
|
|
||||||
|
### Componentes
|
||||||
|
|
||||||
|
1. **Coordenador/Gerador**: Gera veículos e injeta no sistema
|
||||||
|
2. **Cruzamentos (Cr1-Cr5)**: Processos independentes que gerem tráfego local
|
||||||
|
3. **Nó de Saída (S)**: Recolhe estatísticas de veículos que saem do sistema
|
||||||
|
4. **Dashboard Server**: Agrega e exibe dados em tempo real
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Protocolo de Comunicação
|
||||||
|
|
||||||
|
### Formato de Serialização: JSON (Gson)
|
||||||
|
|
||||||
|
O sistema utiliza JSON como formato de serialização por ser mais rápido, seguro e legível que a serialização em Java.
|
||||||
|
|
||||||
|
### Estrutura de Mensagens
|
||||||
|
|
||||||
|
Todas as mensagens seguem o formato base:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "uuid",
|
||||||
|
"type": "MESSAGE_TYPE",
|
||||||
|
"senderId": "sender_id",
|
||||||
|
"destinationId": "destination_id",
|
||||||
|
"timestamp": 1729595234567,
|
||||||
|
"payload": { ... }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tipos de Mensagens
|
||||||
|
|
||||||
|
#### 1. VEHICLE_TRANSFER
|
||||||
|
|
||||||
|
Transfere um veículo entre cruzamentos.
|
||||||
|
|
||||||
|
**Estrutura:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "a3c5e7f9-1234-5678-90ab-cdef12345678",
|
||||||
|
"type": "VEHICLE_TRANSFER",
|
||||||
|
"senderId": "Cr1",
|
||||||
|
"destinationId": "Cr2",
|
||||||
|
"timestamp": 1729595234567,
|
||||||
|
"payload": {
|
||||||
|
"id": "V123",
|
||||||
|
"type": "LIGHT",
|
||||||
|
"entryTime": 15.7,
|
||||||
|
"route": ["Cr1", "Cr2", "Cr5", "S"],
|
||||||
|
"currentRouteIndex": 1,
|
||||||
|
"totalWaitingTime": 3.2,
|
||||||
|
"totalCrossingTime": 1.8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fluxo:**
|
||||||
|
1. Veículo completa travessia no Cr1
|
||||||
|
2. Cr1 serializa mensagem VEHICLE_TRANSFER
|
||||||
|
3. Envia para Cr2 via socket
|
||||||
|
4. Cr2 desserializa e adiciona veículo à fila
|
||||||
|
|
||||||
|
#### 2. STATS_UPDATE
|
||||||
|
|
||||||
|
Envia estatísticas de um cruzamento para o Dashboard.
|
||||||
|
|
||||||
|
**Estrutura:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "b4d6e8f0-2345-6789-01bc-def123456789",
|
||||||
|
"type": "STATS_UPDATE",
|
||||||
|
"senderId": "Cr3",
|
||||||
|
"destinationId": "Dashboard",
|
||||||
|
"timestamp": 1729595234789,
|
||||||
|
"payload": {
|
||||||
|
"intersectionId": "Cr3",
|
||||||
|
"queueLengths": {
|
||||||
|
"North": 5,
|
||||||
|
"South": 3,
|
||||||
|
"East": 7,
|
||||||
|
"West": 2
|
||||||
|
},
|
||||||
|
"vehiclesProcessed": 142,
|
||||||
|
"averageWaitTime": 4.5,
|
||||||
|
"currentTime": 123.45
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frequência:** A cada 10 segundos (configurável)
|
||||||
|
|
||||||
|
#### 3. VEHICLE_EXIT
|
||||||
|
|
||||||
|
Notifica quando um veículo sai do sistema.
|
||||||
|
|
||||||
|
**Estrutura:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "c5e7f9a1-3456-7890-12bc-def123456789",
|
||||||
|
"type": "VEHICLE_EXIT",
|
||||||
|
"senderId": "Cr5",
|
||||||
|
"destinationId": "ExitNode",
|
||||||
|
"timestamp": 1729595234890,
|
||||||
|
"payload": {
|
||||||
|
"id": "V123",
|
||||||
|
"type": "LIGHT",
|
||||||
|
"entryTime": 15.7,
|
||||||
|
"exitTime": 45.2,
|
||||||
|
"totalSystemTime": 29.5,
|
||||||
|
"totalWaitingTime": 8.3,
|
||||||
|
"totalCrossingTime": 4.8,
|
||||||
|
"routeTaken": ["Cr1", "Cr2", "Cr5", "S"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. HEARTBEAT
|
||||||
|
|
||||||
|
Mantém a ligação ativa e monitoriza a saúde dos processos.
|
||||||
|
|
||||||
|
**Estrutura:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "d6e8f0a2-4567-8901-23cd-ef1234567890",
|
||||||
|
"type": "HEARTBEAT",
|
||||||
|
"senderId": "Cr1",
|
||||||
|
"destinationId": "Coordinator",
|
||||||
|
"timestamp": 1729595235000,
|
||||||
|
"payload": {
|
||||||
|
"status": "RUNNING",
|
||||||
|
"uptime": 120.5,
|
||||||
|
"vehiclesInQueue": 12
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frequência:** A cada 5 segundos
|
||||||
|
|
||||||
|
#### 5. LIGHT_CHANGE
|
||||||
|
|
||||||
|
Notifica mudança de estado de semáforo (para logging/debugging).
|
||||||
|
|
||||||
|
**Estrutura:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"messageId": "e7f9a1b3-5678-9012-34de-f12345678901",
|
||||||
|
"type": "LIGHT_CHANGE",
|
||||||
|
"senderId": "Cr1-North",
|
||||||
|
"destinationId": "Dashboard",
|
||||||
|
"timestamp": 1729595235100,
|
||||||
|
"payload": {
|
||||||
|
"lightId": "Cr1-North",
|
||||||
|
"previousState": "RED",
|
||||||
|
"newState": "GREEN",
|
||||||
|
"queueSize": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tipos de Veículos
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"BIKE": {
|
||||||
|
"probability": 0.20,
|
||||||
|
"crossingTime": 1.5
|
||||||
|
},
|
||||||
|
"LIGHT": {
|
||||||
|
"probability": 0.60,
|
||||||
|
"crossingTime": 2.0
|
||||||
|
},
|
||||||
|
"HEAVY": {
|
||||||
|
"probability": 0.20,
|
||||||
|
"crossingTime": 4.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Estados dos Semáforos
|
||||||
|
|
||||||
|
```
|
||||||
|
RED → Veículos aguardam na fila
|
||||||
|
GREEN → Veículos podem atravessar
|
||||||
|
```
|
||||||
|
|
||||||
|
### Exemplo de Comunicação Completa
|
||||||
|
|
||||||
|
```
|
||||||
|
Tempo Processo Ação Mensagem
|
||||||
|
------ --------- ------------------------------------- ------------------
|
||||||
|
15.7s Gerador Gera veículo V123 -
|
||||||
|
15.7s Gerador → Injeta V123 em Cr1 VEHICLE_TRANSFER
|
||||||
|
18.2s Cr1 V123 inicia travessia -
|
||||||
|
20.2s Cr1 V123 completa travessia -
|
||||||
|
20.2s Cr1 → Cr2 Transfere V123 para Cr2 VEHICLE_TRANSFER
|
||||||
|
23.5s Cr2 V123 inicia travessia -
|
||||||
|
25.5s Cr2 V123 completa travessia -
|
||||||
|
25.5s Cr2 → Cr5 Transfere V123 para Cr5 VEHICLE_TRANSFER
|
||||||
|
28.0s Cr5 V123 inicia travessia -
|
||||||
|
30.0s Cr5 V123 completa travessia -
|
||||||
|
30.0s Cr5 → Exit V123 sai do sistema VEHICLE_EXIT
|
||||||
|
30.0s Exit → Dash Estatísticas de V123 STATS_UPDATE
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estrutura do Projeto
|
||||||
|
|
||||||
|
```
|
||||||
|
Trabalho-Pratico-SD/
|
||||||
|
├── README.md # Este ficheiro
|
||||||
|
├── TODO.md # Plano de desenvolvimento
|
||||||
|
├── main/
|
||||||
|
│ ├── pom.xml # Configuração do Maven
|
||||||
|
│ ├── docs/
|
||||||
|
│ │ ├── README.md # Índice da documentação
|
||||||
|
│ │ ├── SERIALIZATION_SPECIFICATION.md
|
||||||
|
│ │ ├── SERIALIZATION_DECISION.md
|
||||||
|
│ │ ├── SERIALIZATION_SUMMARY.md
|
||||||
|
│ │ └── SERIALIZATION_ARCHITECTURE.md
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── main/java/sd/
|
||||||
|
│ │ │ ├── Entry.java # Ponto de entrada
|
||||||
|
│ │ │ ├── config/
|
||||||
|
│ │ │ │ └── SimulationConfig.java
|
||||||
|
│ │ │ ├── engine/
|
||||||
|
│ │ │ │ └── SimulationEngine.java
|
||||||
|
│ │ │ ├── model/
|
||||||
|
│ │ │ │ ├── Event.java
|
||||||
|
│ │ │ │ ├── EventType.java
|
||||||
|
│ │ │ │ ├── Intersection.java
|
||||||
|
│ │ │ │ ├── Message.java # Estrutura de mensagens
|
||||||
|
│ │ │ │ ├── MessageType.java # Tipos de mensagens
|
||||||
|
│ │ │ │ ├── TrafficLight.java
|
||||||
|
│ │ │ │ ├── Vehicle.java
|
||||||
|
│ │ │ │ └── VehicleType.java
|
||||||
|
│ │ │ ├── serialization/ # Sistema de serialização
|
||||||
|
│ │ │ │ ├── MessageSerializer.java
|
||||||
|
│ │ │ │ ├── SerializationException.java
|
||||||
|
│ │ │ │ ├── JsonMessageSerializer.java
|
||||||
|
│ │ │ │ ├── SerializerFactory.java
|
||||||
|
│ │ │ │ ├── SerializationExample.java
|
||||||
|
│ │ │ │ └── README.md
|
||||||
|
│ │ │ └── util/
|
||||||
|
│ │ │ ├── RandomGenerator.java
|
||||||
|
│ │ │ ├── StatisticsCollector.java
|
||||||
|
│ │ │ └── VehicleGenerator.java
|
||||||
|
│ │ └── test/java/
|
||||||
|
│ │ ├── SimulationTest.java
|
||||||
|
│ │ └── sd/serialization/
|
||||||
|
│ │ └── SerializationTest.java
|
||||||
|
│ └── target/ # Ficheiros compilados
|
||||||
|
└── .vscode/ # Configuração do VS Code
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Instalação e Execução
|
||||||
|
|
||||||
|
### Pré-requisitos
|
||||||
|
|
||||||
|
- **Java 17** ou superior
|
||||||
|
- **Maven 3.8+**
|
||||||
|
- **Git**
|
||||||
|
|
||||||
|
### Instalação
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clonar o repositório
|
||||||
|
git clone https://github.com/davidalves04/Trabalho-Pratico-SD.git
|
||||||
|
cd Trabalho-Pratico-SD/main
|
||||||
|
|
||||||
|
# Compilar o projeto
|
||||||
|
mvn clean compile
|
||||||
|
|
||||||
|
# Executar os testes
|
||||||
|
mvn test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Execução
|
||||||
|
|
||||||
|
#### Simulação Básica (Single Process)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn exec:java -Dexec.mainClass="sd.Entry"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Exemplo de Serialização
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn exec:java -Dexec.mainClass="sd.serialization.SerializationExample"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Configuração
|
||||||
|
|
||||||
|
Editar `src/main/resources/simulation.properties`:
|
||||||
|
|
||||||
|
```properties
|
||||||
|
# Duração da simulação (segundos)
|
||||||
|
simulation.duration=60.0
|
||||||
|
|
||||||
|
# Modelo de chegada: FIXED ou POISSON
|
||||||
|
arrival.model=POISSON
|
||||||
|
|
||||||
|
# Taxa de chegada (veículos/segundo)
|
||||||
|
arrival.rate=0.5
|
||||||
|
|
||||||
|
# Intervalo de atualização de estatísticas (segundos)
|
||||||
|
stats.update.interval=10.0
|
||||||
|
|
||||||
|
# Distribuição de tipos de veículos
|
||||||
|
vehicle.type.bike.probability=0.20
|
||||||
|
vehicle.type.light.probability=0.60
|
||||||
|
vehicle.type.heavy.probability=0.20
|
||||||
|
|
||||||
|
# Tempos de travessia por tipo (segundos)
|
||||||
|
vehicle.type.bike.crossing.time=1.5
|
||||||
|
vehicle.type.light.crossing.time=2.0
|
||||||
|
vehicle.type.heavy.crossing.time=4.0
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Documentação
|
||||||
|
|
||||||
|
### Documentação de Serialização
|
||||||
|
|
||||||
|
A documentação completa sobre o protocolo de serialização está disponível em:
|
||||||
|
|
||||||
|
- **[Índice Completo](./main/docs/README.md)** - Navegação da documentação
|
||||||
|
- **[Especificação](./main/docs/SERIALIZATION_SPECIFICATION.md)** - Design detalhado
|
||||||
|
- **[Guia de Decisão](./main/docs/SERIALIZATION_DECISION.md)** - Porquê JSON?
|
||||||
|
- **[Resumo](./main/docs/SERIALIZATION_SUMMARY.md)** - Estado de implementação
|
||||||
|
- **[Arquitetura](./main/docs/SERIALIZATION_ARCHITECTURE.md)** - Diagramas visuais
|
||||||
|
|
||||||
|
### Guias de Utilização
|
||||||
|
|
||||||
|
- **[Serialization README](./main/src/main/java/sd/serialization/README.md)** - Como utilizar os serializers
|
||||||
|
|
||||||
|
### Exemplos de Código
|
||||||
|
|
||||||
|
```java
|
||||||
|
// Criar serializer
|
||||||
|
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||||
|
|
||||||
|
// Serializar mensagem
|
||||||
|
Vehicle vehicle = new Vehicle("V123", VehicleType.LIGHT, 10.5, route);
|
||||||
|
Message message = new Message(
|
||||||
|
MessageType.VEHICLE_TRANSFER,
|
||||||
|
"Cr1",
|
||||||
|
"Cr2",
|
||||||
|
vehicle
|
||||||
|
);
|
||||||
|
byte[] data = serializer.serialize(message);
|
||||||
|
|
||||||
|
// Enviar via socket
|
||||||
|
outputStream.write(data);
|
||||||
|
|
||||||
|
// Receber e desserializar
|
||||||
|
byte[] received = inputStream.readAllBytes();
|
||||||
|
Message msg = serializer.deserialize(received, Message.class);
|
||||||
|
Vehicle v = msg.getPayloadAs(Vehicle.class);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Desenvolvimento
|
||||||
|
|
||||||
|
### Estado do Projeto
|
||||||
|
|
||||||
|
| Componente | Estado | Notas |
|
||||||
|
|------------|--------|-------|
|
||||||
|
| Modelo de Dados | Completo | Vehicle, Message, Event, etc. |
|
||||||
|
| Simulação DES | Completo | Single-process funcional |
|
||||||
|
| Serialização | Completo | JSON e Java implementados |
|
||||||
|
| Testes | 14/14 | Suite de serialização |
|
||||||
|
| Processos Distribuídos | Planeado | Próxima etapa |
|
||||||
|
| Comunicação Sockets | Planeado | Em design |
|
||||||
|
| Dashboard | Planeado | UI web |
|
||||||
|
|
||||||
|
### Roteiro de Desenvolvimento
|
||||||
|
|
||||||
|
#### Fase 1: Fundações (Concluído)
|
||||||
|
- Modelação de classes
|
||||||
|
- Simulação DES single-process
|
||||||
|
- Design de protocolo de serialização
|
||||||
|
- Implementação JSON/Java serialization
|
||||||
|
- Testes unitários
|
||||||
|
|
||||||
|
#### Fase 2: Distribuição (Em Curso)
|
||||||
|
- Implementar comunicação via sockets
|
||||||
|
- Separar cruzamentos em processos
|
||||||
|
- Implementar threads de semáforos
|
||||||
|
- Testar comunicação entre processos
|
||||||
|
|
||||||
|
#### Fase 3: Dashboard e Monitorização
|
||||||
|
- Dashboard server
|
||||||
|
- UI web em tempo real
|
||||||
|
- Visualização de estatísticas
|
||||||
|
- Logs estruturados
|
||||||
|
|
||||||
|
#### Fase 4: Optimização e Análise
|
||||||
|
- Testes de carga
|
||||||
|
- Análise de diferentes políticas
|
||||||
|
- Recolha de métricas
|
||||||
|
- Relatório final
|
||||||
|
|
||||||
|
### Executar Testes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Todos os testes
|
||||||
|
mvn test
|
||||||
|
|
||||||
|
# Apenas testes de serialização
|
||||||
|
mvn test -Dtest=SerializationTest
|
||||||
|
|
||||||
|
# Com relatório de cobertura
|
||||||
|
mvn test jacoco:report
|
||||||
|
```
|
||||||
|
|
||||||
|
### Contribuir
|
||||||
|
|
||||||
|
1. Fork o projeto
|
||||||
|
2. Criar uma branch para a funcionalidade (`git checkout -b feature/MinhaFuncionalidade`)
|
||||||
|
3. Commit das alterações (`git commit -m 'Adiciona MinhaFuncionalidade'`)
|
||||||
|
4. Push para a branch (`git push origin feature/MinhaFuncionalidade`)
|
||||||
|
5. Abrir um Pull Request
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Métricas de Desempenho
|
||||||
|
|
||||||
|
### Serialização
|
||||||
|
|
||||||
|
| Formato | Tamanho | Latência | Throughput |
|
||||||
|
|---------|---------|----------|------------|
|
||||||
|
| JSON | 300 bytes | 40.79 μs | ~24k msgs/s |
|
||||||
|
| Java | 657 bytes | 33.34 μs | ~30k msgs/s |
|
||||||
|
|
||||||
|
**Conclusão**: JSON é 54% menor com overhead desprezível (7 μs)
|
||||||
|
|
||||||
|
### Simulação
|
||||||
|
|
||||||
|
- **Veículos gerados/s**: ~0.5-1.0 (configurável)
|
||||||
|
- **Throughput**: ~0.2 veículos/s (saída)
|
||||||
|
- **Tempo de execução**: 140ms para 60s de simulação
|
||||||
|
- **Overhead**: < 0.25% do tempo simulado
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Protocolo de Mensagens - Resumo
|
||||||
|
|
||||||
|
### Formato Base
|
||||||
|
|
||||||
|
```
|
||||||
|
+------------------+
|
||||||
|
| Message Header |
|
||||||
|
|------------------|
|
||||||
|
| messageId | UUID único
|
||||||
|
| type | Enum MessageType
|
||||||
|
| senderId | ID do processo remetente
|
||||||
|
| destinationId | ID do processo destino (null = broadcast)
|
||||||
|
| timestamp | Tempo de criação (ms)
|
||||||
|
+------------------+
|
||||||
|
| Payload |
|
||||||
|
|------------------|
|
||||||
|
| Object | Dados específicos do tipo de mensagem
|
||||||
|
+------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### Serialização
|
||||||
|
|
||||||
|
- **Formato**: JSON (UTF-8)
|
||||||
|
- **Biblioteca**: Gson 2.10.1
|
||||||
|
- **Codificação**: UTF-8
|
||||||
|
- **Compressão**: Opcional (gzip)
|
||||||
|
|
||||||
|
### Transporte
|
||||||
|
|
||||||
|
- **Protocolo**: TCP/IP
|
||||||
|
- **Porta base**: 5000+ (configurável)
|
||||||
|
- **Timeout**: 30s
|
||||||
|
- **Keep-alive**: Heartbeat a cada 5s
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Segurança
|
||||||
|
|
||||||
|
### Considerações
|
||||||
|
|
||||||
|
1. **Validação de Mensagens**
|
||||||
|
- Verificar tipos esperados
|
||||||
|
- Validar intervalos de valores
|
||||||
|
- Rejeitar mensagens malformadas
|
||||||
|
|
||||||
|
2. **Autenticação** (Planeado)
|
||||||
|
- Autenticação baseada em token
|
||||||
|
- Whitelist de processos
|
||||||
|
|
||||||
|
3. **Encriptação** (Opcional)
|
||||||
|
- TLS/SSL para produção
|
||||||
|
- Não necessário para ambiente de desenvolvimento local
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Licença
|
||||||
|
|
||||||
|
Este projeto é desenvolvido para fins académicos no âmbito da disciplina de Sistemas Distribuídos (SD) do Instituto Politécnico do Porto.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Equipa
|
||||||
|
|
||||||
|
**Instituição**: Instituto Politécnico do Porto
|
||||||
|
**Curso**: Sistemas Distribuídos
|
||||||
|
**Ano Letivo**: 2025-2026 (1º Semestre)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Suporte
|
||||||
|
|
||||||
|
Para questões ou problemas:
|
||||||
|
|
||||||
|
1. Consultar a [documentação](./main/docs/README.md)
|
||||||
|
2. Ver [exemplos de código](./main/src/main/java/sd/serialization/SerializationExample.java)
|
||||||
|
3. Executar testes: `mvn test`
|
||||||
|
4. Abrir issue no GitHub
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ligações Úteis
|
||||||
|
|
||||||
|
- [Documentação do Projeto](./main/docs/README.md)
|
||||||
|
- [Plano de Desenvolvimento](./TODO.md)
|
||||||
|
- [Especificação de Serialização](./main/docs/SERIALIZATION_SPECIFICATION.md)
|
||||||
|
- [Guia de Serialização](./main/src/main/java/sd/serialization/README.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Última actualização**: 23 de outubro de 2025
|
||||||
|
**Versão**: 1.0.0
|
||||||
|
**Estado**: Em Desenvolvimento Activo
|
||||||
134
STEP2_SUMMARY.md
Normal file
134
STEP2_SUMMARY.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
# 🏁 Single-Process Prototype — Implementation Summary
|
||||||
|
|
||||||
|
**Status:** ✅ Complete
|
||||||
|
**Date:** October 22, 2025
|
||||||
|
**Branch:** `8-single-process-prototype`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The single-process prototype implements a **discrete event simulation (DES)** of a 3×3 urban grid with five intersections, realistic vehicle behavior, and fully synchronized traffic lights. Everything runs under one process, laying the groundwork for the distributed architecture in Phase 3.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Core Architecture
|
||||||
|
|
||||||
|
### **SimulationEngine**
|
||||||
|
|
||||||
|
Drives the DES loop with a priority queue of timestamped events — vehicles, lights, crossings, and periodic stats updates. Handles five intersections (Cr1–Cr5) and six event types.
|
||||||
|
|
||||||
|
**Main loop:**
|
||||||
|
|
||||||
|
```
|
||||||
|
while (events && time < duration):
|
||||||
|
event = nextEvent()
|
||||||
|
time = event.timestamp
|
||||||
|
handle(event)
|
||||||
|
```
|
||||||
|
|
||||||
|
### **VehicleGenerator**
|
||||||
|
|
||||||
|
Spawns vehicles via:
|
||||||
|
|
||||||
|
* **Poisson arrivals** (λ = 0.5 veh/s) or fixed intervals
|
||||||
|
* **Probabilistic routes** from E1–E3
|
||||||
|
* **Type distribution**: 20% BIKE, 60% LIGHT, 20% HEAVY
|
||||||
|
|
||||||
|
### **StatisticsCollector**
|
||||||
|
|
||||||
|
Tracks system-wide and per-type metrics: throughput, avg. wait, queue sizes, light cycles — updated every 10 s and at simulation end.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Model Highlights
|
||||||
|
|
||||||
|
* **Vehicle** – type, route, timings, lifecycle.
|
||||||
|
* **Intersection** – routing tables, traffic lights, queues.
|
||||||
|
* **TrafficLight** – red/green cycles with FIFO queues.
|
||||||
|
* **Event** – timestamped, comparable; 6 types for all DES actions.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration (`simulation.properties`)
|
||||||
|
|
||||||
|
```properties
|
||||||
|
simulation.duration=60.0
|
||||||
|
simulation.arrival.model=POISSON
|
||||||
|
simulation.arrival.rate=0.5
|
||||||
|
|
||||||
|
vehicle.bike.crossingTime=1.5
|
||||||
|
vehicle.light.crossingTime=2.0
|
||||||
|
vehicle.heavy.crossingTime=4.0
|
||||||
|
|
||||||
|
statistics.update.interval=10.0
|
||||||
|
```
|
||||||
|
|
||||||
|
**Speed logic:**
|
||||||
|
`t_bike = 0.5×t_car`, `t_heavy = 2×t_car`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Topology
|
||||||
|
|
||||||
|
```
|
||||||
|
E1→Cr1→Cr4→Cr5→S
|
||||||
|
E2→Cr2→Cr5→S
|
||||||
|
E3→Cr3→S
|
||||||
|
Bi-dir: Cr1↔Cr2, Cr2↔Cr3
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Results
|
||||||
|
|
||||||
|
**Unit Tests:** 7/7 ✅
|
||||||
|
**60-Second Simulation:**
|
||||||
|
|
||||||
|
* Generated: 22 vehicles
|
||||||
|
* Completed: 5 (22.7%)
|
||||||
|
* Avg system time: 15.47 s
|
||||||
|
* Throughput: 0.08 veh/s
|
||||||
|
* All lights & intersections operational
|
||||||
|
|
||||||
|
**Performance:**
|
||||||
|
~0.03 s real-time run (≈2000× speed-up), < 50 MB RAM.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Code Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
sd/
|
||||||
|
├── engine/SimulationEngine.java
|
||||||
|
├── model/{Vehicle,Intersection,TrafficLight,Event}.java
|
||||||
|
├── util/{VehicleGenerator,StatisticsCollector}.java
|
||||||
|
└── config/SimulationConfig.java
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Flow
|
||||||
|
|
||||||
|
1. Initialize intersections, lights, first events.
|
||||||
|
2. Process events chronologically.
|
||||||
|
3. Vehicles follow routes → queue → cross → exit.
|
||||||
|
4. Lights toggle, queues drain, stats update.
|
||||||
|
5. Print summary and performance metrics.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Next Steps — Phase 3
|
||||||
|
|
||||||
|
* Split intersections into independent **processes**.
|
||||||
|
* Add **socket-based communication**.
|
||||||
|
* Run **traffic lights as threads**.
|
||||||
|
* Enable **distributed synchronization** and fault handling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## TL;DR
|
||||||
|
|
||||||
|
Solid single-process DES ✅
|
||||||
|
Everything’s working — traffic lights, routing, vehicles, stats.
|
||||||
|
Ready to go distributed next.
|
||||||
27
TODO.md
27
TODO.md
@@ -1,3 +1,26 @@
|
|||||||
|
## ✅ SINGLE-PROCESS PROTOTYPE - COMPLETED
|
||||||
|
|
||||||
|
### Phase 2 Status: DONE ✅
|
||||||
|
|
||||||
|
All components for the single-process prototype have been successfully implemented and tested:
|
||||||
|
|
||||||
|
- ✅ **SimulationEngine** - Priority queue-based discrete event simulation
|
||||||
|
- ✅ **VehicleGenerator** - Poisson and Fixed arrival models
|
||||||
|
- ✅ **StatisticsCollector** - Comprehensive metrics tracking
|
||||||
|
- ✅ **Entry point** - Main simulation runner
|
||||||
|
- ✅ **60s test simulation** - Successfully validated event processing and routing
|
||||||
|
|
||||||
|
### Test Results:
|
||||||
|
- All 7 unit tests passing
|
||||||
|
- 60-second simulation completed successfully
|
||||||
|
- Generated 22 vehicles with 5 completing their routes
|
||||||
|
- Traffic light state changes working correctly
|
||||||
|
- Vehicle routing through intersections validated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NEXT: Distributed Architecture Implementation
|
||||||
|
|
||||||
### Compreender os Conceitos Fundamentais
|
### Compreender os Conceitos Fundamentais
|
||||||
|
|
||||||
Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem ser totalmente compreendidos.
|
Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem ser totalmente compreendidos.
|
||||||
@@ -16,7 +39,7 @@ Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem
|
|||||||
|
|
||||||
- Uma **lista de eventos** central, frequentemente uma fila de prioridades, será necessária para armazenar eventos futuros, ordenados pelo seu timestamp. O ciclo principal da simulação retira o próximo evento da lista, processa-o e adiciona quaisquer novos eventos que resultem dele.
|
- Uma **lista de eventos** central, frequentemente uma fila de prioridades, será necessária para armazenar eventos futuros, ordenados pelo seu timestamp. O ciclo principal da simulação retira o próximo evento da lista, processa-o e adiciona quaisquer novos eventos que resultem dele.
|
||||||
|
|
||||||
- **Processo de Poisson:** Para o modelo "mais realista" de chegadas de veículos, é especificado um processo de Poisson. A principal conclusão é que o tempo _entre_ chegadas consecutivas de veículos segue uma **distribuição exponencial**. Em Java, este intervalo pode ser gerado usando `Math.log(1 - Math.random()) / -lambda`, onde `lambda` (λi) é a taxa de chegada especificada.
|
- **Processo de Poisson:** Para o modelo 'mais realista' de chegadas de veículos, é especificado um processo de Poisson. A principal conclusão é que o tempo _entre_ chegadas consecutivas de veículos segue uma **distribuição exponencial**. Em Java, este intervalo pode ser gerado usando `Math.log(1 - Math.random()) / -lambda`, onde `lambda` (λi) é a taxa de chegada especificada.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -172,4 +195,4 @@ Assim que o sistema completo estiver a funcionar, as experiências exigidas pela
|
|||||||
|
|
||||||
- **Debugging:** Debugging de sistemas distribuídos podem ser difíceis. Uma framework de logging (como Log4j 2 ou SLF4J) pode ser usada para registar eventos//alterações de estado nos diferentes processos.
|
- **Debugging:** Debugging de sistemas distribuídos podem ser difíceis. Uma framework de logging (como Log4j 2 ou SLF4J) pode ser usada para registar eventos//alterações de estado nos diferentes processos.
|
||||||
|
|
||||||
- **Configuração:** Valores como endereços IP, números de porta ou parâmetros da simulação não devem ser "hardcoded". Um ficheiro de configuração (ex: um ficheiro `.properties` ou `.json`) torna a aplicação mais fácil de executar e testar.
|
- **Configuração:** Valores como endereços IP, números de porta ou parâmetros da simulação não devem ser "hardcoded". Um ficheiro de configuração (ex: um ficheiro `.properties` ou `.json`) torna a aplicação mais fácil de executar e testar.
|
||||||
|
|||||||
41
main/pom.xml
41
main/pom.xml
@@ -22,6 +22,47 @@
|
|||||||
<version>5.10.0</version>
|
<version>5.10.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Gson for JSON serialization -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.10.1</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<!-- Maven Exec Plugin for running examples -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>sd.Entry</mainClass>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
|
<version>3.5.2</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>shade</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<transformers>
|
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
|
<mainClass>sd.Entry</mainClass>
|
||||||
|
</transformer>
|
||||||
|
</transformers>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -31,7 +31,7 @@ public class SimulationConfig {
|
|||||||
* (por exemplo quando executado a partir do classpath/jar),
|
* (por exemplo quando executado a partir do classpath/jar),
|
||||||
* faz fallback para carregar a partir do classpath usando o ClassLoader.
|
* faz fallback para carregar a partir do classpath usando o ClassLoader.
|
||||||
*/
|
*/
|
||||||
IOException lastException = null;
|
IOException lastException = null; //FIXME: melhorar esta parte para reportar erros de forma mais clara
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try (InputStream input = new FileInputStream(filePath)) {
|
try (InputStream input = new FileInputStream(filePath)) {
|
||||||
|
|||||||
@@ -264,32 +264,19 @@ public class SimulationEngine {
|
|||||||
*/
|
*/
|
||||||
private void processEvent(Event event) {
|
private void processEvent(Event event) {
|
||||||
switch (event.getType()) {
|
switch (event.getType()) {
|
||||||
case VEHICLE_GENERATION:
|
case VEHICLE_GENERATION -> handleVehicleGeneration();
|
||||||
handleVehicleGeneration();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VEHICLE_ARRIVAL:
|
case VEHICLE_ARRIVAL -> handleVehicleArrival(event);
|
||||||
handleVehicleArrival(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TRAFFIC_LIGHT_CHANGE:
|
case TRAFFIC_LIGHT_CHANGE -> handleTrafficLightChange(event);
|
||||||
handleTrafficLightChange(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CROSSING_START:
|
case CROSSING_START -> handleCrossingStart(event);
|
||||||
handleCrossingStart(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CROSSING_END:
|
case CROSSING_END -> handleCrossingEnd(event);
|
||||||
handleCrossingEnd(event);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STATISTICS_UPDATE:
|
case STATISTICS_UPDATE -> handleStatisticsUpdate();
|
||||||
handleStatisticsUpdate();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default -> System.err.println("Unknown event type: " + event.getType());
|
||||||
System.err.println("Unknown event type: " + event.getType());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,7 +373,7 @@ public class SimulationEngine {
|
|||||||
* @param vehicle The vehicle to process.
|
* @param vehicle The vehicle to process.
|
||||||
* @param intersection The intersection where the vehicle is.
|
* @param intersection The intersection where the vehicle is.
|
||||||
*/
|
*/
|
||||||
private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) {
|
private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) { //FIXME
|
||||||
// Find the direction (and light) this vehicle is queued at
|
// Find the direction (and light) this vehicle is queued at
|
||||||
// This logic is a bit flawed: it just finds the *first* non-empty queue
|
// This logic is a bit flawed: it just finds the *first* non-empty queue
|
||||||
// A better approach would be to get the light from the vehicle's route
|
// A better approach would be to get the light from the vehicle's route
|
||||||
@@ -591,16 +578,12 @@ public class SimulationEngine {
|
|||||||
* @return The crossing time in seconds.
|
* @return The crossing time in seconds.
|
||||||
*/
|
*/
|
||||||
private double getCrossingTime(VehicleType type) {
|
private double getCrossingTime(VehicleType type) {
|
||||||
switch (type) {
|
return switch (type) {
|
||||||
case BIKE:
|
case BIKE -> config.getBikeVehicleCrossingTime();
|
||||||
return config.getBikeVehicleCrossingTime();
|
case LIGHT -> config.getLightVehicleCrossingTime();
|
||||||
case LIGHT:
|
case HEAVY -> config.getHeavyVehicleCrossingTime();
|
||||||
return config.getLightVehicleCrossingTime();
|
default -> 2.0;
|
||||||
case HEAVY:
|
}; // Default fallback
|
||||||
return config.getHeavyVehicleCrossingTime();
|
|
||||||
default:
|
|
||||||
return 2.0; // Default fallback
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
142
main/src/main/java/sd/model/Message.java
Normal file
142
main/src/main/java/sd/model/Message.java
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
package sd.model;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a message exchanged between processes in the distributed simulation.
|
||||||
|
* Each message has a unique ID, a type, a sender, a destination, and a payload.
|
||||||
|
* This class implements {@link Serializable} to allow transmission over the network.
|
||||||
|
*/
|
||||||
|
public class Message implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for this message.
|
||||||
|
*/
|
||||||
|
private final String messageId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of this message (e.g., VEHICLE_TRANSFER, STATS_UPDATE).
|
||||||
|
*/
|
||||||
|
private final MessageType type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier of the process that sent this message.
|
||||||
|
*/
|
||||||
|
private final String senderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifier of the destination process. Can be null for broadcast messages.
|
||||||
|
*/
|
||||||
|
private final String destinationId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The actual data being transmitted. Type depends on the message type.
|
||||||
|
*/
|
||||||
|
private final Object payload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timestamp when this message was created (simulation time or real time).
|
||||||
|
*/
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new message with all parameters.
|
||||||
|
*
|
||||||
|
* @param type The message type
|
||||||
|
* @param senderId The ID of the sending process
|
||||||
|
* @param destinationId The ID of the destination process (null for broadcast)
|
||||||
|
* @param payload The message payload
|
||||||
|
* @param timestamp The timestamp of message creation
|
||||||
|
*/
|
||||||
|
public Message(MessageType type, String senderId, String destinationId,
|
||||||
|
Object payload, long timestamp) {
|
||||||
|
this.messageId = UUID.randomUUID().toString();
|
||||||
|
this.type = type;
|
||||||
|
this.senderId = senderId;
|
||||||
|
this.destinationId = destinationId;
|
||||||
|
this.payload = payload;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new message with current system time as timestamp.
|
||||||
|
*
|
||||||
|
* @param type The message type
|
||||||
|
* @param senderId The ID of the sending process
|
||||||
|
* @param destinationId The ID of the destination process
|
||||||
|
* @param payload The message payload
|
||||||
|
*/
|
||||||
|
public Message(MessageType type, String senderId, String destinationId, Object payload) {
|
||||||
|
this(type, senderId, destinationId, payload, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a broadcast message (no specific destination).
|
||||||
|
*
|
||||||
|
* @param type The message type
|
||||||
|
* @param senderId The ID of the sending process
|
||||||
|
* @param payload The message payload
|
||||||
|
*/
|
||||||
|
public Message(MessageType type, String senderId, Object payload) {
|
||||||
|
this(type, senderId, null, payload, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Getters
|
||||||
|
|
||||||
|
public String getMessageId() {
|
||||||
|
return messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MessageType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSenderId() {
|
||||||
|
return senderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDestinationId() {
|
||||||
|
return destinationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimestamp() {
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this is a broadcast message (no specific destination).
|
||||||
|
*
|
||||||
|
* @return true if destinationId is null, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isBroadcast() {
|
||||||
|
return destinationId == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the payload cast to a specific type.
|
||||||
|
* Use with caution and ensure type safety.
|
||||||
|
*
|
||||||
|
* @param <T> The expected payload type
|
||||||
|
* @return The payload cast to type T
|
||||||
|
* @throws ClassCastException if the payload is not of type T
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T getPayloadAs(Class<T> clazz) {
|
||||||
|
return (T) payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("Message[id=%s, type=%s, from=%s, to=%s, timestamp=%d]",
|
||||||
|
messageId, type, senderId,
|
||||||
|
destinationId != null ? destinationId : "BROADCAST",
|
||||||
|
timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
81
main/src/main/java/sd/model/MessageType.java
Normal file
81
main/src/main/java/sd/model/MessageType.java
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package sd.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration representing all possible message types for distributed communication.
|
||||||
|
* These types are used for inter-process communication between different components
|
||||||
|
* of the distributed traffic simulation system.
|
||||||
|
*/
|
||||||
|
public enum MessageType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to transfer a vehicle between intersections or processes.
|
||||||
|
* Payload: Vehicle object with current state
|
||||||
|
*/
|
||||||
|
VEHICLE_TRANSFER,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to update statistics across the distributed system.
|
||||||
|
* Payload: Statistics data (waiting times, queue sizes, etc.)
|
||||||
|
*/
|
||||||
|
STATS_UPDATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to synchronize traffic light states between processes.
|
||||||
|
* Payload: TrafficLight state and timing information
|
||||||
|
*/
|
||||||
|
TRAFFIC_LIGHT_SYNC,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Heartbeat message to check if a process is alive.
|
||||||
|
* Payload: Process ID and timestamp
|
||||||
|
*/
|
||||||
|
HEARTBEAT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request to join the distributed simulation.
|
||||||
|
* Payload: Process information and capabilities
|
||||||
|
*/
|
||||||
|
JOIN_REQUEST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response to a join request.
|
||||||
|
* Payload: Acceptance status and configuration
|
||||||
|
*/
|
||||||
|
JOIN_RESPONSE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to notify about a new vehicle generation.
|
||||||
|
* Payload: Vehicle generation parameters
|
||||||
|
*/
|
||||||
|
VEHICLE_SPAWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to request the current state of an intersection.
|
||||||
|
* Payload: Intersection ID
|
||||||
|
*/
|
||||||
|
STATE_REQUEST,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response containing the current state of an intersection.
|
||||||
|
* Payload: Complete intersection state
|
||||||
|
*/
|
||||||
|
STATE_RESPONSE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message to signal shutdown of a process.
|
||||||
|
* Payload: Process ID and reason
|
||||||
|
*/
|
||||||
|
SHUTDOWN,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledgment message for reliable communication.
|
||||||
|
* Payload: Message ID being acknowledged
|
||||||
|
*/
|
||||||
|
ACK,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error message to report problems in the distributed system.
|
||||||
|
* Payload: Error description and context
|
||||||
|
*/
|
||||||
|
ERROR
|
||||||
|
}
|
||||||
114
main/src/main/java/sd/serialization/JsonMessageSerializer.java
Normal file
114
main/src/main/java/sd/serialization/JsonMessageSerializer.java
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON-based implementation of {@link MessageSerializer} using Google's Gson library.
|
||||||
|
*
|
||||||
|
* This serializer converts objects to JSON format for transmission, providing:
|
||||||
|
* - Human-readable message format (easy debugging)
|
||||||
|
* - Cross-platform compatibility
|
||||||
|
* - Smaller message sizes compared to Java native serialization
|
||||||
|
* - Better security (no code execution during deserialization)
|
||||||
|
*
|
||||||
|
* The serializer is configured with pretty printing disabled by default for
|
||||||
|
* production use, but can be enabled for debugging purposes.
|
||||||
|
*
|
||||||
|
* Thread-safety: This class is thread-safe as Gson instances are thread-safe.
|
||||||
|
*
|
||||||
|
* @see MessageSerializer
|
||||||
|
*/
|
||||||
|
public class JsonMessageSerializer implements MessageSerializer {
|
||||||
|
|
||||||
|
private final Gson gson;
|
||||||
|
private final boolean prettyPrint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new JSON serializer with default configuration (no pretty printing).
|
||||||
|
*/
|
||||||
|
public JsonMessageSerializer() {
|
||||||
|
this(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new JSON serializer with optional pretty printing.
|
||||||
|
*
|
||||||
|
* @param prettyPrint If true, JSON output will be formatted with indentation
|
||||||
|
*/
|
||||||
|
public JsonMessageSerializer(boolean prettyPrint) {
|
||||||
|
this.prettyPrint = prettyPrint;
|
||||||
|
GsonBuilder builder = new GsonBuilder();
|
||||||
|
|
||||||
|
if (prettyPrint) {
|
||||||
|
builder.setPrettyPrinting();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register custom type adapters here if needed
|
||||||
|
// builder.registerTypeAdapter(Vehicle.class, new VehicleAdapter());
|
||||||
|
|
||||||
|
this.gson = builder.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] serialize(Object object) throws SerializationException {
|
||||||
|
if (object == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot serialize null object");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String json = gson.toJson(object);
|
||||||
|
return json.getBytes(StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SerializationException(
|
||||||
|
"Failed to serialize object of type " + object.getClass().getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException {
|
||||||
|
if (data == null) {
|
||||||
|
throw new IllegalArgumentException("Cannot deserialize null data");
|
||||||
|
}
|
||||||
|
if (clazz == null) {
|
||||||
|
throw new IllegalArgumentException("Class type cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String json = new String(data, StandardCharsets.UTF_8);
|
||||||
|
return gson.fromJson(json, clazz);
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
throw new SerializationException(
|
||||||
|
"Failed to parse JSON for type " + clazz.getName(), e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SerializationException(
|
||||||
|
"Failed to deserialize object of type " + clazz.getName(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "JSON (Gson)";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the underlying Gson instance for advanced usage.
|
||||||
|
*
|
||||||
|
* @return The Gson instance
|
||||||
|
*/
|
||||||
|
public Gson getGson() {
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if pretty printing is enabled.
|
||||||
|
*
|
||||||
|
* @return true if pretty printing is enabled
|
||||||
|
*/
|
||||||
|
public boolean isPrettyPrint() {
|
||||||
|
return prettyPrint;
|
||||||
|
}
|
||||||
|
}
|
||||||
48
main/src/main/java/sd/serialization/MessageSerializer.java
Normal file
48
main/src/main/java/sd/serialization/MessageSerializer.java
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for serializing and deserializing objects for network transmission.
|
||||||
|
*
|
||||||
|
* This interface provides a common abstraction for different serialization strategies
|
||||||
|
* allowing the system to switch between implementations without changing the communication layer.
|
||||||
|
*
|
||||||
|
* Implementations must ensure:
|
||||||
|
* - Thread-safety if used in concurrent contexts
|
||||||
|
* - Proper exception handling with meaningful error messages
|
||||||
|
* - Preservation of object state during round-trip serialization
|
||||||
|
*
|
||||||
|
* @see JsonMessageSerializer
|
||||||
|
*/
|
||||||
|
public interface MessageSerializer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes an object into a byte array for transmission.
|
||||||
|
*
|
||||||
|
* @param object The object to serialize (must not be null)
|
||||||
|
* @return A byte array containing the serialized representation
|
||||||
|
* @throws SerializationException If serialization fails
|
||||||
|
* @throws IllegalArgumentException If object is null
|
||||||
|
*/
|
||||||
|
byte[] serialize(Object object) throws SerializationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes a byte array back into an object of the specified type.
|
||||||
|
*
|
||||||
|
* @param <T> The expected type of the deserialized object
|
||||||
|
* @param data The byte array containing serialized data (must not be null)
|
||||||
|
* @param clazz The class of the expected object type (must not be null)
|
||||||
|
* @return The deserialized object
|
||||||
|
* @throws SerializationException If deserialization fails
|
||||||
|
* @throws IllegalArgumentException If data or clazz is null
|
||||||
|
*/
|
||||||
|
<T> T deserialize(byte[] data, Class<T> clazz) throws SerializationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of this serialization strategy (e.g., "JSON", "Java Native").
|
||||||
|
* Useful for logging and debugging.
|
||||||
|
*
|
||||||
|
* @return The serializer name
|
||||||
|
*/
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
}
|
||||||
134
main/src/main/java/sd/serialization/SerializationExample.java
Normal file
134
main/src/main/java/sd/serialization/SerializationExample.java
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
import sd.model.Message;
|
||||||
|
import sd.model.MessageType;
|
||||||
|
import sd.model.Vehicle;
|
||||||
|
import sd.model.VehicleType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demonstration of JSON serialization usage in the traffic simulation system.
|
||||||
|
*
|
||||||
|
* This class shows practical examples of how to use JSON (Gson) serialization
|
||||||
|
* for network communication between simulation processes.
|
||||||
|
*/
|
||||||
|
public class SerializationExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== JSON Serialization Example ===\n");
|
||||||
|
|
||||||
|
// Create a sample vehicle
|
||||||
|
List<String> route = Arrays.asList("Cr1", "Cr2", "Cr5", "S");
|
||||||
|
Vehicle vehicle = new Vehicle("V001", VehicleType.LIGHT, 10.5, route);
|
||||||
|
vehicle.addWaitingTime(2.3);
|
||||||
|
vehicle.addCrossingTime(1.2);
|
||||||
|
|
||||||
|
// Create a message containing the vehicle
|
||||||
|
Message message = new Message(
|
||||||
|
MessageType.VEHICLE_TRANSFER,
|
||||||
|
"Cr1",
|
||||||
|
"Cr2",
|
||||||
|
vehicle
|
||||||
|
);
|
||||||
|
|
||||||
|
// ===== JSON Serialization =====
|
||||||
|
demonstrateJsonSerialization(message);
|
||||||
|
|
||||||
|
// ===== Factory Usage =====
|
||||||
|
demonstrateFactoryUsage(message);
|
||||||
|
|
||||||
|
// ===== Performance Test =====
|
||||||
|
performanceTest(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void demonstrateJsonSerialization(Message message) {
|
||||||
|
System.out.println("--- JSON Serialization ---");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create JSON serializer with pretty printing for readability
|
||||||
|
MessageSerializer serializer = new JsonMessageSerializer(true);
|
||||||
|
|
||||||
|
// Serialize to bytes
|
||||||
|
byte[] data = serializer.serialize(message);
|
||||||
|
|
||||||
|
// Display the JSON
|
||||||
|
String json = new String(data);
|
||||||
|
System.out.println("Serialized JSON (" + data.length + " bytes):");
|
||||||
|
System.out.println(json);
|
||||||
|
|
||||||
|
// Deserialize back
|
||||||
|
Message deserialized = serializer.deserialize(data, Message.class);
|
||||||
|
System.out.println("\nDeserialized: " + deserialized);
|
||||||
|
System.out.println("✓ JSON serialization successful\n");
|
||||||
|
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
System.err.println("❌ JSON serialization failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void demonstrateFactoryUsage(Message message) {
|
||||||
|
System.out.println("--- Using SerializerFactory ---");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get default serializer (JSON)
|
||||||
|
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||||
|
System.out.println("Default serializer: " + serializer.getName());
|
||||||
|
|
||||||
|
// Use it
|
||||||
|
byte[] data = serializer.serialize(message);
|
||||||
|
Message deserialized = serializer.deserialize(data, Message.class);
|
||||||
|
|
||||||
|
System.out.println("Message type: " + deserialized.getType());
|
||||||
|
System.out.println("From: " + deserialized.getSenderId() +
|
||||||
|
" → To: " + deserialized.getDestinationId());
|
||||||
|
System.out.println("✓ Factory usage successful\n");
|
||||||
|
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
System.err.println("❌ Factory usage failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void performanceTest(Message message) {
|
||||||
|
System.out.println("--- Performance Test ---");
|
||||||
|
|
||||||
|
int iterations = 1000;
|
||||||
|
|
||||||
|
try {
|
||||||
|
MessageSerializer compactSerializer = new JsonMessageSerializer(false);
|
||||||
|
MessageSerializer prettySerializer = new JsonMessageSerializer(true);
|
||||||
|
|
||||||
|
// Warm up
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
compactSerializer.serialize(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test compact JSON
|
||||||
|
long compactStart = System.nanoTime();
|
||||||
|
byte[] compactData = null;
|
||||||
|
for (int i = 0; i < iterations; i++) {
|
||||||
|
compactData = compactSerializer.serialize(message);
|
||||||
|
}
|
||||||
|
long compactTime = System.nanoTime() - compactStart;
|
||||||
|
|
||||||
|
// Test pretty JSON
|
||||||
|
byte[] prettyData = prettySerializer.serialize(message);
|
||||||
|
|
||||||
|
// Results
|
||||||
|
System.out.println("Iterations: " + iterations);
|
||||||
|
System.out.println("\nJSON Compact:");
|
||||||
|
System.out.println(" Size: " + compactData.length + " bytes");
|
||||||
|
System.out.println(" Time: " + (compactTime / 1_000_000.0) + " ms total");
|
||||||
|
System.out.println(" Avg: " + (compactTime / iterations / 1_000.0) + " μs/operation");
|
||||||
|
|
||||||
|
System.out.println("\nJSON Pretty-Print:");
|
||||||
|
System.out.println(" Size: " + prettyData.length + " bytes");
|
||||||
|
System.out.println(" Size increase: " +
|
||||||
|
String.format("%.1f%%", ((double)prettyData.length / compactData.length - 1) * 100));
|
||||||
|
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
System.err.println("❌ Performance test failed: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when serialization or deserialization operations fail.
|
||||||
|
*
|
||||||
|
* This exception wraps underlying errors (I/O exceptions, parsing errors, etc.)
|
||||||
|
* and provides context about what went wrong during the serialization process.
|
||||||
|
*/
|
||||||
|
public class SerializationException extends Exception {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L; // Long(64bits) instead of int(32bits)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new serialization exception with the specified detail message.
|
||||||
|
*
|
||||||
|
* @param message The detail message
|
||||||
|
*/
|
||||||
|
public SerializationException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new serialization exception with the specified detail message
|
||||||
|
* and cause.
|
||||||
|
*
|
||||||
|
* @param message The detail message
|
||||||
|
* @param cause The cause of this exception
|
||||||
|
*/
|
||||||
|
public SerializationException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new serialization exception with the specified cause.
|
||||||
|
*
|
||||||
|
* @param cause The cause of this exception
|
||||||
|
*/
|
||||||
|
public SerializationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
main/src/main/java/sd/serialization/SerializerFactory.java
Normal file
66
main/src/main/java/sd/serialization/SerializerFactory.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory for creating {@link MessageSerializer} instances.
|
||||||
|
*
|
||||||
|
* This factory provides a centralized way to create and configure JSON serializers
|
||||||
|
* using Gson, making it easy to configure serialization throughout the application.
|
||||||
|
*
|
||||||
|
* The factory can be configured via system properties for easy deployment configuration.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* <pre>
|
||||||
|
* MessageSerializer serializer = SerializerFactory.createDefault();
|
||||||
|
* byte[] data = serializer.serialize(myObject);
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class SerializerFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System property key for enabling pretty-print in JSON serialization.
|
||||||
|
* Set to "true" for debugging, "false" for production.
|
||||||
|
*/
|
||||||
|
public static final String JSON_PRETTY_PRINT_PROPERTY = "sd.serialization.json.prettyPrint";
|
||||||
|
|
||||||
|
// Default configuration
|
||||||
|
private static final boolean DEFAULT_JSON_PRETTY_PRINT = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor to prevent instantiation.
|
||||||
|
*/
|
||||||
|
private SerializerFactory() {
|
||||||
|
throw new UnsupportedOperationException("Factory class cannot be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JSON serializer based on system configuration.
|
||||||
|
*
|
||||||
|
* Pretty-print is determined by checking the system property
|
||||||
|
* {@value #JSON_PRETTY_PRINT_PROPERTY}. If not set, defaults to false.
|
||||||
|
*
|
||||||
|
* @return A configured JsonMessageSerializer instance
|
||||||
|
*/
|
||||||
|
public static MessageSerializer createDefault() {
|
||||||
|
boolean prettyPrint = Boolean.getBoolean(JSON_PRETTY_PRINT_PROPERTY);
|
||||||
|
return new JsonMessageSerializer(prettyPrint);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JSON serializer with default configuration (no pretty printing).
|
||||||
|
*
|
||||||
|
* @return A JsonMessageSerializer instance
|
||||||
|
*/
|
||||||
|
public static MessageSerializer createSerializer() {
|
||||||
|
return createSerializer(DEFAULT_JSON_PRETTY_PRINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JSON serializer with specified pretty-print setting.
|
||||||
|
*
|
||||||
|
* @param prettyPrint Whether to enable pretty printing
|
||||||
|
* @return A JsonMessageSerializer instance
|
||||||
|
*/
|
||||||
|
public static MessageSerializer createSerializer(boolean prettyPrint) {
|
||||||
|
return new JsonMessageSerializer(prettyPrint);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,7 +43,7 @@ class SimulationTest {
|
|||||||
assertEquals("TEST1", vehicle.getId());
|
assertEquals("TEST1", vehicle.getId());
|
||||||
assertNotNull(vehicle.getType());
|
assertNotNull(vehicle.getType());
|
||||||
assertNotNull(vehicle.getRoute());
|
assertNotNull(vehicle.getRoute());
|
||||||
assertTrue(vehicle.getRoute().size() > 0);
|
assertTrue(!vehicle.getRoute().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
140
main/src/test/java/sd/serialization/SerializationTest.java
Normal file
140
main/src/test/java/sd/serialization/SerializationTest.java
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package sd.serialization;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import sd.model.Message;
|
||||||
|
import sd.model.Vehicle;
|
||||||
|
import sd.model.VehicleType;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for JSON serialization.
|
||||||
|
*
|
||||||
|
* Tests JSON serialization to ensure:
|
||||||
|
* - Correct serialization and deserialization
|
||||||
|
* - Data integrity during round-trip conversion
|
||||||
|
* - Proper error handling
|
||||||
|
*/
|
||||||
|
class SerializationTest {
|
||||||
|
|
||||||
|
private MessageSerializer jsonSerializer = new JsonMessageSerializer();
|
||||||
|
|
||||||
|
private Vehicle testVehicle = new Vehicle("V001", VehicleType.LIGHT, 10.5,
|
||||||
|
Arrays.asList("Cr1", "Cr2", "Cr5", "S"));
|
||||||
|
private Message testMessage = new Message(
|
||||||
|
sd.model.MessageType.VEHICLE_TRANSFER,
|
||||||
|
"Cr1",
|
||||||
|
"Cr2",
|
||||||
|
testVehicle
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
// ===== JSON Serialization Tests =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should serialize and deserialize Vehicle correctly")
|
||||||
|
void testJsonVehicleRoundTrip() throws SerializationException {
|
||||||
|
// Serialize
|
||||||
|
byte[] data = jsonSerializer.serialize(testVehicle);
|
||||||
|
assertNotNull(data);
|
||||||
|
assertTrue(data.length > 0);
|
||||||
|
|
||||||
|
// Print JSON for inspection
|
||||||
|
System.out.println("JSON Vehicle:");
|
||||||
|
System.out.println(new String(data));
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
Vehicle deserialized = jsonSerializer.deserialize(data, Vehicle.class);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertNotNull(deserialized);
|
||||||
|
assertEquals(testVehicle.getId(), deserialized.getId());
|
||||||
|
assertEquals(testVehicle.getType(), deserialized.getType());
|
||||||
|
assertEquals(testVehicle.getEntryTime(), deserialized.getEntryTime());
|
||||||
|
assertEquals(testVehicle.getRoute(), deserialized.getRoute());
|
||||||
|
assertEquals(testVehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime());
|
||||||
|
assertEquals(testVehicle.getTotalCrossingTime(), deserialized.getTotalCrossingTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should serialize and deserialize Message correctly")
|
||||||
|
void testJsonMessageRoundTrip() throws SerializationException {
|
||||||
|
// Serialize
|
||||||
|
byte[] data = jsonSerializer.serialize(testMessage);
|
||||||
|
assertNotNull(data);
|
||||||
|
|
||||||
|
// Print JSON for inspection
|
||||||
|
System.out.println("\nJSON Message:");
|
||||||
|
System.out.println(new String(data));
|
||||||
|
|
||||||
|
// Deserialize
|
||||||
|
Message deserialized = jsonSerializer.deserialize(data, Message.class);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertNotNull(deserialized);
|
||||||
|
assertEquals(testMessage.getType(), deserialized.getType());
|
||||||
|
assertEquals(testMessage.getSenderId(), deserialized.getSenderId());
|
||||||
|
assertEquals(testMessage.getDestinationId(), deserialized.getDestinationId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should throw exception on null object")
|
||||||
|
void testJsonSerializeNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
jsonSerializer.serialize(null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should throw exception on null data")
|
||||||
|
void testJsonDeserializeNull() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> {
|
||||||
|
jsonSerializer.deserialize(null, Vehicle.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should throw exception on invalid JSON")
|
||||||
|
void testJsonDeserializeInvalid() {
|
||||||
|
byte[] invalidData = "{ invalid json }".getBytes();
|
||||||
|
assertThrows(SerializationException.class, () -> {
|
||||||
|
jsonSerializer.deserialize(invalidData, Vehicle.class);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("JSON: Should preserve data integrity for complex objects")
|
||||||
|
void testDataIntegrity() throws SerializationException {
|
||||||
|
// Create a more complex vehicle
|
||||||
|
Vehicle vehicle = new Vehicle("V999", VehicleType.HEAVY, 100.5,
|
||||||
|
Arrays.asList("Cr1", "Cr2", "Cr3", "Cr4", "Cr5", "S"));
|
||||||
|
vehicle.addWaitingTime(10.5);
|
||||||
|
vehicle.addWaitingTime(5.3);
|
||||||
|
vehicle.addCrossingTime(2.1);
|
||||||
|
vehicle.advanceRoute();
|
||||||
|
vehicle.advanceRoute();
|
||||||
|
|
||||||
|
// Serialize and deserialize
|
||||||
|
byte[] jsonData = jsonSerializer.serialize(vehicle);
|
||||||
|
Vehicle deserialized = jsonSerializer.deserialize(jsonData, Vehicle.class);
|
||||||
|
|
||||||
|
// Verify all fields match
|
||||||
|
assertEquals(vehicle.getId(), deserialized.getId());
|
||||||
|
assertEquals(vehicle.getType(), deserialized.getType());
|
||||||
|
assertEquals(vehicle.getTotalWaitingTime(), deserialized.getTotalWaitingTime());
|
||||||
|
assertEquals(vehicle.getCurrentRouteIndex(), deserialized.getCurrentRouteIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== Factory Tests =====
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Factory: Should create JSON serializer by default")
|
||||||
|
void testFactoryDefault() {
|
||||||
|
MessageSerializer serializer = SerializerFactory.createDefault();
|
||||||
|
assertNotNull(serializer);
|
||||||
|
assertEquals("JSON (Gson)", serializer.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user