mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 12:33:31 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30fc2d6554 | ||
|
|
d41973d27f | ||
|
|
ce226f261a | ||
| 874fd53a21 | |||
|
|
08b254b8de | ||
|
|
19bf313c81 | ||
|
|
cfb24b21bf | ||
|
|
b9991ba6ba | ||
|
|
651dc754b8 |
50
.gitignore
vendored
Normal file
50
.gitignore
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
# Compiled class files
|
||||
*.class
|
||||
|
||||
# Log files
|
||||
*.log
|
||||
|
||||
# BlueJ files
|
||||
*.ctxt
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# VS Code settings
|
||||
.vscode/
|
||||
|
||||
# Eclipse files
|
||||
*.pydevproject
|
||||
.project
|
||||
.classpath
|
||||
.cproject
|
||||
.settings/
|
||||
bin/
|
||||
tmp/
|
||||
|
||||
# IntelliJ IDEA files
|
||||
*.iml
|
||||
.idea/
|
||||
out/
|
||||
|
||||
# Mac system files
|
||||
.DS_Store
|
||||
|
||||
# Windows system files
|
||||
Thumbs.db
|
||||
|
||||
# Maven
|
||||
target/
|
||||
|
||||
# Gradle
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# Other
|
||||
*.swp
|
||||
*.pdf
|
||||
27
Diagrama de arquitetura - SD.drawio
Normal file
27
Diagrama de arquitetura - SD.drawio
Normal file
@@ -0,0 +1,27 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0" version="28.2.7">
|
||||
<diagram name="Página-1" id="B1_hHcevBzWlEwI7FSV6">
|
||||
<mxGraphModel dx="778" dy="476" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="vcp7vux32DhQR4tKQhnF-8" value="Dashboard" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=#C73500;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;fillColor=#fa6800;shape=mxgraph.mscae.oms.dashboard;fontColor=#000000;" vertex="1" parent="1">
|
||||
<mxGeometry x="389" y="230" width="50" height="41" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="vcp7vux32DhQR4tKQhnF-12" value="Semaforo.java" style="shape=image;html=1;verticalAlign=top;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;imageAspect=0;aspect=fixed;image=https://icons.diagrams.net/icon-cache1/Strabo-2829/traffic_light-1068.png" vertex="1" parent="1">
|
||||
<mxGeometry x="230" y="350" width="53" height="53" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="vcp7vux32DhQR4tKQhnF-13" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;movable=1;resizable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" edge="1" parent="1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="310" y="330" as="sourcePoint" />
|
||||
<mxPoint x="360" y="280" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="vcp7vux32DhQR4tKQhnF-14" value="CruzamentoServer.java" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=1;points=[];movable=1;rotatable=1;deletable=1;editable=1;locked=0;connectable=1;" vertex="1" connectable="0" parent="vcp7vux32DhQR4tKQhnF-13">
|
||||
<mxGeometry x="-0.3933" relative="1" as="geometry">
|
||||
<mxPoint x="25" y="25" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
175
TODO.md
Normal file
175
TODO.md
Normal file
@@ -0,0 +1,175 @@
|
||||
### 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.
|
||||
172
arquitetura_simplificada.xml
Normal file
172
arquitetura_simplificada.xml
Normal file
@@ -0,0 +1,172 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<mxfile host="app.diagrams.net" agent="Gemini" version="28.2.7">
|
||||
<diagram name="Arquitetura-Sistema-Trafego" id="L-jWkP8vD7q_2fM6N-yC">
|
||||
<mxGraphModel dx="1434" dy="746" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1654" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="E1-process" value="Processo Gerador E1" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="100" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="E2-process" value="Processo Gerador E2" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="100" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="E3-process" value="Processo Gerador E3" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="720" y="100" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr1-process" value="Processo Cruzamento (Cr1)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;verticalAlign=top;spacingTop=5;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="240" width="140" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr1-thread1" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr1-process">
|
||||
<mxGeometry x="20" y="30" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr1-thread2" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr1-process">
|
||||
<mxGeometry x="20" y="65" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr2-process" value="Processo Cruzamento (Cr2)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;verticalAlign=top;spacingTop=5;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="240" width="140" height="140" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr2-thread1" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr2-process">
|
||||
<mxGeometry x="20" y="30" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr2-thread2" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr2-process">
|
||||
<mxGeometry x="20" y="65" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr2-thread3" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr2-process">
|
||||
<mxGeometry x="20" y="100" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr3-process" value="Processo Cruzamento (Cr3)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;verticalAlign=top;spacingTop=5;" vertex="1" parent="1">
|
||||
<mxGeometry x="720" y="240" width="140" height="140" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr3-thread1" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr3-process">
|
||||
<mxGeometry x="20" y="30" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr3-thread2" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr3-process">
|
||||
<mxGeometry x="20" y="65" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr3-thread3" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr3-process">
|
||||
<mxGeometry x="20" y="100" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr4-process" value="Processo Cruzamento (Cr4)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;verticalAlign=top;spacingTop=5;" vertex="1" parent="1">
|
||||
<mxGeometry x="140" y="460" width="140" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr4-thread1" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr4-process">
|
||||
<mxGeometry x="20" y="30" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr4-thread-peao" value="Thread Semáforo (Peões)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr4-process">
|
||||
<mxGeometry x="20" y="65" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr5-process" value="Processo Cruzamento (Cr5)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;verticalAlign=top;spacingTop=5;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="460" width="140" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr5-thread1" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr5-process">
|
||||
<mxGeometry x="20" y="30" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Cr5-thread2" value="Thread Semáforo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="Cr5-process">
|
||||
<mxGeometry x="20" y="65" width="100" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="S-process" value="Processo Saída (S)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
|
||||
<mxGeometry x="720" y="460" width="140" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="dashboard-server" value="Servidor Dashboard" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
|
||||
<mxGeometry x="430" y="640" width="140" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-E1-Cr1" value="Fluxo Veículos<br>(Sockets/Middleware)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;fontSize=10;align=left;verticalAlign=bottom;" edge="1" parent="1" source="E1-process" target="Cr1-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-E2-Cr2" value="Fluxo Veículos<br>(Sockets/Middleware)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;fontSize=10;align=left;verticalAlign=bottom;" edge="1" parent="1" source="E2-process" target="Cr2-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-E3-Cr3" value="Fluxo Veículos<br>(Sockets/Middleware)" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;fontSize=10;align=left;verticalAlign=bottom;" edge="1" parent="1" source="E3-process" target="Cr3-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr1-Cr4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr1-process" target="Cr4-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr2-Cr5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr2-process" target="Cr5-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr4-Cr5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr4-process" target="Cr5-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr5-S" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr5-process" target="S-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr3-S" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr3-process" target="S-process">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr1-Cr2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr1-process" target="Cr2-process">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="280" y="290" />
|
||||
<mxPoint x="430" y="290" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr2-Cr1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr2-process" target="Cr1-process">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="430" y="310" />
|
||||
<mxPoint x="280" y="310" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr2-Cr3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr2-process" target="Cr3-process">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="570" y="290" />
|
||||
<mxPoint x="720" y="290" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="arrow-Cr3-Cr2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="Cr3-process" target="Cr2-process">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="720" y="310" />
|
||||
<mxPoint x="570" y="310" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="stats-Cr1-Dash" value="Envio de Estatísticas" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;fontSize=10;verticalAlign=bottom;" edge="1" parent="1" source="Cr1-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="210" y="680" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="stats-Cr2-Dash" value="Envio de Estatísticas" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;fontSize=10;verticalAlign=bottom;" edge="1" parent="1" source="Cr2-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="stats-Cr3-Dash" value="Envio de Estatísticas" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;fontSize=10;verticalAlign=bottom;" edge="1" parent="1" source="Cr3-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="790" y="680" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="stats-Cr4-Dash" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;" edge="1" parent="1" source="Cr4-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="210" y="680" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="stats-Cr5-Dash" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;" edge="1" parent="1" source="Cr5-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="stats-S-Dash" value="Estatísticas Globais" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;dashed=1;strokeColor=#9673a6;fontSize=10;verticalAlign=bottom;" edge="1" parent="1" source="S-process" target="dashboard-server">
|
||||
<mxGeometry relative="1" as="geometry">
|
||||
<Array as="points">
|
||||
<mxPoint x="790" y="680" />
|
||||
</Array>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="legend" value="Legenda simplificada (removida tabela)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#999999;" vertex="1" parent="1">
|
||||
</mxCell>
|
||||
<mxCell id="title" value="Diagrama de Arquitetura - Simulador de Tráfego Distribuído" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=18;fontStyle=1" vertex="1" parent="1">
|
||||
<mxGeometry x="290" y="40" width="420" height="30" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -1,34 +0,0 @@
|
||||
package sd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
public class CruzamentoServer {
|
||||
public static void main(String[] args) {
|
||||
// ... Inicializa Semáforos (Threads) ...
|
||||
// ... Inicializa as Estruturas de Dados ...
|
||||
|
||||
try (ServerSocket serverSocket = new ServerSocket(portaDoCruzamento)) {
|
||||
while (true) {
|
||||
Socket clienteSocket = serverSocket.accept();
|
||||
// Cria uma Thread de atendimento para lidar com o Veículo/Cliente
|
||||
new Thread(new AtendenteVeiculo(clienteSocket)).start();
|
||||
}
|
||||
} catch (IOException e) { /* ... */ }
|
||||
}
|
||||
|
||||
// Método chamado pelo AtendenteVeiculo para gerenciar o tráfego
|
||||
public synchronized boolean tentarPassar(Veiculo veiculo, String direcao) {
|
||||
// 1. Veículo entra na fila da direção
|
||||
// 2. Verifica o estado do semáforo da direção:
|
||||
Semaforo semaforo = getSemaforo(direcao);
|
||||
semaforo.esperarPeloVerde(); // O Veículo fica bloqueado se for vermelho
|
||||
|
||||
// 3. Após o verde:
|
||||
// - Remove da fila
|
||||
// - Permite a passagem (envia resposta de volta ao Veículo cliente)
|
||||
// 4. Envia estatística de passagem ao Simulador Principal (Cliente TCP)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
7
main/src/main/java/sd/Entry.java
Normal file
7
main/src/main/java/sd/Entry.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package sd;
|
||||
|
||||
public class Entry {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello, World!");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package sd;
|
||||
|
||||
public class Main {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package sd;
|
||||
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
public class Semaforo extends Thread {
|
||||
// ... atributos ...
|
||||
private final Lock semaforoLock; // Para sincronizar acesso ao estado
|
||||
private final Condition verdeCondition; // Para Veículos esperarem pelo verde
|
||||
|
||||
public Semaforo(...) {
|
||||
this.semaforoLock = new ReentrantLock();
|
||||
this.verdeCondition = semaforoLock.newCondition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (true) {
|
||||
// Ciclo de tempo (ajustável para controle)
|
||||
estado = Estado.VERMELHO;
|
||||
// Notificar o Cruzamento sobre o estado
|
||||
try {
|
||||
Thread.sleep(tempoVermelho);
|
||||
estado = Estado.VERDE;
|
||||
// Ao ficar VERDE, notifica as threads Veículo que estão esperando
|
||||
semaforoLock.lock();
|
||||
try {
|
||||
verdeCondition.signalAll();
|
||||
} finally {
|
||||
semaforoLock.unlock();
|
||||
}
|
||||
Thread.sleep(tempoVerde);
|
||||
} catch (InterruptedException e) { /* ... */ }
|
||||
}
|
||||
}
|
||||
// Método para a thread Veículo esperar
|
||||
public void esperarPeloVerde() throws InterruptedException {
|
||||
semaforoLock.lock();
|
||||
try {
|
||||
if (estado == Estado.VERMELHO) {
|
||||
verdeCondition.await();
|
||||
}
|
||||
} finally {
|
||||
semaforoLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package sd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
|
||||
public class Veiculo implements Runnable {
|
||||
// ...
|
||||
private String proximoCruzamentoIP;
|
||||
private int proximoCruzamentoPorta;
|
||||
|
||||
public void run() {
|
||||
// Simular o movimento na rua (Thread.sleep(t))
|
||||
|
||||
// 1. Tenta se conectar ao próximo Cruzamento
|
||||
try (Socket socket = new Socket(proximoCruzamentoIP, proximoCruzamentoPorta);
|
||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream(socket.getInputStream())) {
|
||||
|
||||
// Envia o objeto Veículo com a solicitação de passagem
|
||||
out.writeObject(this);
|
||||
|
||||
// 2. BLOQUEIA a Thread, esperando a resposta do Servidor/Cruzamento
|
||||
String permissao = (String) in.readObject();
|
||||
|
||||
if ("OK_PASSAR".equals(permissao)) {
|
||||
// Simular tempo de travessia do cruzamento (pequeno Thread.sleep())
|
||||
// Atualiza a rota (próximo nó)
|
||||
}
|
||||
|
||||
} catch (IOException | ClassNotFoundException e) { /* ... */ }
|
||||
// ... continua o loop da rota até a Saída (S) ...
|
||||
}
|
||||
}
|
||||
113
main/src/main/java/sd/config/SimulationConfig.java
Normal file
113
main/src/main/java/sd/config/SimulationConfig.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package sd.config;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Class to load and manage simulation configurations.
|
||||
* Configurations are read from a .properties file.
|
||||
*/
|
||||
public class SimulationConfig {
|
||||
private final Properties properties;
|
||||
|
||||
public SimulationConfig(String filePath) throws IOException {
|
||||
properties = new Properties();
|
||||
try (InputStream input = new FileInputStream(filePath)) {
|
||||
properties.load(input);
|
||||
}
|
||||
}
|
||||
|
||||
// Network configurations
|
||||
public String getIntersectionHost(String intersectionId) {
|
||||
return properties.getProperty("intersection." + intersectionId + ".host", "localhost");
|
||||
}
|
||||
|
||||
public int getIntersectionPort(String intersectionId) {
|
||||
return Integer.parseInt(properties.getProperty("intersection." + intersectionId + ".port", "0"));
|
||||
}
|
||||
|
||||
public String getDashboardHost() {
|
||||
return properties.getProperty("dashboard.host", "localhost");
|
||||
}
|
||||
|
||||
public int getDashboardPort() {
|
||||
return Integer.parseInt(properties.getProperty("dashboard.port", "9000"));
|
||||
}
|
||||
|
||||
public String getExitHost() {
|
||||
return properties.getProperty("exit.host", "localhost");
|
||||
}
|
||||
|
||||
public int getExitPort() {
|
||||
return Integer.parseInt(properties.getProperty("exit.port", "9001"));
|
||||
}
|
||||
|
||||
// Simulation configurations
|
||||
public double getSimulationDuration() {
|
||||
return Double.parseDouble(properties.getProperty("simulation.duration", "3600.0"));
|
||||
}
|
||||
|
||||
public String getArrivalModel() {
|
||||
return properties.getProperty("simulation.arrival.model", "POISSON");
|
||||
}
|
||||
|
||||
public double getArrivalRate() {
|
||||
return Double.parseDouble(properties.getProperty("simulation.arrival.rate", "0.5"));
|
||||
}
|
||||
|
||||
public double getFixedArrivalInterval() {
|
||||
return Double.parseDouble(properties.getProperty("simulation.arrival.fixed.interval", "2.0"));
|
||||
}
|
||||
|
||||
// Traffic light configurations
|
||||
public double getTrafficLightGreenTime(String intersectionId, String direction) {
|
||||
String key = "trafficlight." + intersectionId + "." + direction + ".green";
|
||||
return Double.parseDouble(properties.getProperty(key, "30.0"));
|
||||
}
|
||||
|
||||
public double getTrafficLightRedTime(String intersectionId, String direction) {
|
||||
String key = "trafficlight." + intersectionId + "." + direction + ".red";
|
||||
return Double.parseDouble(properties.getProperty(key, "30.0"));
|
||||
}
|
||||
|
||||
// Vehicle configurations
|
||||
public double getLightVehicleProbability() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.probability.light", "0.7"));
|
||||
}
|
||||
|
||||
public double getLightVehicleCrossingTime() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.light", "2.0"));
|
||||
}
|
||||
|
||||
public double getBikeVehicleProbability() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.probability.bike", "0.0"));
|
||||
}
|
||||
|
||||
public double getBikeVehicleCrossingTime() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.bike", "1.5"));
|
||||
}
|
||||
|
||||
public double getHeavyVehicleProbability() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.probability.heavy", "0.0"));
|
||||
}
|
||||
|
||||
public double getHeavyVehicleCrossingTime() {
|
||||
return Double.parseDouble(properties.getProperty("vehicle.crossing.time.heavy", "4.0"));
|
||||
}
|
||||
|
||||
// Statistics
|
||||
public double getStatisticsUpdateInterval() {
|
||||
return Double.parseDouble(properties.getProperty("statistics.update.interval", "10.0"));
|
||||
}
|
||||
|
||||
// Generic method to get any property
|
||||
public String getProperty(String key, String defaultValue) {
|
||||
return properties.getProperty(key, defaultValue);
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
return properties.getProperty(key);
|
||||
}
|
||||
}
|
||||
61
main/src/main/java/sd/model/Event.java
Normal file
61
main/src/main/java/sd/model/Event.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package sd.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Represents an event in the discrete event simulation.
|
||||
* Events are ordered by timestamp for sequential processing.
|
||||
*/
|
||||
public class Event implements Comparable<Event>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final double timestamp; // Time when the event occurs
|
||||
private final EventType type;
|
||||
private final Object data; // Data associated with the event (e.g., Vehicle, traffic light id, etc.)
|
||||
private final String location; // Intersection or location where the event occurs
|
||||
|
||||
public Event(double timestamp, EventType type, Object data, String location) {
|
||||
this.timestamp = timestamp;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public Event(double timestamp, EventType type, Object data) {
|
||||
this(timestamp, type, data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Event other) {
|
||||
// Sort by timestamp (earlier events have priority)
|
||||
int cmp = Double.compare(this.timestamp, other.timestamp);
|
||||
if (cmp == 0) {
|
||||
// If timestamps are equal, sort by event type
|
||||
return this.type.compareTo(other.type);
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public double getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public EventType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Event{t=%.2f, type=%s, loc=%s}",
|
||||
timestamp, type, location);
|
||||
}
|
||||
}
|
||||
13
main/src/main/java/sd/model/EventType.java
Normal file
13
main/src/main/java/sd/model/EventType.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package sd.model;
|
||||
|
||||
/**
|
||||
* Enumeration representing event types in the simulation.
|
||||
*/
|
||||
public enum EventType {
|
||||
VEHICLE_ARRIVAL, // Vehicle arrives at an intersection
|
||||
TRAFFIC_LIGHT_CHANGE, // Traffic light changes state (green/red)
|
||||
CROSSING_START, // Vehicle starts crossing the intersection
|
||||
CROSSING_END, // Vehicle finishes crossing
|
||||
VEHICLE_GENERATION, // New vehicle is generated in the system
|
||||
STATISTICS_UPDATE // Time to send statistics to dashboard
|
||||
}
|
||||
132
main/src/main/java/sd/model/Intersection.java
Normal file
132
main/src/main/java/sd/model/Intersection.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package sd.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents an intersection in the traffic simulation.
|
||||
*
|
||||
* Each intersection coordinates multiple traffic lights - one for each direction -
|
||||
* and handles routing vehicles based on their next destination.
|
||||
*/
|
||||
public class Intersection {
|
||||
|
||||
// Identity and configuration
|
||||
private final String id; // ex. "Cr1", "Cr2"
|
||||
private final Map<String, TrafficLight> trafficLights; // direction -> light
|
||||
private final Map<String, String> routing; // destination -> direction
|
||||
|
||||
// Stats
|
||||
private int totalVehiclesReceived;
|
||||
private int totalVehiclesSent;
|
||||
private double averageWaitingTime;
|
||||
|
||||
public Intersection(String id) {
|
||||
this.id = id;
|
||||
this.trafficLights = new HashMap<>();
|
||||
this.routing = new HashMap<>();
|
||||
this.totalVehiclesReceived = 0;
|
||||
this.totalVehiclesSent = 0;
|
||||
this.averageWaitingTime = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a traffic light under this intersection.
|
||||
* The light is identified by its direction (ex., "North", "East").
|
||||
*/
|
||||
public void addTrafficLight(TrafficLight trafficLight) {
|
||||
trafficLights.put(trafficLight.getDirection(), trafficLight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines how vehicles should be routed through this intersection.
|
||||
*
|
||||
* @param nextDestination The next intersection or exit on the vehicle's route
|
||||
* @param direction The direction (traffic light) vehicles should take
|
||||
*/
|
||||
public void configureRoute(String nextDestination, String direction) {
|
||||
routing.put(nextDestination, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts an incoming vehicle and places it in the correct queue.
|
||||
* If the route or traffic light can't be found, logs an error.
|
||||
*/
|
||||
public void receiveVehicle(Vehicle vehicle) {
|
||||
totalVehiclesReceived++;
|
||||
|
||||
String nextDestination = vehicle.getCurrentDestination();
|
||||
String direction = routing.get(nextDestination);
|
||||
|
||||
if (direction != null && trafficLights.containsKey(direction)) {
|
||||
trafficLights.get(direction).addVehicle(vehicle);
|
||||
} else {
|
||||
System.err.printf(
|
||||
"Routing error: could not place vehicle %s (destination: %s)%n",
|
||||
vehicle.getId(), nextDestination
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the traffic light controlling the given direction, if any. */
|
||||
public TrafficLight getTrafficLight(String direction) {
|
||||
return trafficLights.get(direction);
|
||||
}
|
||||
|
||||
/** Returns all traffic lights belonging to this intersection. */
|
||||
public List<TrafficLight> getTrafficLights() {
|
||||
return new ArrayList<>(trafficLights.values());
|
||||
}
|
||||
|
||||
/** Returns the total number of vehicles currently queued across all directions. */
|
||||
public int getTotalQueueSize() {
|
||||
return trafficLights.values().stream()
|
||||
.mapToInt(TrafficLight::getQueueSize)
|
||||
.sum();
|
||||
}
|
||||
|
||||
// --- Stats and getters ---
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getTotalVehiclesReceived() {
|
||||
return totalVehiclesReceived;
|
||||
}
|
||||
|
||||
public int getTotalVehiclesSent() {
|
||||
return totalVehiclesSent;
|
||||
}
|
||||
|
||||
public void incrementVehiclesSent() {
|
||||
totalVehiclesSent++;
|
||||
}
|
||||
|
||||
public double getAverageWaitingTime() {
|
||||
return averageWaitingTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the running average waiting time with a new sample.
|
||||
*/
|
||||
public void updateAverageWaitingTime(double newTime) {
|
||||
// Weighted incremental average (avoids recalculating from scratch)
|
||||
averageWaitingTime = (averageWaitingTime * (totalVehiclesSent - 1) + newTime)
|
||||
/ totalVehiclesSent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"Intersection{id='%s', lights=%d, queues=%d, received=%d, sent=%d}",
|
||||
id,
|
||||
trafficLights.size(),
|
||||
getTotalQueueSize(),
|
||||
totalVehiclesReceived,
|
||||
totalVehiclesSent
|
||||
);
|
||||
}
|
||||
}
|
||||
180
main/src/main/java/sd/model/TrafficLight.java
Normal file
180
main/src/main/java/sd/model/TrafficLight.java
Normal file
@@ -0,0 +1,180 @@
|
||||
package sd.model;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Represents a single traffic light controlling one direction at an intersection.
|
||||
*
|
||||
* Each light maintains its own queue of vehicles and alternates between
|
||||
* green and red states. It's designed to be thread-safe (maybe...), so multiple
|
||||
* threads (like vehicles or controllers) can safely interact with it.
|
||||
*/
|
||||
public class TrafficLight {
|
||||
|
||||
// Identity and configuration
|
||||
private final String id; // ex. "Cr1-N"
|
||||
private final String direction; // ex. "North", "South", etc.
|
||||
private TrafficLightState state;
|
||||
|
||||
// Vehicle management
|
||||
private final Queue<Vehicle> queue;
|
||||
|
||||
// Synchronization primitives
|
||||
private final Lock lock;
|
||||
private final Condition vehicleAdded;
|
||||
private final Condition lightGreen;
|
||||
|
||||
// Timing configuration (seconds)
|
||||
private double greenTime;
|
||||
private double redTime;
|
||||
|
||||
// Basic stats
|
||||
private int totalVehiclesProcessed;
|
||||
|
||||
public TrafficLight(String id, String direction, double greenTime, double redTime) {
|
||||
this.id = id;
|
||||
this.direction = direction;
|
||||
this.state = TrafficLightState.RED;
|
||||
this.queue = new LinkedList<>();
|
||||
|
||||
this.lock = new ReentrantLock();
|
||||
this.vehicleAdded = lock.newCondition();
|
||||
this.lightGreen = lock.newCondition();
|
||||
|
||||
this.greenTime = greenTime;
|
||||
this.redTime = redTime;
|
||||
this.totalVehiclesProcessed = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a vehicle to the waiting queue.
|
||||
* Signals any waiting threads that a new vehicle has arrived.
|
||||
*/
|
||||
public void addVehicle(Vehicle vehicle) {
|
||||
lock.lock();
|
||||
try {
|
||||
queue.offer(vehicle);
|
||||
vehicleAdded.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to let one vehicle pass through.
|
||||
* Only works if the light is green; otherwise returns null.
|
||||
*/
|
||||
public Vehicle removeVehicle() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (state == TrafficLightState.GREEN && !queue.isEmpty()) {
|
||||
Vehicle vehicle = queue.poll();
|
||||
totalVehiclesProcessed++;
|
||||
return vehicle;
|
||||
}
|
||||
return null;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the light’s state (ex., RED -> GREEN).
|
||||
* When the light turns green, waiting threads are notified.
|
||||
* ¯\_(ツ)_/¯
|
||||
*/
|
||||
public void changeState(TrafficLightState newState) {
|
||||
lock.lock();
|
||||
try {
|
||||
this.state = newState;
|
||||
if (newState == TrafficLightState.GREEN) {
|
||||
lightGreen.signalAll();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns how many vehicles are currently queued. */
|
||||
public int getQueueSize() {
|
||||
lock.lock();
|
||||
try {
|
||||
return queue.size();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/** Checks whether there are no vehicles waiting. */
|
||||
public boolean isQueueEmpty() {
|
||||
lock.lock();
|
||||
try {
|
||||
return queue.isEmpty();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// --- Getters & Setters ---
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public TrafficLightState getState() {
|
||||
lock.lock();
|
||||
try {
|
||||
return state;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public double getGreenTime() {
|
||||
return greenTime;
|
||||
}
|
||||
|
||||
public void setGreenTime(double greenTime) {
|
||||
this.greenTime = greenTime;
|
||||
}
|
||||
|
||||
public double getRedTime() {
|
||||
return redTime;
|
||||
}
|
||||
|
||||
public void setRedTime(double redTime) {
|
||||
this.redTime = redTime;
|
||||
}
|
||||
|
||||
public int getTotalVehiclesProcessed() {
|
||||
return totalVehiclesProcessed;
|
||||
}
|
||||
|
||||
public Lock getLock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
public Condition getVehicleAdded() {
|
||||
return vehicleAdded;
|
||||
}
|
||||
|
||||
public Condition getLightGreen() {
|
||||
return lightGreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"TrafficLight{id='%s', direction='%s', state=%s, queueSize=%d}",
|
||||
id, direction, state, getQueueSize()
|
||||
);
|
||||
}
|
||||
}
|
||||
9
main/src/main/java/sd/model/TrafficLightState.java
Normal file
9
main/src/main/java/sd/model/TrafficLightState.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package sd.model;
|
||||
|
||||
/**
|
||||
* Enumeration representing the state of a traffic light.
|
||||
*/
|
||||
public enum TrafficLightState {
|
||||
GREEN, // Allows passage
|
||||
RED // Blocks passage
|
||||
}
|
||||
117
main/src/main/java/sd/model/Vehicle.java
Normal file
117
main/src/main/java/sd/model/Vehicle.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package sd.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a single vehicle moving through the simulation.
|
||||
*
|
||||
* Each vehicle has a route - a sequence of intersections it will pass through -
|
||||
* and keeps track of how long it has waited and traveled overall.
|
||||
*
|
||||
* Serializable so it can be sent between processes or nodes over sockets. type shit
|
||||
*/
|
||||
public class Vehicle implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// Identity and configuration
|
||||
private final String id;
|
||||
private final VehicleType type;
|
||||
private final double entryTime; // When it entered the system
|
||||
private final List<String> route; // ex., ["Cr1", "Cr3", "S"]
|
||||
private int currentRouteIndex; // Current position in the route
|
||||
|
||||
// Metrics
|
||||
private double totalWaitingTime; // Total time spent waiting at red lights
|
||||
private double totalCrossingTime; // Time spent actually moving between intersections
|
||||
|
||||
public Vehicle(String id, VehicleType type, double entryTime, List<String> route) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.entryTime = entryTime;
|
||||
this.route = new ArrayList<>(route);
|
||||
this.currentRouteIndex = 0;
|
||||
this.totalWaitingTime = 0.0;
|
||||
this.totalCrossingTime = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the vehicle to the next stop in its route.
|
||||
*
|
||||
* @return true if there are still destinations ahead, false if the route is finished
|
||||
*/
|
||||
public boolean advanceRoute() {
|
||||
currentRouteIndex++;
|
||||
return currentRouteIndex < route.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current destination (the next intersection or exit).
|
||||
* Returns null if the route is already complete.
|
||||
*/
|
||||
public String getCurrentDestination() {
|
||||
return (currentRouteIndex < route.size()) ? route.get(currentRouteIndex) : null;
|
||||
}
|
||||
|
||||
/** Returns true if the vehicle has completed its entire route. */
|
||||
public boolean hasReachedEnd() {
|
||||
return currentRouteIndex >= route.size();
|
||||
}
|
||||
|
||||
// --- Getters and metrics management ---
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public VehicleType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public double getEntryTime() {
|
||||
return entryTime;
|
||||
}
|
||||
|
||||
public List<String> getRoute() {
|
||||
return new ArrayList<>(route);
|
||||
}
|
||||
|
||||
public int getCurrentRouteIndex() {
|
||||
return currentRouteIndex;
|
||||
}
|
||||
|
||||
public double getTotalWaitingTime() {
|
||||
return totalWaitingTime;
|
||||
}
|
||||
|
||||
public void addWaitingTime(double time) {
|
||||
totalWaitingTime += time;
|
||||
}
|
||||
|
||||
public double getTotalCrossingTime() {
|
||||
return totalCrossingTime;
|
||||
}
|
||||
|
||||
public void addCrossingTime(double time) {
|
||||
totalCrossingTime += time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates how long the vehicle has been in the system so far.
|
||||
*
|
||||
* @param currentTime the current simulation time
|
||||
* @return total elapsed time since the vehicle entered
|
||||
*/
|
||||
public double getTotalTravelTime(double currentTime) {
|
||||
return currentTime - entryTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"Vehicle{id='%s', type=%s, next='%s', route=%s}",
|
||||
id, type, getCurrentDestination(), route
|
||||
);
|
||||
}
|
||||
}
|
||||
10
main/src/main/java/sd/model/VehicleType.java
Normal file
10
main/src/main/java/sd/model/VehicleType.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package sd.model;
|
||||
|
||||
/**
|
||||
* Enumeration representing vehicle types in the simulation.
|
||||
*/
|
||||
public enum VehicleType {
|
||||
BIKE, // Motorcycle
|
||||
LIGHT, // Light vehicle (car)
|
||||
HEAVY // Heavy vehicle (truck, bus)
|
||||
}
|
||||
68
main/src/main/java/sd/util/RandomGenerator.java
Normal file
68
main/src/main/java/sd/util/RandomGenerator.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package sd.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Utility class for generating random values used throughout the simulation.
|
||||
*
|
||||
* Includes helpers for exponential distributions (for vehicle arrivals),
|
||||
* uniform randoms, and probability-based decisions.
|
||||
*/
|
||||
public class RandomGenerator {
|
||||
|
||||
private static final Random random = new Random();
|
||||
|
||||
/**
|
||||
* Returns a random time interval that follows an exponential distribution.
|
||||
*
|
||||
* Useful for modeling inter-arrival times in a Poisson process.
|
||||
*
|
||||
* @param lambda the arrival rate (λ)
|
||||
* @return the time interval until the next arrival
|
||||
*/
|
||||
public static double generateExponentialInterval(double lambda) {
|
||||
return Math.log(1 - random.nextDouble()) / -lambda;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random integer between {@code min} and {@code max}, inclusive.
|
||||
*/
|
||||
public static int generateRandomInt(int min, int max) {
|
||||
return random.nextInt(max - min + 1) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random double between {@code min} (inclusive) and {@code max} (exclusive).
|
||||
*/
|
||||
public static double generateRandomDouble(double min, double max) {
|
||||
return min + (max - min) * random.nextDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} with the given probability.
|
||||
*
|
||||
* @param probability a value between 0.0 and 1.0
|
||||
*/
|
||||
public static boolean occursWithProbability(double probability) {
|
||||
return random.nextDouble() < probability;
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a random element from the given array.
|
||||
*
|
||||
* @throws IllegalArgumentException if the array is empty
|
||||
*/
|
||||
public static <T> T chooseRandom(T[] array) {
|
||||
if (array.length == 0) {
|
||||
throw new IllegalArgumentException("Array cannot be empty.");
|
||||
}
|
||||
return array[random.nextInt(array.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the random generator’s seed, allowing reproducible results.
|
||||
*/
|
||||
public static void setSeed(long seed) {
|
||||
random.setSeed(seed);
|
||||
}
|
||||
}
|
||||
113
main/src/main/resources/simulation.properties
Normal file
113
main/src/main/resources/simulation.properties
Normal file
@@ -0,0 +1,113 @@
|
||||
# =========================================================
|
||||
# Traffic Simulation Configuration
|
||||
# ---------------------------------------------------------
|
||||
# All parameters controlling network layout, timing,
|
||||
# and simulation behavior.
|
||||
# =========================================================
|
||||
|
||||
# === NETWORK CONFIGURATION ===
|
||||
|
||||
# Intersections (each with its host and port)
|
||||
intersection.Cr1.host=localhost
|
||||
intersection.Cr1.port=8001
|
||||
intersection.Cr2.host=localhost
|
||||
intersection.Cr2.port=8002
|
||||
intersection.Cr3.host=localhost
|
||||
intersection.Cr3.port=8003
|
||||
intersection.Cr4.host=localhost
|
||||
intersection.Cr4.port=8004
|
||||
intersection.Cr5.host=localhost
|
||||
intersection.Cr5.port=8005
|
||||
|
||||
# Exit node
|
||||
exit.host=localhost
|
||||
exit.port=9001
|
||||
|
||||
# Dashboard server
|
||||
dashboard.host=localhost
|
||||
dashboard.port=9000
|
||||
|
||||
|
||||
# === SIMULATION CONFIGURATION ===
|
||||
|
||||
# Total duration in seconds (3600 = 1 hour)
|
||||
simulation.duration=3600.0
|
||||
|
||||
# Vehicle arrival model: FIXED or POISSON
|
||||
simulation.arrival.model=POISSON
|
||||
|
||||
# λ (lambda): average arrival rate (vehicles per second)
|
||||
simulation.arrival.rate=0.5
|
||||
|
||||
# Fixed interval between arrivals (only used if model=FIXED)
|
||||
simulation.arrival.fixed.interval=2.0
|
||||
|
||||
|
||||
# === TRAFFIC LIGHT TIMINGS ===
|
||||
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds>
|
||||
|
||||
# Intersection 1
|
||||
trafficlight.Cr1.North.green=30.0
|
||||
trafficlight.Cr1.North.red=30.0
|
||||
trafficlight.Cr1.South.green=30.0
|
||||
trafficlight.Cr1.South.red=30.0
|
||||
trafficlight.Cr1.East.green=30.0
|
||||
trafficlight.Cr1.East.red=30.0
|
||||
trafficlight.Cr1.West.green=30.0
|
||||
trafficlight.Cr1.West.red=30.0
|
||||
|
||||
# Intersection 2
|
||||
trafficlight.Cr2.North.green=25.0
|
||||
trafficlight.Cr2.North.red=35.0
|
||||
trafficlight.Cr2.South.green=25.0
|
||||
trafficlight.Cr2.South.red=35.0
|
||||
trafficlight.Cr2.East.green=35.0
|
||||
trafficlight.Cr2.East.red=25.0
|
||||
trafficlight.Cr2.West.green=35.0
|
||||
trafficlight.Cr2.West.red=25.0
|
||||
|
||||
# Intersection 3
|
||||
trafficlight.Cr3.North.green=30.0
|
||||
trafficlight.Cr3.North.red=30.0
|
||||
trafficlight.Cr3.South.green=30.0
|
||||
trafficlight.Cr3.South.red=30.0
|
||||
trafficlight.Cr3.East.green=30.0
|
||||
trafficlight.Cr3.East.red=30.0
|
||||
trafficlight.Cr3.West.green=30.0
|
||||
trafficlight.Cr3.West.red=30.0
|
||||
|
||||
# Intersection 4
|
||||
trafficlight.Cr4.North.green=30.0
|
||||
trafficlight.Cr4.North.red=30.0
|
||||
trafficlight.Cr4.South.green=30.0
|
||||
trafficlight.Cr4.South.red=30.0
|
||||
trafficlight.Cr4.East.green=30.0
|
||||
trafficlight.Cr4.East.red=30.0
|
||||
trafficlight.Cr4.West.green=30.0
|
||||
trafficlight.Cr4.West.red=30.0
|
||||
|
||||
# Intersection 5
|
||||
trafficlight.Cr5.North.green=30.0
|
||||
trafficlight.Cr5.North.red=30.0
|
||||
trafficlight.Cr5.South.green=30.0
|
||||
trafficlight.Cr5.South.red=30.0
|
||||
trafficlight.Cr5.East.green=30.0
|
||||
trafficlight.Cr5.East.red=30.0
|
||||
trafficlight.Cr5.West.green=30.0
|
||||
trafficlight.Cr5.West.red=30.0
|
||||
|
||||
# === VEHICLE CONFIGURATION ===
|
||||
# Probability distribution for vehicle types (must sum to 1.0)
|
||||
vehicle.probability.bike=0.2
|
||||
vehicle.probability.light=0.6
|
||||
vehicle.probability.heavy=0.2
|
||||
|
||||
# Average crossing times (in seconds)
|
||||
vehicle.crossing.time.bike=1.5
|
||||
vehicle.crossing.time.light=2.0
|
||||
vehicle.crossing.time.heavy=4.0
|
||||
|
||||
# === STATISTICS ===
|
||||
|
||||
# Interval between dashboard updates (seconds)
|
||||
statistics.update.interval=10.0
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user