Files
SD/TODO.md

11 KiB
Raw Permalink Blame History

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.