mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 20:43:32 +00:00
removed empty impl test files
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
*.log
|
*.log
|
||||||
|
*.trace
|
||||||
|
logs
|
||||||
|
*.md
|
||||||
|
|
||||||
# BlueJ files
|
# BlueJ files
|
||||||
*.ctxt
|
*.ctxt
|
||||||
|
|||||||
620
README.md
620
README.md
@@ -1,620 +0,0 @@
|
|||||||
# 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
134
STEP2_SUMMARY.md
@@ -1,134 +0,0 @@
|
|||||||
# 🏁 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.
|
|
||||||
198
TODO.md
198
TODO.md
@@ -1,198 +0,0 @@
|
|||||||
## ✅ 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
|
|
||||||
|
|
||||||
Primeiro, as tecnologias e paradigmas chave necessários para este projeto devem ser totalmente compreendidos.
|
|
||||||
|
|
||||||
- **Processos vs. Threads:** O projeto especifica o uso de ambos.
|
|
||||||
|
|
||||||
- **Processos (para Cruzamentos)** são programas independentes, cada um com o seu próprio espaço de memória. Em Java, cada cruzamento será provavelmente executado como uma aplicação Java separada (uma instância distinta da JVM).
|
|
||||||
|
|
||||||
- **Threads (para Semáforos)** existem _dentro_ de um processo e partilham memória. Isto é adequado para os semáforos, pois eles precisam de ser coordenados e partilhar dados (como filas de veículos) dentro do mesmo cruzamento.
|
|
||||||
|
|
||||||
- **Comunicação Entre Processos (IPC - Inter-Process Communication):** Como os cruzamentos são processos separados, é necessário um método para que eles comuniquem. **Sockets** são o método especificado. Quando um veículo sai de um cruzamento (ex: `Cr1`) e vai para outro (ex: `Cr2`), o processo `Cr1` precisa de enviar uma mensagem contendo os dados do veículo para o processo `Cr2` através de uma conexão por socket.
|
|
||||||
|
|
||||||
- **Simulação de Eventos Discretos (DES - Discrete-Event Simulation):** Este é o paradigma de simulação que deve ser utilizado. Em vez de o tempo fluir continuamente, o relógio da simulação salta de um evento para o seguinte.
|
|
||||||
|
|
||||||
- Um **evento** é um objeto que representa algo que acontece num ponto específico no tempo (ex: "Veículo A chega ao Cr2 no tempo 15.7s").
|
|
||||||
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Uma Sugestão de Arquitetura de Alto Nível
|
|
||||||
|
|
||||||
Abaixo, é apresentada uma possível estrutura para a aplicação distribuída. Pode ser vista como um conjunto de programas independentes que comunicam através de uma rede.
|
|
||||||
|
|
||||||
1. **Processo Coordenador/Gerador (1 Processo):**
|
|
||||||
|
|
||||||
- **Propósito:** Iniciar a simulação, gerar veículos e gerir o relógio global da simulação ou os critérios de paragem.
|
|
||||||
|
|
||||||
- **Responsabilidades:**
|
|
||||||
|
|
||||||
- Lê a configuração da simulação (ex: carga de tráfego λi, tempos dos semáforos).
|
|
||||||
|
|
||||||
- Gera veículos de acordo com o modelo selecionado (intervalo fixo ou processo de Poisson).
|
|
||||||
|
|
||||||
- Atribui a cada novo veículo um percurso com base na distribuição uniforme especificada.
|
|
||||||
|
|
||||||
- Injeta o veículo no sistema enviando uma mensagem para o primeiro processo de cruzamento no seu percurso (ex: de um ponto de entrada E1 para Cr1).
|
|
||||||
|
|
||||||
2. **Processos de Cruzamento (5 Processos):**
|
|
||||||
|
|
||||||
- **Propósito:** Simular cada cruzamento (`Cr1` a `Cr5`) como um processo distinto.
|
|
||||||
|
|
||||||
- **Responsabilidades:**
|
|
||||||
|
|
||||||
- Escuta por veículos a chegar de outros processos.
|
|
||||||
|
|
||||||
- Gere as filas de veículos para os seus semáforos.
|
|
||||||
|
|
||||||
- Executa múltiplas **threads de Semáforo** internamente.
|
|
||||||
|
|
||||||
- Coordena estas threads para garantir que apenas uma direção de tráfego está aberta a cada momento.
|
|
||||||
|
|
||||||
- Quando um veículo atravessa, é encaminhado para o processo seguinte no seu percurso.
|
|
||||||
|
|
||||||
- Envia periodicamente as suas estatísticas (ex: comprimentos atuais das filas) para o Servidor do Dashboard.
|
|
||||||
|
|
||||||
3. **Processo de Nó de Saída (1 Processo):**
|
|
||||||
|
|
||||||
- **Propósito:** Representar o ponto de saída `S` e atuar como um coletor de dados para estatísticas globais.
|
|
||||||
|
|
||||||
- **Responsabilidades:**
|
|
||||||
|
|
||||||
- Recebe veículos que completaram o seu percurso.
|
|
||||||
|
|
||||||
- Calcula métricas globais como o tempo total de viagem (tempo de permanência) para cada veículo.
|
|
||||||
|
|
||||||
- Agrega e calcula as estatísticas finais (ex: tempo de viagem mínimo, máximo e médio por tipo de veículo).
|
|
||||||
|
|
||||||
- Envia estas estatísticas globais para o Servidor do Dashboard.
|
|
||||||
|
|
||||||
4. **Processo do Servidor do Dashboard (1 Processo):**
|
|
||||||
|
|
||||||
- **Propósito:** Agregar e exibir todos os dados da simulação em tempo real.
|
|
||||||
|
|
||||||
- **Responsabilidades:**
|
|
||||||
|
|
||||||
- Abre um socket de servidor e escuta por dados a chegar de todos os processos de Cruzamento e de Saída.
|
|
||||||
|
|
||||||
- Armazena e atualiza as estatísticas à medida que chegam.
|
|
||||||
|
|
||||||
- Apresenta os dados numa interface de utilizador, que deve exibir métricas e ser atualizada durante a simulação.
|
|
||||||
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Plano
|
|
||||||
|
|
||||||
Nem tudo deve ser construído de uma só vez. Os seguintes passos incrementais são recomendados.
|
|
||||||
|
|
||||||
#### **Passo 1: Modelação e Classes Principais (Não-distribuído)**
|
|
||||||
|
|
||||||
Antes de escrever qualquer lógica complexa, as estruturas de dados devem ser definidas. Devem ser criados Plain Old Java Objects (POJOs) para:
|
|
||||||
|
|
||||||
- `Veiculo`: Com atributos como um identificador único, tipo, tempo de entrada e o percurso realizado. Deve ser tornado `Serializable` para que possa ser enviado através de sockets.
|
|
||||||
|
|
||||||
- `Evento`: Com atributos como um timestamp e o tipo de evento (ex: `VEHICLE_ARRIVAL`), bem como dados associados.
|
|
||||||
|
|
||||||
- `Semaforo`: Para conter o seu estado (`VERDE`/`VERMELHO`) e a fila de veículos.
|
|
||||||
|
|
||||||
- `Cruzamento`: Para conter os seus semáforos e a lógica operacional.
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 2: Construir um Protótipo de Processo Único**
|
|
||||||
|
|
||||||
Este é um passo crucial. Sockets e processos devem ser deixados de lado por agora para construir toda a simulação numa única aplicação Java.
|
|
||||||
|
|
||||||
- Deve ser criado um ciclo de simulação central baseado numa fila de prioridades para objetos `Evento`.
|
|
||||||
|
|
||||||
- Todos os objetos `Cruzamento` e `Semaforo` devem ser instanciados.
|
|
||||||
|
|
||||||
- A lógica principal deve ser tornada funcional: veículos a moverem-se entre filas, semáforos a mudar de estado e estatísticas básicas a serem recolhidas.
|
|
||||||
|
|
||||||
- **Objetivo:** Uma simulação totalmente funcional e não-distribuída. Isto torna a depuração significativamente mais fácil.
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 3: Distribuir os Cruzamentos**
|
|
||||||
|
|
||||||
O protótipo pode agora ser convertido num sistema distribuído.
|
|
||||||
|
|
||||||
- A classe `Cruzamento` deve ser tornada executável como uma aplicação Java autónoma (com um método `main`). Serão lançadas cinco instâncias, uma para cada cruzamento.
|
|
||||||
|
|
||||||
- Devem ser configurados sockets TCP para comunicação. Cada processo de cruzamento precisa de saber o endereço/porta dos vizinhos para os quais pode enviar veículos.
|
|
||||||
|
|
||||||
- Um **protocolo de comunicação** claro deve ser definido. Por exemplo, quando `Cr1` envia um veículo para `Cr2`, o objeto `Veiculo` é serializado e escrito no socket conectado a `Cr2`. O processo `Cr2` terá uma thread dedicada para escutar estas conexões de entrada.
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 4: Implementar as Threads dos Semáforos**
|
|
||||||
|
|
||||||
Dentro de cada processo `Cruzamento`, os semáforos devem ser implementados como threads.
|
|
||||||
|
|
||||||
- O principal desafio aqui é a **sincronização**. As threads dos semáforos num único cruzamento partilham as filas de veículos.
|
|
||||||
|
|
||||||
- As ferramentas de concorrência do Java (como `synchronized`, `ReentrantLock`, `Semaphore`) devem ser usadas para garantir que apenas um semáforo pode estar verde para um percurso conflituante e que o acesso às filas partilhadas é seguro (thread-safe).
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 5: Implementar o Dashboard**
|
|
||||||
|
|
||||||
- O processo `DashboardServer` deve ser criado. Ele irá escutar numa porta específica por estatísticas a chegar.
|
|
||||||
|
|
||||||
- Nos processos `Cruzamento` e `Saida`, deve ser adicionado um mecanismo para enviar periodicamente um resumo das suas estatísticas atuais para o Servidor do Dashboard.
|
|
||||||
|
|
||||||
- A UI deve ser construída para exibir estes dados em tempo real.
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 6: Testes e Análise**
|
|
||||||
|
|
||||||
Assim que o sistema completo estiver a funcionar, as experiências exigidas pela descrição do projeto podem ser realizadas.
|
|
||||||
|
|
||||||
- A simulação deve ser executada com diferentes taxas de chegada de veículos para simular cargas baixas, médias e altas.
|
|
||||||
|
|
||||||
- Diferentes políticas de temporização dos semáforos devem ser testadas para medir o seu impacto no congestionamento.
|
|
||||||
|
|
||||||
- Diferentes algoritmos de seleção de percurso e o seu impacto no desempenho do sistema devem ser avaliados.
|
|
||||||
|
|
||||||
- Para cada cenário, a simulação deve ser executada várias vezes para recolher estatísticas fiáveis (médias, desvios padrão, intervalos de confiança), conforme solicitado.
|
|
||||||
|
|
||||||
|
|
||||||
#### **Passo 7: Escrever o Relatório**
|
|
||||||
|
|
||||||
À medida que cada passo é concluído, deve ser documentado. Isto tornará a escrita do relatório final muito mais fácil. Todos os pontos mencionados nas secções "Entrega" e "Critérios de Avaliação" devem ser abordados.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### OBS:
|
|
||||||
|
|
||||||
- **Começar de Forma Simples:** O protótipo de processo único (Passo 2) evitará grandes dificuldades mais tarde.
|
|
||||||
|
|
||||||
- **Protocolo de Comunicação:** O protocolo de mensagens deve ser definido o mais cedo possível. A informação exata que um processo envia para outro deve ser clara//simples//consistente.
|
|
||||||
|
|
||||||
- **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.
|
|
||||||
Reference in New Issue
Block a user