diff --git a/main/src/main/java/sd/ExitNodeProcess.java b/main/src/main/java/sd/ExitNodeProcess.java index 725fd44..b82f275 100644 --- a/main/src/main/java/sd/ExitNodeProcess.java +++ b/main/src/main/java/sd/ExitNodeProcess.java @@ -27,22 +27,23 @@ import sd.protocol.MessageProtocol; import sd.protocol.SocketConnection; /** - * Destino final de todos os veículos da simulação (nó de saída S). - * - *
Opera como sumidouro da rede: - *
Participa no DES rastreando eventos, mas opera principalmente - * de forma reativa, aguardando chegadas via socket. + * Ponto terminal da malha de simulação (Sink Node). + *
+ * Este processo atua como o sumidouro da rede de filas. A sua função primária é + * a coleta de telemetria final. Diferente das interseções, não encaminha veículos; + * em vez disso, retira-os do sistema, calcula as métricas de latência "end-to-end" + * (tempo no sistema, tempo de espera acumulado) e reporta ao Dashboard. + *
+ * Arquitetura de Concorrência:
+ * Utiliza um {@link ServerSocket} multithreaded para aceitar conexões simultâneas de
+ * qualquer interseção de fronteira (Cr1, Cr5, etc.) que envie veículos para fora da malha.
*/
public class ExitNodeProcess {
private final SimulationConfig config;
private ServerSocket serverSocket;
+
+ /** Pool de threads elástica para tratamento de conexões de entrada. */
private final ExecutorService connectionHandlerPool;
// DES components
@@ -51,37 +52,37 @@ public class ExitNodeProcess {
private final EventLogger eventLogger;
private Thread eventProcessorThread;
- /** Flag de controlo (volatile para visibilidade entre threads) */
+ /** Flag de controlo (volatile para visibilidade entre threads de I/O e lógica). */
private volatile boolean running;
- /** Instante de início da simulação (milissegundos) */
+ /** Instante de início da simulação (milissegundos) sincronizado com o Coordenador. */
private long simulationStartMillis;
- /** Contador de veículos que completaram a rota */
+ /** Contador atómico (via synchronized) de throughput total. */
private int totalVehiclesReceived;
- /** Tempo acumulado no sistema de todos os veículos */
+ /** Tempo acumulado no sistema (System Time) de todos os veículos. */
private double totalSystemTime;
- /** Tempo acumulado em espera de todos os veículos */
+ /** Tempo acumulado em espera (Waiting Time) de todos os veículos. */
private double totalWaitingTime;
- /** Tempo acumulado em travessia de todos os veículos */
+ /** Tempo acumulado em travessia (Service Time) de todos os veículos. */
private double totalCrossingTime;
- /** Contagem de veículos por tipo */
+ /** Agregação por categoria de veículo. */
private final Map
+ * Mantém o socket aberto e consome mensagens num loop até que a conexão seja fechada
+ * pelo remetente. Responsável pela desserialização polimórfica (JSON/Gson).
+ * * @param clientSocket O socket conectado.
*/
private void handleIncomingConnection(Socket clientSocket) {
String clientAddress = clientSocket.getInetAddress().getHostAddress();
@@ -350,14 +336,14 @@ public class ExitNodeProcess {
" from " + message.getSourceNode());
if (message.getType() == MessageType.SIMULATION_START) {
- // Coordinator sends start time - use it instead of our local start
+ // Sincronização de relógio com o Coordenador
simulationStartMillis = ((Number) message.getPayload()).longValue();
System.out.println("[Exit] Simulation start time synchronized");
} else if (message.getType() == MessageType.VEHICLE_TRANSFER) {
Object payload = message.getPayload();
System.out.println("[Exit] Payload type: " + payload.getClass().getName());
- // Handle Gson LinkedHashMap
+ // Tratamento de artefatos de desserialização do Gson (LinkedTreeMap -> POJO)
Vehicle vehicle;
if (payload instanceof com.google.gson.internal.LinkedTreeMap ||
payload instanceof java.util.LinkedHashMap) {
@@ -390,26 +376,21 @@ public class ExitNodeProcess {
}
/**
- * Processa um veículo que acabou de sair do sistema.
- *
- * Calculamos quanto tempo demorou, atualizamos as nossas estatísticas globais e
- * notificamos o dashboard.
- * Este método é sincronizado porque múltiplos veículos podem chegar ao mesmo
- * tempo.
- *
- * @param vehicle O veículo que completou a sua rota.
+ * Processa atomicamente a saída de um veículo.
+ *
+ * Secção Crítica: Método {@code synchronized} para garantir que a atualização
+ * das estatísticas globais (totalSystemTime, contadores) é atómica, prevenindo
+ * Race Conditions quando múltiplos veículos chegam simultaneamente de interseções diferentes.
+ * * @param vehicle O veículo que completou a rota.
*/
private synchronized void processExitingVehicle(Vehicle vehicle) {
totalVehiclesReceived++;
- // Use simulation time instead of wall-clock time
- // System time = total time vehicle spent in system (wait + crossing times)
- // This represents the actual simulation time elapsed, not real-time
+ // Cálculo de métricas finais baseadas no tempo virtual de simulação acumulado no veículo
double waitTime = vehicle.getTotalWaitingTime();
double crossingTime = vehicle.getTotalCrossingTime();
double systemTime = waitTime + crossingTime;
- // Store times in seconds, will be converted to ms when sending to dashboard
totalSystemTime += systemTime;
totalWaitingTime += waitTime;
totalCrossingTime += crossingTime;
@@ -421,23 +402,20 @@ public class ExitNodeProcess {
System.out.printf("[Exit] Vehicle %s completed (type=%s, system_time=%.2fs, wait=%.2fs, crossing=%.2fs)%n",
vehicle.getId(), vehicle.getType(), systemTime, waitTime, crossingTime);
- // Log vehicle exit
+ // Logging estruturado
EventLogger.getInstance().logVehicle(EventType.VEHICLE_EXITED, "ExitNode", vehicle.getId(),
String.format("Completed - System: %.2fs, Wait: %.2fs, Crossing: %.2fs", systemTime, waitTime,
crossingTime));
- // Complete vehicle trace if tracking
+ // Finaliza o trace individual do veículo
VehicleTracer.getInstance().logExit(vehicle, systemTime);
- // Send stats after every vehicle to ensure dashboard updates quickly
+ // Push imediato para o Dashboard para visualização em tempo real
sendStatsToDashboard();
}
/**
- * Envia as estatísticas mais recentes para o dashboard.
- *
- * Empacotamos as contagens totais e os tempos médios num `StatsUpdatePayload`
- * e enviamo-lo.
+ * Constrói e transmite o DTO de atualização de estatísticas.
*/
private void sendStatsToDashboard() {
if (dashboardClient == null || !dashboardClient.isConnected()) {
@@ -448,29 +426,28 @@ public class ExitNodeProcess {
// Create stats payload
StatsUpdatePayload payload = new StatsUpdatePayload();
- // Set global stats - convert seconds to milliseconds
+ // Set global stats - convert seconds to milliseconds for display consistency
payload.setTotalVehiclesCompleted(totalVehiclesReceived);
- payload.setTotalSystemTime((long) (totalSystemTime * 1000.0)); // s -> ms
- payload.setTotalWaitingTime((long) (totalWaitingTime * 1000.0)); // s -> ms
+ payload.setTotalSystemTime((long) (totalSystemTime * 1000.0));
+ payload.setTotalWaitingTime((long) (totalWaitingTime * 1000.0));
- // Set intersection-like stats so it shows up correctly in the dashboard table
+ // Hack: Usar campos de interseção para mostrar throughput no dashboard
payload.setIntersectionArrivals(totalVehiclesReceived);
payload.setIntersectionDepartures(totalVehiclesReceived);
payload.setIntersectionQueueSize(0);
- // Set vehicle type stats
+ // Detailed breakdown
Map
+ * Esta classe implementa a lógica de uma interseção rodoviária utilizando uma
+ * arquitetura híbrida:
+ *
+ * A sincronização entre a chegada assíncrona de veículos (Rede) e o
+ * processamento determinístico (DES) é gerida através de estruturas de dados
+ * concorrentes e bloqueios justos (Fair Locks).
*/
public class IntersectionProcess {
@@ -57,48 +60,56 @@ public class IntersectionProcess {
private ServerSocket serverSocket;
+ /**
+ * Tabela de encaminhamento dinâmico para conexões de saída (Next-Hop Cache).
+ */
private final Map
+ * Executa o ciclo "Fetch-Decode-Execute":
+ *
+ * Encaminha o evento para a lógica de negócio específica baseada no tipo
+ * {@link DESEventType}.
+ *
+ * @param event O evento de simulação a ser processado.
*/
private void processEvent(SimulationEvent event) {
try {
@@ -198,8 +214,8 @@ public class IntersectionProcess {
break;
case VEHICLE_ARRIVAL:
- // Vehicle arrivals are still handled via network messages
- // This event type is for internal scheduling if needed
+ // Chegadas são tratadas reativamente via Socket, mas eventos podem ser usados
+ // para métricas
break;
case VEHICLE_CROSSING_START:
@@ -225,12 +241,18 @@ public class IntersectionProcess {
}
/**
- * Trata da mudança dos semáforos.
- *
- * Quando um semáforo muda de estado, registamos o evento, atualizamos o modelo
- * e, se tiver mudado para VERDE,
- * verificamos imediatamente se há veículos à espera para atravessar.
- * Também agendamos aqui o *próximo* evento de mudança, mantendo o ciclo ativo.
+ * Gere a máquina de estados dos semáforos.
+ *
+ * O fluxo de execução é o seguinte:
+ *
- * Para cada veículo na fila:
- *
- * Os veículos que não couberem no tempo verde ficam à espera do próximo ciclo.
- *
+ * Atualmente serve como placeholder para lógica futura de animação ou
+ * ocupação de zonas críticas na interseção.
+ *
+ * @param event O evento de início de travessia.
*/
private void handleVehicleCrossingStartEvent(SimulationEvent event) {
- // Implementation will depend on how vehicle crossing is modeled
- // For now, log the event
+ // Placeholder para lógica futura de animação ou ocupação de zona crítica
eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId,
"Vehicle crossing started at time " + event.getTimestamp());
}
/**
- * Trata o fim da travessia de um veículo pela interseção.
- * Atualiza estatísticas, regista o tempo de travessia e envia o veículo
- * para o próximo destino na sua rota.
- *
- * @param event evento contendo o veículo que terminou a travessia
+ * Finaliza a lógica de travessia e inicia a transferência (handover) para o
+ * próximo nó.
+ *
+ * Este método é invocado quando o tempo de travessia expira no relógio virtual.
+ * Executa as seguintes ações:
+ *
+ * Inclui a criação de semáforos, configuração de encaminhamento e conexão ao
+ * Dashboard.
+ */
public void initialize() {
System.out.println("\n[" + intersectionId + "] Initializing intersection...");
@@ -473,7 +506,7 @@ public class IntersectionProcess {
}
/**
- * Estabelece ligação ao servidor do dashboard para reportar estatísticas.
+ * Estabelece a conexão com o Dashboard para envio de telemetria em tempo real.
*/
private void connectToDashboard() {
try {
@@ -497,9 +530,7 @@ public class IntersectionProcess {
}
/**
- * Cria os semáforos para esta interseção com base nas suas ligações físicas.
- * Cada interseção tem um número e direções de semáforos diferentes de acordo
- * com a topologia da rede.
+ * Inicializa os semáforos da interseção com base na configuração carregada.
*/
private void createTrafficLights() {
System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
@@ -528,6 +559,13 @@ public class IntersectionProcess {
}
}
+ /**
+ * Obtém a configuração específica para esta interseção a partir da configuração
+ * global.
+ *
+ * @return O objeto de configuração da interseção.
+ * @throws RuntimeException Se a configuração estiver em falta.
+ */
private SimulationConfig.IntersectionConfig getIntersectionConfig() {
if (config.getNetworkConfig() == null || config.getNetworkConfig().getIntersections() == null) {
throw new RuntimeException("Network configuration not loaded or empty.");
@@ -538,6 +576,11 @@ public class IntersectionProcess {
.orElseThrow(() -> new RuntimeException("Intersection config not found for " + intersectionId));
}
+ /**
+ * Configura a tabela de encaminhamento (routing) da interseção.
+ *
+ * Define para cada destino qual a direção de saída (semáforo) correspondente.
+ */
private void configureRouting() {
System.out.println("\n[" + intersectionId + "] Configuring routing...");
@@ -559,11 +602,10 @@ public class IntersectionProcess {
}
/**
- * Solicita permissão para um semáforo ficar verde.
- * Bloqueia até que a permissão seja concedida (nenhum outro semáforo está
- * verde).
- *
- * @param direction A direção que solicita o sinal verde
+ * Primitiva de bloqueio: Solicita acesso exclusivo à zona crítica da
+ * interseção.
+ *
+ * @param direction A direção que solicita passagem.
*/
public void requestGreenLight(String direction) {
trafficCoordinationLock.lock();
@@ -571,10 +613,9 @@ public class IntersectionProcess {
}
/**
- * Liberta a permissão de sinal verde, permitindo que outro semáforo fique
- * verde.
- *
- * @param direction A direção que liberta o sinal verde
+ * Primitiva de bloqueio: Liberta o acesso exclusivo à zona crítica.
+ *
+ * @param direction A direção que está a libertar a passagem.
*/
public void releaseGreenLight(String direction) {
if (direction.equals(currentGreenDirection)) {
@@ -584,8 +625,10 @@ public class IntersectionProcess {
}
/**
- * Modo DES: Agenda os eventos iniciais de mudança de semáforo.
- * Isto substitui a antiga abordagem baseada em threads startTrafficLights().
+ * Inicializa o estado dos semáforos no arranque da simulação (t=0).
+ *
+ * Garante que apenas um semáforo começa em Verde e os restantes em Vermelho,
+ * agendando os eventos iniciais na fila do DES.
*/
private void scheduleInitialTrafficLightEvents() {
System.out.println("\n[" + intersectionId + "] Scheduling initial traffic light events (DES mode)...");
@@ -596,12 +639,12 @@ public class IntersectionProcess {
for (TrafficLight light : intersection.getTrafficLights()) {
String direction = light.getDirection();
- // Set initial state (first light starts green, others red)
+ // Lógica de arranque: Primeiro da lista = Verde, outros = Vermelho
boolean isFirstLight = intersection.getTrafficLights().indexOf(light) == 0;
TrafficLightState initialState = isFirstLight ? TrafficLightState.GREEN : TrafficLightState.RED;
light.changeState(initialState);
- // Schedule first state change
+ // Agenda a primeira transição
double firstChangeTime = currentTime +
(initialState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime());
@@ -624,14 +667,16 @@ public class IntersectionProcess {
}
/**
- * Envia um veículo para o seu próximo destino via ligação socket.
+ * Encaminhamento de rede: Serializa e envia o objeto veículo para o próximo nó.
+ *
+ * Calcula também o tempo de viagem virtual entre nós (Edge Weight).
*
- * @param vehicle O veículo que atravessou esta interseção.
+ * @param vehicle O veículo a ser enviado.
*/
public void sendVehicleToNextDestination(Vehicle vehicle) {
String nextDestination = vehicle.getCurrentDestination();
- // Calculate travel time
+ // Cálculo de latência de viagem (Edge Weight)
double baseTime = config.getBaseTravelTime();
double multiplier = 1.0;
switch (vehicle.getType()) {
@@ -644,22 +689,25 @@ public class IntersectionProcess {
System.out.printf("[%s] Vehicle %s departing to %s. Travel time: %.2fs%n",
intersectionId, vehicle.getId(), nextDestination, travelTime);
- // Record departure immediately as it leaves the intersection
recordVehicleDeparture();
- // In DES mode, send immediately (no real-time delay)
+ // Envio imediato (o delay de viagem é implícito no tempo de chegada no próximo
+ // nó ou simulado aqui)
sendVehicleImmediately(vehicle, nextDestination);
}
/**
- * Envia imediatamente um veículo para o seu destino via rede.
+ * Envia o veículo imediatamente para o próximo nó via conexão TCP persistente.
+ *
+ * @param vehicle O veículo a ser enviado.
+ * @param nextDestination O identificador do próximo nó destino.
*/
private void sendVehicleImmediately(Vehicle vehicle, String nextDestination) {
try {
- // Get or create connection to next destination
+ // Lazy loading da conexão
SocketConnection connection = getOrCreateConnection(nextDestination);
- // Create and send message using Message class
+ // Encapsulamento da mensagem
MessageProtocol message = new Message(
MessageType.VEHICLE_TRANSFER,
intersectionId,
@@ -672,8 +720,6 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] Vehicle " + vehicle.getId() +
" arrived at " + nextDestination + " (msg sent)");
- // Note: vehicle route is advanced when it arrives at the next intersection
-
} catch (IOException | InterruptedException e) {
System.err.println("[" + intersectionId + "] Failed to send vehicle " +
vehicle.getId() + " to " + nextDestination + ": " + e.getMessage());
@@ -681,12 +727,15 @@ public class IntersectionProcess {
}
/**
- * Obtém uma ligação existente para um destino ou cria uma nova.
+ * Obtém ou cria uma conexão para o destino especificado (Singleton por
+ * destino).
+ *
+ * Este método é thread-safe.
*
- * @param destinationId O ID do nó de destino.
- * @return A SocketConnection para esse destino.
- * @throws IOException Se a ligação não puder ser estabelecida.
- * @throws InterruptedException Se a tentativa de ligação for interrompida.
+ * @param destinationId O identificador do nó destino.
+ * @return A conexão TCP estabelecida.
+ * @throws IOException Se ocorrer um erro de I/O na criação da conexão.
+ * @throws InterruptedException Se a thread for interrompida durante a espera.
*/
private synchronized SocketConnection getOrCreateConnection(String destinationId)
throws IOException, InterruptedException {
@@ -706,10 +755,10 @@ public class IntersectionProcess {
}
/**
- * Obtém o endereço host para um nó de destino a partir da configuração.
+ * Resolve o hostname ou endereço IP para um determinado destino.
*
- * @param destinationId O ID do nó de destino.
- * @return O endereço host.
+ * @param destinationId O ID do destino.
+ * @return O endereço do host.
*/
private String getHostForDestination(String destinationId) {
if (destinationId.equals("S")) {
@@ -720,9 +769,9 @@ public class IntersectionProcess {
}
/**
- * Obtém o número da porta para um nó de destino a partir da configuração.
+ * Resolve a porta TCP para um determinado destino.
*
- * @param destinationId O ID do nó de destino.
+ * @param destinationId O ID do destino.
* @return O número da porta.
*/
private int getPortForDestination(String destinationId) {
@@ -734,10 +783,11 @@ public class IntersectionProcess {
}
/**
- * Inicia o socket do servidor e começa a aceitar ligações recebidas.
- * Este é o loop principal de escuta do processo.
+ * Inicia o servidor e o loop de aceitação de conexões.
+ *
+ * Este método bloqueia a thread chamadora durante a execução do servidor.
*
- * @throws IOException Se o socket do servidor não puder ser criado.
+ * @throws IOException Se ocorrer um erro ao fazer bind da porta.
*/
public void start() throws IOException {
int port = config.getIntersectionPort(intersectionId);
@@ -751,12 +801,12 @@ public class IntersectionProcess {
startEventProcessor();
System.out.println("[" + intersectionId + "] Running in DES mode");
- // Start stats updater
+ // Background task para telemetria
statsExecutor.scheduleAtFixedRate(this::sendStatsToDashboard, 1, 1, TimeUnit.SECONDS);
System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n");
- // Main accept loop
+ // Loop principal de aceitação de conexões
while (running) {
try {
Socket clientSocket = serverSocket.accept();
@@ -764,13 +814,12 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] New connection accepted from " +
clientSocket.getInetAddress().getHostAddress());
- // Check running flag again before handling
if (!running) {
clientSocket.close();
break;
}
- // **Set timeout before submitting to handler**
+ // Configura timeout para evitar bloqueios infinitos em leitura
try {
clientSocket.setSoTimeout(1000);
} catch (java.net.SocketException e) {
@@ -779,13 +828,12 @@ public class IntersectionProcess {
continue;
}
- // Handle each connection in a separate thread
+ // Delega processamento para thread pool (NIO style)
connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket));
} catch (IOException e) {
- // Expected when serverSocket.close() is called during shutdown
if (!running) {
- break; // Normal shutdown
+ break; // Shutdown normal
}
System.err.println("[" + intersectionId + "] Error accepting connection: " +
e.getMessage());
@@ -794,10 +842,13 @@ public class IntersectionProcess {
}
/**
- * Trata uma ligação recebida de outro processo.
- * Escuta continuamente mensagens de transferência de veículos.
+ * Lógica de tratamento de conexões de entrada (Consumer).
+ *
+ * Lê continuamente do socket até que a conexão seja fechada, processando
+ * mensagens
+ * de chegada de veículos ou comandos de simulação.
*
- * @param clientSocket A ligação socket aceite.
+ * @param clientSocket O socket do cliente conectado.
*/
private void handleIncomingConnection(Socket clientSocket) {
try {
@@ -813,27 +864,24 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] New connection accepted from " +
clientSocket.getInetAddress().getHostAddress());
- // Continuously receive messages while connection is active
while (running && connection.isConnected()) {
try {
MessageProtocol message = connection.receiveMessage();
- // Handle simulation start time synchronization
if (message.getType() == MessageType.SIMULATION_START) {
System.out.println("[" + intersectionId + "] Simulation start time synchronized");
continue;
}
- // Accept both VEHICLE_TRANSFER and VEHICLE_SPAWN (from coordinator)
if (message.getType() == MessageType.VEHICLE_TRANSFER ||
message.getType() == MessageType.VEHICLE_SPAWN) {
- // Cast payload to Vehicle - handle Gson deserialization
+
+ // Lógica de desserialização polimórfica (Vehicle ou Map)
Vehicle vehicle;
Object payload = message.getPayload();
if (payload instanceof Vehicle) {
vehicle = (Vehicle) payload;
} else if (payload instanceof java.util.Map) {
- // Gson deserialized as LinkedHashMap - re-serialize and deserialize as Vehicle
com.google.gson.Gson gson = new com.google.gson.Gson();
String json = gson.toJson(payload);
vehicle = gson.fromJson(json, Vehicle.class);
@@ -845,43 +893,37 @@ public class IntersectionProcess {
System.out.println("[" + intersectionId + "] Received vehicle: " +
vehicle.getId() + " from " + message.getSourceNode());
- // Advance vehicle to next destination in its route
+ // Lógica de Roteamento Local
vehicle.advanceRoute();
-
- // Add vehicle to appropriate queue with current simulation time
intersection.receiveVehicle(vehicle, clock.getCurrentTime());
- // Log queue status after adding vehicle
System.out.printf("[%s] Vehicle %s queued. Total queue size: %d%n",
intersectionId, vehicle.getId(), intersection.getTotalQueueSize());
- // Record arrival for statistics
recordVehicleArrival();
+
} else if (message.getType() == MessageType.SHUTDOWN) {
System.out.println(
"[" + intersectionId + "] Received SHUTDOWN command from " + message.getSourceNode());
running = false;
- // Close this specific connection
break;
}
} catch (java.net.SocketTimeoutException e) {
- // Timeout - check running flag and continue
if (!running) {
break;
}
- // Continue waiting for next message
} catch (ClassNotFoundException e) {
System.err.println("[" + intersectionId + "] Unknown message type received: " +
e.getMessage());
- break; // Invalid message, close connection
+ break;
} catch (IOException e) {
if (running) {
System.err.println("[" + intersectionId + "] Failed to deserialize message: " +
e.getMessage());
- e.printStackTrace(); // For debugging - maybe change//remove later
+ e.printStackTrace();
}
- break; // Connection error, close connection
+ break;
}
}
@@ -889,27 +931,29 @@ public class IntersectionProcess {
if (running) {
System.err.println("[" + intersectionId + "] Connection error: " + e.getMessage());
}
- // Expected during shutdown
}
}
/**
- * Stops the intersection process gracefully.
- * Shuts down all threads and closes all connections.
+ * Procedimento de Encerramento Gracioso (Graceful Shutdown).
+ *
+ * Inclui o número acumulado de chegadas, partidas e o tamanho atual das filas.
*/
private void sendStatsToDashboard() {
if (dashboardClient == null || !dashboardClient.isConnected()) {
@@ -1000,7 +1043,6 @@ public class IntersectionProcess {
}
try {
- // Calculate current queue size
int currentQueueSize = intersection.getTrafficLights().stream()
.mapToInt(TrafficLight::getQueueSize)
.sum();
@@ -1010,7 +1052,6 @@ public class IntersectionProcess {
.setIntersectionDepartures(totalDepartures)
.setIntersectionQueueSize(currentQueueSize);
- // Send StatsUpdatePayload directly as the message payload
sd.model.Message message = new sd.model.Message(
MessageType.STATS_UPDATE,
intersectionId,
@@ -1026,4 +1067,4 @@ public class IntersectionProcess {
System.err.println("[" + intersectionId + "] Failed to send stats to dashboard: " + e.getMessage());
}
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/dashboard/DashboardUI.java b/main/src/main/java/sd/dashboard/DashboardUI.java
index 88378d3..8757784 100644
--- a/main/src/main/java/sd/dashboard/DashboardUI.java
+++ b/main/src/main/java/sd/dashboard/DashboardUI.java
@@ -30,8 +30,22 @@ import sd.config.SimulationConfig;
import sd.model.VehicleType;
/**
- * JavaFX-based Dashboard UI for displaying real-time simulation statistics.
- * Provides a graphical interface with auto-updating statistics panels.
+ * Interface Gráfica (GUI) baseada em JavaFX para visualização de telemetria em tempo real.
+ *
+ * Esta classe atua como a camada de apresentação (View) do sistema. Implementa o padrão
+ * Observer (via polling) para refletir o estado do modelo {@link DashboardStatistics}
+ * nos componentes visuais.
+ *
+ * Aspetos Técnicos Relevantes:
+ *
+ * Esta classe atua como um supervisor (Process Manager), responsável pelo bootstrapping
+ * e teardown das múltiplas Java Virtual Machines (JVMs) que compõem o sistema.
+ *
+ * Funcionalidades principais:
+ *
+ * A ordem de inicialização é crítica para evitar Race Conditions na conexão TCP:
+ *
+ * Como o Coordenador gere o relógio DES e a geração de eventos, a sua terminação
+ * (após o drain time) sinaliza o fim efetivo da simulação.
+ * * @return true se o Coordenador ainda estiver ativo (alive).
*/
public boolean isSimulationRunning() {
if (runningProcesses.isEmpty()) {
@@ -78,8 +100,10 @@ public class SimulationProcessManager {
}
/**
- * Waits for the simulation to complete naturally.
- * Returns true if completed, false if timeout.
+ * Bloqueia a thread atual até que a simulação termine naturalmente ou ocorra timeout.
+ * * @param timeoutSeconds Tempo máximo de espera.
+ * @return true se terminou, false se o timeout expirou.
+ * @throws InterruptedException Se a espera for interrompida.
*/
public boolean waitForCompletion(long timeoutSeconds) throws InterruptedException {
if (runningProcesses.isEmpty()) {
@@ -91,7 +115,11 @@ public class SimulationProcessManager {
}
/**
- * Stops all running simulation processes.
+ * Executa o procedimento de encerramento (Teardown) de todos os processos.
+ *
+ * Tenta primeiro uma paragem graciosa (`SIGTERM`), aguarda meio segundo, e
+ * força a paragem (`SIGKILL`) para processos persistentes, garantindo que não
+ * ficam processos órfãos no SO.
*/
public void stopSimulation() {
System.out.println("Stopping simulation processes...");
@@ -120,7 +148,8 @@ public class SimulationProcessManager {
}
/**
- * Helper para iniciar um único processo Java.
+ * Helper de baixo nível para construção e lançamento de processos Java.
+ * Configura o redirecionamento de I/O para ficheiros de log na diretoria temporária do SO.
*/
private void startProcess(String className, String arg) throws IOException {
String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java";
@@ -152,4 +181,4 @@ public class SimulationProcessManager {
// print where the logs are actually going
System.out.println("Logs redirected to: " + logFile.getAbsolutePath());
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/dashboard/StatsMessage.java b/main/src/main/java/sd/dashboard/StatsMessage.java
index 7209130..abc4730 100644
--- a/main/src/main/java/sd/dashboard/StatsMessage.java
+++ b/main/src/main/java/sd/dashboard/StatsMessage.java
@@ -4,7 +4,14 @@ import sd.model.MessageType;
import sd.protocol.MessageProtocol;
/**
- * Message wrapper for sending statistics to the dashboard.
+ * Implementação concreta do protocolo de mensagens destinada ao transporte de telemetria.
+ *
+ * Esta classe atua como um envelope especializado para o envio de dados estatísticos
+ * (encapsulados em {@link StatsUpdatePayload}) dos nós operacionais (Interseções, Coordenador)
+ * para o servidor de Dashboard centralizado.
+ *
+ * Diferencia-se das mensagens de controlo genéricas por ter o destino fixado no
+ * "DashboardServer" e um tipo de mensagem imutável ({@code STATS_UPDATE}).
*/
public class StatsMessage implements MessageProtocol {
@@ -14,27 +21,49 @@ public class StatsMessage implements MessageProtocol {
private final String destinationNode;
private final StatsUpdatePayload payload;
+ /**
+ * Cria uma nova mensagem de estatística.
+ *
+ * @param sourceNode O ID do nó que gerou as estatísticas (ex: "Cr1", "ExitNode").
+ * @param payload O objeto DTO contendo os dados estatísticos brutos ou agregados.
+ */
public StatsMessage(String sourceNode, StatsUpdatePayload payload) {
this.sourceNode = sourceNode;
- this.destinationNode = "DashboardServer";
+ this.destinationNode = "DashboardServer"; // Destino implícito e fixo
this.payload = payload;
}
+ /**
+ * Retorna o tipo da mensagem, que identifica semanticamente o conteúdo para o recetor.
+ * @return Sempre {@link MessageType#STATS_UPDATE}.
+ */
@Override
public MessageType getType() {
return MessageType.STATS_UPDATE;
}
+ /**
+ * Obtém a carga útil da mensagem.
+ * @return O objeto {@link StatsUpdatePayload} associado.
+ */
@Override
public Object getPayload() {
return payload;
}
+ /**
+ * Identifica a origem da mensagem.
+ * @return O ID do nó remetente.
+ */
@Override
public String getSourceNode() {
return sourceNode;
}
+ /**
+ * Identifica o destino da mensagem.
+ * @return Sempre "DashboardServer".
+ */
@Override
public String getDestinationNode() {
return destinationNode;
@@ -45,4 +74,4 @@ public class StatsMessage implements MessageProtocol {
return String.format("StatsMessage[from=%s, to=%s, payload=%s]",
sourceNode, destinationNode, payload);
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/dashboard/StatsUpdatePayload.java b/main/src/main/java/sd/dashboard/StatsUpdatePayload.java
index e62e866..6f1915e 100644
--- a/main/src/main/java/sd/dashboard/StatsUpdatePayload.java
+++ b/main/src/main/java/sd/dashboard/StatsUpdatePayload.java
@@ -7,25 +7,60 @@ import java.util.Map;
import sd.model.VehicleType;
/**
- * DTO para atualizações de estatísticas ao dashboard.
- * Campos com valor -1 não são atualizados nesta mensagem.
+ * Objeto de Transferência de Dados (DTO) otimizado para transporte de telemetria.
+ *
+ * Esta classe encapsula as métricas de desempenho enviadas pelos nós da simulação (Coordenador,
+ * Interseções, ExitNode) para o Dashboard. Foi desenhada para suportar atualizações parciais
+ * (Sparse Updates):
+ * Unidade fundamental de execução num sistema DES:
+ * Representa um evento atómico e imutável no contexto da Simulação de Eventos Discretos (DES).
+ *
+ * Esta classe é a unidade fundamental de processamento. Numa arquitetura DES, o estado do sistema
+ * não muda continuamente, mas sim em instantes discretos definidos por estes eventos.
+ *
+ * Características principais:
*
+ * Lógica de Ordenação:
+ *
+ * Este objeto atua como o payload transportado por um {@link SimulationEvent}
+ * quando o tipo de evento é relacionado com controlo de tráfego (ex: mudança Verde -> Amarelo).
+ * Permite que o motor DES identifique exatamente qual instância de {@link TrafficLight}
+ * deve ser atualizada numa determinada interseção e direção.
*/
public class TrafficLightEvent {
private final TrafficLight light;
private final String direction;
private final String intersectionId;
+ /**
+ * Cria um novo payload de evento de semáforo.
+ * @param light A instância do objeto semáforo a ser manipulado.
+ * @param direction A direção cardeal associada (ex: "North", "East").
+ * @param intersectionId O identificador da interseção onde o semáforo reside.
+ */
public TrafficLightEvent(TrafficLight light, String direction, String intersectionId) {
this.light = light;
this.direction = direction;
this.intersectionId = intersectionId;
}
+ /**
+ * @return A referência direta para o objeto de domínio do semáforo.
+ */
public TrafficLight getLight() {
return light;
}
+ /**
+ * @return A direção do fluxo controlado por este semáforo.
+ */
public String getDirection() {
return direction;
}
+ /**
+ * @return O ID da interseção pai.
+ */
public String getIntersectionId() {
return intersectionId;
}
@@ -33,4 +52,4 @@ public class TrafficLightEvent {
public String toString() {
return String.format("TrafficLightEvent[%s-%s]", intersectionId, direction);
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/logging/EventLogger.java b/main/src/main/java/sd/logging/EventLogger.java
index f653ff9..dd08b23 100644
--- a/main/src/main/java/sd/logging/EventLogger.java
+++ b/main/src/main/java/sd/logging/EventLogger.java
@@ -11,10 +11,19 @@ import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Sistema de registo centralizado de eventos para a simulação distribuída.
- *
- * Regista todos os eventos da simulação num ficheiro com timestamps e categorização.
- * Thread-safe e não-bloqueante para impacto mínimo na performance.
+ * Implementa o padrão Singleton para garantir um ponto centralizado de registo.
+ * Utiliza o padrão Producer-Consumer com uma {@link BlockingQueue} para desacoplar
+ * a geração de eventos (crítica para a performance da simulação) da persistência em disco
+ * (operação de I/O lenta).
+ *
+ * Garantias:
+ *
+ * Este enumerado padroniza a categorização de todas as ocorrências na simulação, permitindo:
+ *
- * Cria ficheiros de trace detalhados com:
+ * Diferente do {@link EventLogger} (que regista eventos globais do sistema), esta classe foca-se
+ * na perspetiva do agente. Cria um ficheiro de rastro dedicado (`.trace`) para cada veículo
+ * monitorizado, registando cronologicamente cada interação com a infraestrutura (interseções,
+ * filas, semáforos).
+ *
+ * Funcionalidades:
*
+ * Este método também desencadeia a escrita do Sumário de Viagem no final do log
+ * e fecha o ficheiro automaticamente.
*/
public void logExit(Vehicle vehicle, double systemTime) {
if (!isTracking(vehicle.getId()))
@@ -229,7 +249,7 @@ public class VehicleTracer {
String.format("Exited system - Total time: %.2fs, Waiting: %.2fs, Crossing: %.2fs",
systemTime, vehicle.getTotalWaitingTime(), vehicle.getTotalCrossingTime()));
- // Write summary
+ // Escreve estatísticas sumarizadas
trace.writeSummary(vehicle, systemTime);
// Stop tracking and close file
@@ -238,7 +258,8 @@ public class VehicleTracer {
}
/**
- * Shuts down the tracer and closes all trace files.
+ * Fecha forçosamente todos os traces abertos.
+ * Deve ser chamado no shutdown da simulação para evitar corrupção de logs.
*/
public void shutdown() {
for (VehicleTrace trace : trackedVehicles.values()) {
@@ -248,7 +269,7 @@ public class VehicleTracer {
}
/**
- * Internal class to handle tracing for a single vehicle.
+ * Classe interna auxiliar que gere o descritor de ficheiro e a formatação para um único veículo.
*/
private class VehicleTrace {
private final String vehicleId;
@@ -340,4 +361,4 @@ public class VehicleTracer {
return str.length() <= maxLength ? str : str.substring(0, maxLength);
}
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/model/Message.java b/main/src/main/java/sd/model/Message.java
index 623bc0d..d7a9573 100644
--- a/main/src/main/java/sd/model/Message.java
+++ b/main/src/main/java/sd/model/Message.java
@@ -5,41 +5,52 @@ import java.util.UUID;
import sd.protocol.MessageProtocol;
/**
- * Representa uma mensagem trocada entre processos na simulação distribuída.
- *
- * Cada mensagem tem um ID único, tipo, remetente, destino e payload.
- * Implementa {@link MessageProtocol} que estende Serializable para transmissão pela rede.
+ * Esta classe atua como a Unidade de Dados de Aplicação (ADU), encapsulando tanto
+ * os metadados de roteamento (origem, destino, tipo) quanto a carga útil (payload)
+ * polimórfica. É agnóstica ao conteúdo, servindo como contentor genérico para
+ * transferência de estado (Veículos, Estatísticas) ou sinais de controlo (Semáforos).
+ *
+ * A imutabilidade dos campos (exceto via serialização) garante a integridade da mensagem
+ * durante o trânsito na rede.
*/
public class Message implements MessageProtocol {
private static final long serialVersionUID = 1L;
- /** Identificador único desta mensagem */
+ /** * Identificador único universal (UUID).
+ * Essencial para rastreabilidade (tracing), logs de auditoria e mecanismos de deduplicação.
+ */
private final String messageId;
- /** Tipo desta mensagem (ex: VEHICLE_TRANSFER, STATS_UPDATE) */
+ /** Discriminador semântico que define como o recetor deve processar o payload. */
private final MessageType type;
- /** Identificador do processo que enviou esta mensagem */
+ /** Identificador lógico do nó emissor (ex: "Cr1", "Coordinator"). */
private final String senderId;
- /** Identificador do processo de destino (pode ser null para broadcast) */
+ /** * Identificador lógico do nó recetor.
+ * Se {@code null}, a mensagem deve ser tratada como Broadcast.
+ */
private final String destinationId;
- /** Dados a serem transmitidos (o tipo depende do tipo de mensagem) */
+ /** * Carga útil polimórfica.
+ * Deve implementar {@link java.io.Serializable} para garantir transmissão correta.
+ */
private final Object payload;
- /** Timestamp de criação da mensagem (tempo de simulação ou real) */
+ /** Marca temporal da criação da mensagem (Unix Timestamp), usada para cálculo de latência de rede. */
private final long timestamp;
/**
- * Cria uma nova mensagem com todos os parâmetros.
+ * Construtor completo para reconstrução de mensagens ou envio com timestamp manual.
*
- * @param type tipo da mensagem
- * @param senderId ID do processo remetente
- * @param destinationId ID do processo de destino (null para broadcast)
- * @param payload conteúdo da mensagem
- * @param timestamp timestamp de criação da mensagem
+ * @param type Classificação semântica da mensagem.
+ * @param senderId ID do processo origem.
+ * @param destinationId ID do processo destino (ou null para broadcast).
+ * @param payload Objeto de domínio a ser transportado.
+ * @param timestamp Instante de criação (ms).
*/
public Message(MessageType type, String senderId, String destinationId,
Object payload, long timestamp) {
@@ -52,23 +63,24 @@ public class Message implements MessageProtocol {
}
/**
- * Cria uma nova mensagem usando o tempo atual do sistema como timestamp.
+ * Construtor de conveniência que atribui automaticamente o timestamp atual do sistema.
*
- * @param type tipo da mensagem
- * @param senderId ID do processo remetente
- * @param destinationId ID do processo de destino
- * @param payload conteúdo da mensagem
+ * @param type Classificação semântica.
+ * @param senderId ID do processo origem.
+ * @param destinationId ID do processo destino.
+ * @param payload Objeto de domínio.
*/
public Message(MessageType type, String senderId, String destinationId, Object payload) {
this(type, senderId, destinationId, payload, System.currentTimeMillis());
}
/**
- * Cria uma mensagem de broadcast (sem destino específico).
+ * Construtor de conveniência para mensagens de difusão (Broadcast).
+ * Define {@code destinationId} como null.
*
- * @param type tipo da mensagem
- * @param senderId ID do processo remetente
- * @param payload conteúdo da mensagem
+ * @param type Classificação semântica.
+ * @param senderId ID do processo origem.
+ * @param payload Objeto de domínio.
*/
public Message(MessageType type, String senderId, Object payload) {
this(type, senderId, null, payload, System.currentTimeMillis());
@@ -101,21 +113,23 @@ public class Message implements MessageProtocol {
}
/**
- * Checks if this is a broadcast message (no specific destination).
+ * Verifica se a mensagem se destina a todos os nós da rede.
*
- * @return true if destinationId is null, false otherwise
+ * @return {@code true} se o destinationId for nulo.
*/
public boolean isBroadcast() {
return destinationId == null;
}
/**
- * Gets the payload cast to a specific type.
- * Use with caution and ensure type safety.
+ * Utilitário para casting seguro e fluente do payload.
+ *
+ * Evita a necessidade de casts explícitos e supressão de warnings no código cliente.
*
- * @param
+ * Esta classe abstrai a complexidade da API nativa {@link java.net.Socket}, oferecendo:
+ *
+ * Este comportamento é vital quando o processo Coordenador inicia antes das Interseções estarem
+ * prontas para aceitar conexões ({@code accept()}).
*
- * @param host endereço do host (ex: "localhost")
- * @param port número da porta
- * @throws IOException se falhar após todas as tentativas
- * @throws UnknownHostException se o host não for encontrado
- * @throws InterruptedException se a thread for interrompida
+ * @param host Endereço do nó de destino (ex: "localhost").
+ * @param port Porta de serviço.
+ * @throws IOException Se a conexão falhar após todas as {@code MAX_RETRIES} tentativas.
+ * @throws UnknownHostException Se o DNS não resolver o hostname.
+ * @throws InterruptedException Se a thread for interrompida durante o sleep de retry.
*/
public SocketConnection(String host, int port) throws IOException, UnknownHostException, InterruptedException {
Socket tempSocket = null;
@@ -52,7 +63,7 @@ public class SocketConnection implements Closeable {
// --- Retry Loop ---
for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
try {
- // Try to establish the connection
+ // Try to establish the connection (SYN -> SYN-ACK -> ACK)
tempSocket = new Socket(host, port);
// If successful, break out of the retry loop
@@ -61,17 +72,17 @@ public class SocketConnection implements Closeable {
break;
} catch (ConnectException | SocketTimeoutException e) {
- // These are common errors indicating the server might not be ready.
+ // Common errors: "Connection refused" (server not up) or "Timeout" (firewall/network)
lastException = e;
System.out.printf("[SocketConnection] Attempt %d/%d failed: %s. Retrying in %d ms...%n",
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
if (attempt < MAX_RETRIES) {
- // Wait before the next attempt
+ // Blocking wait before next attempt
TimeUnit.MILLISECONDS.sleep(RETRY_DELAY_MS);
}
} catch (IOException e) {
- // Other IOExceptions might be more permanent, but we retry anyway.
+ // Other IO errors
lastException = e;
System.out.printf("[SocketConnection] Attempt %d/%d failed with IOException: %s. Retrying in %d ms...%n",
attempt, MAX_RETRIES, e.getMessage(), RETRY_DELAY_MS);
@@ -81,51 +92,49 @@ public class SocketConnection implements Closeable {
}
} // --- End of Retry Loop ---
- // If after all retries tempSocket is still null, it means connection failed permanently.
+ // Final validation
if (tempSocket == null) {
System.err.printf("[SocketConnection] Failed to connect to %s:%d after %d attempts.%n", host, port, MAX_RETRIES);
if (lastException != null) {
- throw lastException; // Throw the last exception encountered
+ throw lastException; // Propagate the root cause
} else {
- // Should not happen if loop ran, but as a fallback
throw new IOException("Failed to connect after " + MAX_RETRIES + " attempts, reason unknown.");
}
}
- // If connection was successful, assign to final variable and create streams
+ // Initialize streams
this.socket = tempSocket;
-
this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream();
this.serializer = SerializerFactory.createDefault();
-
}
-
/**
- * Constructor for the "Server" (who accepts the connection).
- * Receives a Socket that has already been accepted by a ServerSocket.
- * No retry logic needed here as the connection is already established.
+ * Construtor para servidores (Passive Open).
+ * Envolve um socket já conectado (retornado por {@code serverSocket.accept()}).
+ * Não necessita de retry logic pois a conexão física já existe.
*
- * @param acceptedSocket The Socket returned by serverSocket.accept().
- * @throws IOException If stream creation fails.
+ * @param acceptedSocket O socket ativo retornado pelo SO.
+ * @throws IOException Se falhar a obtenção dos streams de I/O.
*/
public SocketConnection(Socket acceptedSocket) throws IOException {
this.socket = acceptedSocket;
this.outputStream = socket.getOutputStream();
this.inputStream = socket.getInputStream();
this.serializer = SerializerFactory.createDefault();
-
}
/**
- * Sends (serializes) a MessageProtocol object over the socket.
+ * Serializa e transmite uma mensagem através do canal.
+ *
+ * Utiliza sincronização ({@code synchronized}) para garantir que escritas concorrentes
+ * na mesma conexão não corrompem a stream de bytes (thread-safety).
*
- * @param message The "envelope" (which contains the Vehicle) to be sent.
- * @throws IOException If writing to the stream fails or socket is not connected.
+ * @param message O objeto de protocolo a enviar.
+ * @throws IOException Se o socket estiver fechado ou ocorrer erro de escrita.
*/
public synchronized void sendMessage(MessageProtocol message) throws IOException {
- if (socket == null || !socket.isConnected()) {
+ if (socket == null || !socket.isConnected()) {
throw new IOException("Socket is not connected");
}
@@ -133,11 +142,11 @@ public class SocketConnection implements Closeable {
// Serializa para bytes JSON
byte[] data = serializer.serialize(message);
- // Write 4-byte length prefix
+ // Write 4-byte length prefix (Framing)
DataOutputStream dataOut = new DataOutputStream(outputStream);
dataOut.writeInt(data.length);
dataOut.write(data);
- dataOut.flush();
+ dataOut.flush(); // Force transmission immediately
} catch (SerializationException e) {
throw new IOException("Failed to serialize message", e);
@@ -145,11 +154,14 @@ public class SocketConnection implements Closeable {
}
/**
- * Tries to read (deserialize) a MessageProtocol object from the socket.
+ * Bloqueia à espera de uma mensagem completa do socket.
+ *
+ * Lê primeiro o cabeçalho de tamanho (4 bytes) e depois o payload exato,
+ * garantindo que processa mensagens completas mesmo se chegarem fragmentadas em múltiplos pacotes TCP.
*
- * @return The "envelope" (MessageProtocol) that was received.
- * @throws IOException If the connection is lost, the stream is corrupted, or socket is not connected.
- * @throws ClassNotFoundException If the received object is unknown.
+ * @return O objeto {@link MessageProtocol} reconstruído.
+ * @throws IOException Se a conexão for perdida (EOF) ou o stream corrompido.
+ * @throws ClassNotFoundException Se o tipo desserializado não for encontrado no classpath.
*/
public MessageProtocol receiveMessage() throws IOException, ClassNotFoundException {
if (socket == null || !socket.isConnected()) {
@@ -161,15 +173,16 @@ public class SocketConnection implements Closeable {
DataInputStream dataIn = new DataInputStream(inputStream);
int length = dataIn.readInt();
- if (length <= 0 || length > 10_000_000) { // Sanity check (10MB max)
+ // Sanity check para evitar OutOfMemory em caso de corrupção de stream
+ if (length <= 0 || length > 10_000_000) { // Max 10MB payload
throw new IOException("Invalid message length: " + length);
}
- // Ler dados da mensagem
+ // Ler dados exatos da mensagem
byte[] data = new byte[length];
dataIn.readFully(data);
- // Deserialize do JSON - use concrete Message class, not interface
+ // Deserialize do JSON - força o tipo concreto Message
return serializer.deserialize(data, sd.model.Message.class);
} catch (SerializationException e) {
@@ -178,7 +191,8 @@ public class SocketConnection implements Closeable {
}
/**
- * Closes the socket and all streams (Input and Output).
+ * Encerra a conexão e liberta os descritores de ficheiro.
+ * Operação idempotente.
*/
@Override
public void close() throws IOException {
@@ -188,7 +202,8 @@ public class SocketConnection implements Closeable {
}
/**
- * @return true if the socket is still connected and not closed.
+ * Verifica o estado operacional da conexão.
+ * @return true se o socket está aberto e conectado.
*/
public boolean isConnected() {
return socket != null && socket.isConnected() && !socket.isClosed();
diff --git a/main/src/main/java/sd/serialization/JsonMessageSerializer.java b/main/src/main/java/sd/serialization/JsonMessageSerializer.java
index 1b70c68..68c6b06 100644
--- a/main/src/main/java/sd/serialization/JsonMessageSerializer.java
+++ b/main/src/main/java/sd/serialization/JsonMessageSerializer.java
@@ -1,26 +1,25 @@
package sd.serialization;
+import java.nio.charset.StandardCharsets;
+
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
-import java.nio.charset.StandardCharsets;
-
/**
- * JSON-based implementation of {@link MessageSerializer} using Google's Gson library.
- *
- * This serializer converts objects to JSON format for transmission, providing:
- * - Human-readable message format (easy debugging)
- * - Cross-platform compatibility
- * - Smaller message sizes compared to Java native serialization
- * - Better security (no code execution during deserialization)
- *
- * The serializer is configured with pretty printing disabled by default for
- * production use, but can be enabled for debugging purposes.
- *
- * Thread-safety: This class is thread-safe as Gson instances are thread-safe.
- *
- * @see MessageSerializer
+ * Implementação baseada em JSON da estratégia {@link MessageSerializer}, utilizando a biblioteca Gson.
+ *
+ * Este serializador converte objetos Java para o formato de texto JSON antes da transmissão.
+ * Oferece várias vantagens técnicas sobre a serialização nativa do Java:
+ *
+ * Thread-Safety: A instância interna do {@code Gson} é imutável e thread-safe, permitindo
+ * que este serializador seja partilhado entre múltiplas threads (ex: no pool do DashboardServer).
+ * * @see MessageSerializer
*/
public class JsonMessageSerializer implements MessageSerializer {
@@ -28,16 +27,16 @@ public class JsonMessageSerializer implements MessageSerializer {
private final boolean prettyPrint;
/**
- * Creates a new JSON serializer with default configuration (no pretty printing).
+ * Cria um novo serializador JSON com configuração otimizada para produção (compacto).
*/
public JsonMessageSerializer() {
this(false);
}
/**
- * Creates a new JSON serializer with optional pretty printing.
- *
- * @param prettyPrint If true, JSON output will be formatted with indentation
+ * Cria um novo serializador JSON com formatação opcional.
+ * * @param prettyPrint Se {@code true}, o JSON gerado incluirá indentação e quebras de linha.
+ * Útil para debug, mas aumenta significativamente o tamanho do payload.
*/
public JsonMessageSerializer(boolean prettyPrint) {
this.prettyPrint = prettyPrint;
@@ -53,6 +52,13 @@ public class JsonMessageSerializer implements MessageSerializer {
this.gson = builder.create();
}
+ /**
+ * Converte um objeto em memória para um array de bytes JSON (UTF-8).
+ *
+ * @param object O objeto a ser serializado.
+ * @return O payload em bytes pronto para transmissão TCP.
+ * @throws SerializationException Se o objeto não for compatível com JSON ou ocorrer erro de encoding.
+ */
@Override
public byte[] serialize(Object object) throws SerializationException {
if (object == null) {
@@ -68,6 +74,16 @@ public class JsonMessageSerializer implements MessageSerializer {
}
}
+ /**
+ * Reconstrói um objeto Java a partir de um array de bytes JSON.
+ *
+ * Realiza a validação sintática do JSON e a validação de tipo baseada na classe alvo.
+ *
+ * @param data O array de bytes recebido da rede.
+ * @param clazz A classe do objeto esperado (Type Token).
+ * @return A instância do objeto reconstruído.
+ * @throws SerializationException Se o JSON for malformado ou incompatível com a classe alvo.
+ */
@Override
public
+ * Esta abstração permite desacoplar a camada de transporte (Sockets TCP) da camada de
+ * apresentação de dados. Ao implementar o padrão Strategy, o sistema ganha flexibilidade
+ * para alternar entre diferentes formatos de codificação (JSON, Binário Nativo, XML, Protobuf)
+ * sem necessidade de refatorização da lógica de rede.
+ *
+ * Requisitos para Implementações:
+ *
+ * Esta classe atua como um wrapper unificador para erros ocorridos na camada de serialização,
+ * abstraindo falhas de baixo nível (como erros de I/O, sintaxe JSON inválida ou incompatibilidade
+ * de tipos) numa única exceção de domínio. Permite que o código cliente trate falhas de
+ * protocolo de forma consistente, independentemente da implementação subjacente (Gson, Nativa, etc.).
*/
public class SerializationException extends Exception {
private static final long serialVersionUID = 1L; // Long(64bits) instead of int(32bits)
/**
- * Constructs a new serialization exception with the specified detail message.
- *
- * @param message The detail message
+ * Constrói uma nova exceção de serialização com uma mensagem descritiva.
+ * * @param message A mensagem detalhando o erro.
*/
public SerializationException(String message) {
super(message);
}
/**
- * Constructs a new serialization exception with the specified detail message
- * and cause.
- *
- * @param message The detail message
- * @param cause The cause of this exception
+ * Constrói uma nova exceção encapsulando a causa raiz do problema.
+ * Útil para preservar a stack trace original de erros de bibliotecas terceiras (ex: Gson).
+ * * @param message A mensagem detalhando o erro.
+ * @param cause A exceção original que causou a falha.
*/
public SerializationException(String message, Throwable cause) {
super(message, cause);
}
/**
- * Constructs a new serialization exception with the specified cause.
- *
- * @param cause The cause of this exception
+ * Constrói uma nova exceção baseada apenas na causa raiz.
+ * * @param cause A exceção original.
*/
public SerializationException(Throwable cause) {
super(cause);
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/serialization/SerializerFactory.java b/main/src/main/java/sd/serialization/SerializerFactory.java
index a2261d3..70d2f0e 100644
--- a/main/src/main/java/sd/serialization/SerializerFactory.java
+++ b/main/src/main/java/sd/serialization/SerializerFactory.java
@@ -1,14 +1,14 @@
package sd.serialization;
/**
- * Factory for creating {@link MessageSerializer} instances.
- *
- * This factory provides a centralized way to create and configure JSON serializers
- * using Gson, making it easy to configure serialization throughout the application.
- *
- * The factory can be configured via system properties for easy deployment configuration.
- *
- * Example usage:
+ * Fábrica estática (Factory Pattern) para instanciação controlada de {@link MessageSerializer}.
+ *
+ * Esta classe centraliza a criação de estratégias de serialização, garantindo consistência
+ * de configuração em todo o sistema distribuído. Permite a injeção de configurações via
+ * Propriedades de Sistema (System Properties), facilitando a alternância entre modos de
+ * depuração (Pretty Print) e produção (Compacto) sem recompilação.
+ *
+ * Exemplo de Uso:
*
+ * Verifica a propriedade de sistema {@value #JSON_PRETTY_PRINT_PROPERTY}.
+ * Se não definida, assume o padrão de produção (falso/compacto).
+ * * @return Uma instância configurada de {@link JsonMessageSerializer}.
*/
public static MessageSerializer createDefault() {
boolean prettyPrint = Boolean.getBoolean(JSON_PRETTY_PRINT_PROPERTY);
@@ -46,21 +45,20 @@ public class SerializerFactory {
}
/**
- * Creates a JSON serializer with default configuration (no pretty printing).
- *
- * @return A JsonMessageSerializer instance
+ * Cria um serializador JSON com configuração padrão otimizada (sem indentação).
+ * Ideal para ambientes de produção onde a largura de banda é prioritária.
+ * * @return Uma instância compacta de {@link JsonMessageSerializer}.
*/
public static MessageSerializer createSerializer() {
return createSerializer(DEFAULT_JSON_PRETTY_PRINT);
}
/**
- * Creates a JSON serializer with specified pretty-print setting.
- *
- * @param prettyPrint Whether to enable pretty printing
- * @return A JsonMessageSerializer instance
+ * Cria um serializador JSON com configuração explícita de formatação.
+ * * @param prettyPrint {@code true} para ativar indentação (Debug), {@code false} para compacto.
+ * @return Uma instância personalizada de {@link JsonMessageSerializer}.
*/
public static MessageSerializer createSerializer(boolean prettyPrint) {
return new JsonMessageSerializer(prettyPrint);
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/util/RandomGenerator.java b/main/src/main/java/sd/util/RandomGenerator.java
index 5b9c65c..e9ab436 100644
--- a/main/src/main/java/sd/util/RandomGenerator.java
+++ b/main/src/main/java/sd/util/RandomGenerator.java
@@ -3,82 +3,88 @@ package sd.util;
import java.util.Random;
/**
- * Utilitário para gerar valores aleatórios usados na simulação.
- *
- * Fornece métodos estáticos para:
+ * Esta classe fornece primitivas para geração de números pseudo-aleatórios, abstraindo
+ * a complexidade de distribuições estatísticas.
+ *
+ * Funcionalidades Principais:
* Usa uma única instância estática de {@link Random}. Componente essencial para modelar processos de Poisson, onde os
- * tempos entre chegadas seguem uma distribuição exponencial. Fórmula: {@code Time = -ln(1 - U) / λ}
+ * Este método implementa o algoritmo de Inverse Transform Sampling para simular
+ * um Processo de Poisson homogêneo. É fundamental para modelar a chegada natural de
+ * veículos, onde eventos independentes ocorrem a uma taxa média constante.
+ *
+ * Fórmula Matemática: {@code T = -ln(1 - U) / λ}
+ * Útil para tomar decisões ponderadas. Por exemplo,
- * {@code occursWithProbability(0.3)} retorna {@code true}
- * aproximadamente 30% das vezes.
+ * Utilizado para decisões de ramificação estocástica (ex: "Este veículo é um camião?").
*
- * @param probability valor entre 0.0 (nunca) e 1.0 (sempre)
- * @return {@code true} ou {@code false}, baseado na probabilidade
+ * @param probability A probabilidade de retorno {@code true} (0.0 a 1.0).
+ * @return {@code true} se o evento ocorrer, {@code false} caso contrário.
*/
public static boolean occursWithProbability(double probability) {
return random.nextDouble() < probability;
}
/**
- * Escolhe um elemento aleatório do array fornecido.
+ * Seleciona aleatoriamente um elemento de um array genérico (Amostragem Uniforme Discreta).
*
- * @param Extremamente útil para debugging e testes, pois permite executar
- * a simulação múltiplas vezes com a mesma sequência de eventos "aleatórios",
- * tornando os resultados reproduzíveis.
+ * Importância Crítica: Permite tornar a simulação determinística. Ao fixar a seed,
+ * a sequência de números "aleatórios" gerada será idêntica em execuções subsequentes,
+ * facilitando a depuração de race conditions ou lógica complexa.
*
- * @param seed seed a usar
+ * @param seed O valor da semente inicial (ex: timestamp ou constante).
*/
public static void setSeed(long seed) {
random.setSeed(seed);
diff --git a/main/src/main/java/sd/util/VehicleGenerator.java b/main/src/main/java/sd/util/VehicleGenerator.java
index dce42ce..344b4d7 100644
--- a/main/src/main/java/sd/util/VehicleGenerator.java
+++ b/main/src/main/java/sd/util/VehicleGenerator.java
@@ -9,55 +9,58 @@ import sd.model.VehicleType;
import sd.routing.RouteSelector;
/**
- * Gera veículos para a simulação.
- *
- * Esta classe é responsável por duas tarefas principais:
+ * Esta classe atua como uma fábrica estocástica de veículos, sendo responsável por:
* As rotas são selecionadas usando uma política de roteamento que pode ser:
- * aleatória, caminho mais curto, menor congestionamento, etc.
+ * Se o modelo for "POISSON", utiliza a técnica de Inverse Transform Sampling
+ * (via {@link RandomGenerator}) para gerar intervalos exponencialmente distribuídos,
+ * simulando a aleatoriedade natural do tráfego.
+ * * @param currentTime O tempo atual da simulação (base de cálculo).
+ * @return O instante futuro (t + delta) para agendamento do evento de geração.
*/
public double getNextArrivalTime(double currentTime) {
if ("POISSON".equalsIgnoreCase(arrivalModel)) {
@@ -69,19 +72,19 @@ public class VehicleGenerator {
}
/**
- * Gera um novo objeto {@link Vehicle}.
- *
- * Passos executados:
+ * O processo de criação segue um pipeline:
* Usa técnica de "probabilidade cumulativa":
+ * Normaliza as probabilidades configuradas e mapeia um número aleatório [0, 1)
+ * para o intervalo correspondente ao tipo de veículo.
*
- * @return tipo de veículo selecionado
+ * @return O tipo enumerado {@link VehicleType} selecionado.
*/
private VehicleType selectVehicleType() {
double bikeProbability = config.getBikeVehicleProbability();
@@ -111,7 +108,9 @@ public class VehicleGenerator {
double heavyProbability = config.getHeavyVehicleProbability();
double total = bikeProbability + lightProbability + heavyProbability;
- if (total == 0) return VehicleType.LIGHT; // Avoid division by zero
+ if (total == 0) return VehicleType.LIGHT; // Fallback de segurança
+
+ // Normalização
bikeProbability /= total;
lightProbability /= total;
@@ -127,10 +126,10 @@ public class VehicleGenerator {
}
/**
- * Seleciona aleatoriamente um ponto de entrada (E1, E2 ou E3).
- * Cada ponto tem probabilidade igual (1/3).
+ * Seleciona um ponto de injeção na borda da rede (Edge Node).
+ * Distribuição Uniforme: ~33.3% para cada entrada (E1, E2, E3).
*
- * @return ponto de entrada selecionado ("E1", "E2" ou "E3")
+ * @return O ID da interseção de entrada.
*/
private String selectRandomEntryPoint() {
double rand = Math.random();
@@ -145,23 +144,19 @@ public class VehicleGenerator {
}
/**
- * Altera dinamicamente o RouteSelector usado para gerar rotas.
- * Permite mudar a política de roteamento durante a simulação.
- *
- * @param newRouteSelector novo seletor de rotas
+ * Atualiza a estratégia de roteamento em tempo de execução (Hot-Swap).
+ *
+ * Permite que o Coordenador altere o comportamento da frota (ex: de RANDOM para SHORTEST_PATH)
+ * sem necessidade de reiniciar a simulação.
+ * * @param newRouteSelector A nova implementação de estratégia a utilizar.
*/
public void setRouteSelector(RouteSelector newRouteSelector) {
- // Note: In Java, we can't directly modify the 'final' field,
- // but we can create a new VehicleGenerator with the new selector.
- // For this implementation, we'll need to remove 'final' from routeSelector.
- // This is acceptable since we want dynamic policy changes.
- throw new UnsupportedOperationException(
- "VehicleGenerator is immutable. Use CoordinatorProcess.changeRoutingPolicy() instead."
- );
+ this.routeSelector = newRouteSelector;
}
/**
- * @return A string providing information about the generator's configuration.
+ * Retorna uma representação textual do estado interno do gerador.
+ * Útil para logs de auditoria e debugging.
*/
public String getInfo() {
return String.format(
+ *
+ *
+ *
*/
private void startEventProcessor() {
eventProcessorThread = new Thread(() -> {
@@ -145,9 +159,9 @@ public class IntersectionProcess {
while (running) {
SimulationEvent event = eventQueue.poll();
if (event == null) {
- // No events currently, wait a bit before checking again
+ // Backoff exponencial ou sleep curto para evitar busy-waiting em idle
try {
- Thread.sleep(50); // Short sleep to avoid busy-waiting
+ Thread.sleep(50);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
@@ -155,7 +169,7 @@ public class IntersectionProcess {
continue;
}
- // Apply time scaling for visualization
+ // Aplicação de escala temporal (Throttle)
if (timeScale > 0) {
double simTimeDelta = event.getTimestamp() - lastTime;
long realDelayMs = (long) (simTimeDelta * timeScale * 1000);
@@ -170,10 +184,10 @@ public class IntersectionProcess {
lastTime = event.getTimestamp();
}
- // Advance clock to event time
+ // Atualização atómica do tempo de simulação
clock.advanceTo(event.getTimestamp());
- // Process the event
+ // Processamento polimórfico
processEvent(event);
}
@@ -185,10 +199,12 @@ public class IntersectionProcess {
}
/**
- * Processa um evento da fila de simulação.
- * Cada tipo de evento é encaminhado para o seu tratador específico.
- *
- * @param event o evento a processar
+ * Despachante central de eventos.
+ *
+ *
+ *
+ * @param event O evento que desencadeou a mudança de estado.
*/
private void handleTrafficLightChangeEvent(SimulationEvent event) {
TrafficLightEvent tlEvent = (TrafficLightEvent) event.getPayload();
@@ -252,12 +274,12 @@ public class IntersectionProcess {
String.format("Direction %s changed to %s at time %.2f",
direction, newState, event.getTimestamp()));
- // If light turned GREEN, process queued vehicles
+ // Processamento de lote (Batch Processing) para a fase Verde
if (newState == TrafficLightState.GREEN) {
processQueuedVehiclesForLight(light, event.getTimestamp());
}
- // Schedule next state change
+ // Agendamento do próximo ciclo (Feedback Loop)
double nextChangeTime = event.getTimestamp() +
(newState == TrafficLightState.GREEN ? light.getGreenTime() : light.getRedTime());
@@ -269,23 +291,19 @@ public class IntersectionProcess {
}
/**
- * Processa a fila de veículos quando um semáforo fica verde.
- *
+ * Calcula a vazão da interseção durante uma fase verde.
*
- *
- *
- *
+ *
+ *
+ * @param event O evento de fim de travessia.
*/
private void handleVehicleCrossingEndEvent(SimulationEvent event) {
Vehicle vehicle = (Vehicle) event.getPayload();
- // Add crossing time to vehicle stats
+ // Atualiza métricas do veículo
double crossingTime = getCrossingTimeForVehicle(vehicle);
vehicle.addCrossingTime(crossingTime);
- // Update intersection statistics
+ // Atualiza métricas locais
intersection.incrementVehiclesSent();
- // Send vehicle to next destination
+ // Handover: Transfere a responsabilidade do veículo para a rede
sendVehicleToNextDestination(vehicle);
eventLogger.log(sd.logging.EventType.VEHICLE_DEPARTED, intersectionId,
@@ -404,10 +428,9 @@ public class IntersectionProcess {
}
/**
- * Trata o evento de fim da simulação.
- * Define a flag de execução como falsa para terminar o processamento.
+ * Finaliza a execução do processo de simulação.
*
- * @param event o evento de fim de simulação
+ * @param event O evento de fim de simulação.
*/
private void handleSimulationEndEvent(SimulationEvent event) {
eventLogger.log(sd.logging.EventType.SIMULATION_STOPPED, intersectionId,
@@ -416,10 +439,9 @@ public class IntersectionProcess {
}
/**
- * Exporta o histórico completo de eventos para um ficheiro.
- * Útil para análise posterior e debugging da simulação.
- *
- * @param outputPath caminho do ficheiro onde guardar o histórico
+ * Exporta o histórico completo de eventos para análise post-mortem.
+ *
+ * @param outputPath O caminho do ficheiro onde o histórico será guardado.
*/
public void exportEventHistory(String outputPath) {
String history = eventQueue.exportEventHistory();
@@ -431,7 +453,12 @@ public class IntersectionProcess {
}
}
- // Main entry point for running an intersection process
+ /**
+ * Ponto de entrada principal da aplicação.
+ *
+ * @param args Argumentos da linha de comando (ID da interseção e ficheiro de
+ * configuração opcional).
+ */
public static void main(String[] args) {
if (args.length < 1) {
System.err.println("Usage: java IntersectionProcess
+ *
*/
public void shutdown() {
- // Check if already shutdown
if (!running) {
- return; // Already shutdown, do nothing
+ return;
}
System.out.println("\n[" + intersectionId + "] Shutting down...");
running = false;
- // Send final stats before closing connections
sendStatsToDashboard();
- // 1. Close ServerSocket first
+ // 1. Close ServerSocket
if (serverSocket != null && !serverSocket.isClosed()) {
try {
serverSocket.close();
@@ -918,8 +962,7 @@ public class IntersectionProcess {
}
}
- // 2. Shutdown thread pools with force
-
+ // 2. Shutdown thread pools
if (connectionHandlerPool != null && !connectionHandlerPool.isShutdown()) {
connectionHandlerPool.shutdownNow();
}
@@ -930,9 +973,8 @@ public class IntersectionProcess {
departureExecutor.shutdownNow();
}
- // 3. Wait briefly for termination (don't block forever)
+ // 3. Wait briefly for termination
try {
-
if (connectionHandlerPool != null) {
connectionHandlerPool.awaitTermination(1, TimeUnit.SECONDS);
}
@@ -968,31 +1010,32 @@ public class IntersectionProcess {
}
/**
- * Gets the Intersection object managed by this process.
- * Useful for testing and monitoring.
- *
- * @return The Intersection object.
+ * Obtém o modelo de dados da interseção.
+ *
+ * @return O objeto Intersection.
*/
public Intersection getIntersection() {
return intersection;
}
/**
- * Records that a vehicle has arrived at this intersection.
+ * Regista a chegada de um novo veículo para fins estatísticos.
*/
public void recordVehicleArrival() {
totalArrivals++;
}
/**
- * Records that a vehicle has departed from this intersection.
+ * Regista a partida de um veículo para fins estatísticos.
*/
public void recordVehicleDeparture() {
totalDepartures++;
}
/**
- * Sends current statistics to the dashboard server.
+ * Envia um "snapshot" do estado atual para o Dashboard (Telemetria Push).
+ *
+ *
*/
public class DashboardUI extends Application {
@@ -52,7 +66,7 @@ public class DashboardUI extends Application {
// Intersection Table
private TableView
+ *
*/
public class SimulationProcessManager {
@@ -16,6 +24,10 @@ public class SimulationProcessManager {
private final String classpath;
private String configFile;
+ /**
+ * Inicializa o gestor capturando o classpath da JVM atual.
+ * Isto garante que os processos filhos herdam as mesmas dependências e configurações de ambiente.
+ */
public SimulationProcessManager() {
this.runningProcesses = new ArrayList<>();
this.classpath = System.getProperty("java.class.path");
@@ -23,9 +35,9 @@ public class SimulationProcessManager {
}
/**
- * Define o ficheiro de configuração a usar.
- *
- * @param configFile nome do ficheiro (ex: "simulation-low.properties")
+ * Define o perfil de configuração a ser injetado nos processos filhos.
+ * Útil para alternar entre cenários (Low/Medium/High Load) dinamicamente.
+ * * @param configFile Nome do ficheiro de propriedades (ex: "simulation-low.properties").
*/
public void setConfigFile(String configFile) {
this.configFile = "src/main/resources/" + configFile;
@@ -33,9 +45,16 @@ public class SimulationProcessManager {
}
/**
- * Inicia a simulação completa: 5 Intersections, 1 Exit Node, e 1 Coordinator.
- *
- * @throws IOException se um processo falhar ao iniciar
+ * Executa o procedimento de arranque (Bootstrap) da simulação distribuída.
+ *
+ *
+ * * @throws IOException Se falhar o fork de algum processo.
*/
public void startSimulation() throws IOException {
if (!runningProcesses.isEmpty()) {
@@ -65,8 +84,11 @@ public class SimulationProcessManager {
}
/**
- * Checks if the coordinator process (last process started) is still running.
- * When the coordinator finishes, the simulation is complete.
+ * Verifica o estado de "liveness" da simulação monitorizando o processo Coordenador.
+ *
+ *
+ * Implementa {@link Serializable} para transmissão direta via Java Sockets.
+ *
+
+[Image of data transfer object pattern]
+
*/
public class StatsUpdatePayload implements Serializable {
private static final long serialVersionUID = 1L;
+ // Global Metrics (Coordinator/ExitNode)
+ /** Total gerado. Valor -1 indica que este campo deve ser ignorado na atualização. */
private int totalVehiclesGenerated = -1;
+
+ /** Total completado. Valor -1 indica que este campo deve ser ignorado. */
private int totalVehiclesCompleted = -1;
+
+ /** Tempo total de sistema acumulado (ms). Valor -1 indica que deve ser ignorado. */
private long totalSystemTime = -1;
+
+ /** Tempo total de espera acumulado (ms). Valor -1 indica que deve ser ignorado. */
private long totalWaitingTime = -1;
+ // Intersection Metrics (Worker Nodes)
+ /** Número de veículos que entraram na interseção desde o último reporte. */
private int intersectionArrivals = 0;
+
+ /** Número de veículos que saíram da interseção desde o último reporte. */
private int intersectionDepartures = 0;
+
+ /** Snapshot do tamanho atual da fila na interseção. */
private int intersectionQueueSize = 0;
+ // Detailed Breakdowns
+ /** Contagem acumulada por tipo de veículo. */
private Map
- *
*/
public class SimulationEvent implements Comparable
+ *
+ *
+ * @param other O outro evento a comparar.
+ * @return Inteiro negativo, zero ou positivo conforme a ordem.
*/
@Override
public int compareTo(SimulationEvent other) {
@@ -67,7 +99,7 @@ public class SimulationEvent implements Comparable
+ *
*/
public class EventLogger {
@@ -22,20 +31,33 @@ public class EventLogger {
private static final Object instanceLock = new Object();
private final PrintWriter writer;
+
+ /** Buffer de memória para absorver picos de eventos (Burst traffic). */
private final BlockingQueue
+ *
*/
public enum EventType {
+ // --- Ciclo de Vida do Veículo ---
VEHICLE_GENERATED("Vehicle Generated"),
VEHICLE_ARRIVED("Vehicle Arrived"),
VEHICLE_QUEUED("Vehicle Queued"),
VEHICLE_DEPARTED("Vehicle Departed"),
VEHICLE_EXITED("Vehicle Exited"),
+ // --- Controlo de Semáforos e Exclusão Mútua ---
LIGHT_CHANGED_GREEN("Light Changed to Green"),
LIGHT_CHANGED_RED("Light Changed to Red"),
LIGHT_REQUEST_GREEN("Light Requested Green"),
LIGHT_RELEASE_GREEN("Light Released Green"),
+ // --- Ciclo de Vida da Simulação/Processos ---
SIMULATION_STARTED("Simulation Started"),
SIMULATION_STOPPED("Simulation Stopped"),
PROCESS_STARTED("Process Started"),
PROCESS_STOPPED("Process Stopped"),
+ // --- Configuração e Telemetria ---
STATS_UPDATE("Statistics Update"),
CONFIG_CHANGED("Configuration Changed"),
+ // --- Camada de Rede (TCP/Sockets) ---
CONNECTION_ESTABLISHED("Connection Established"),
CONNECTION_LOST("Connection Lost"),
MESSAGE_SENT("Message Sent"),
MESSAGE_RECEIVED("Message Received"),
+ // --- Tratamento de Exceções ---
ERROR("Error");
private final String displayName;
@@ -45,4 +57,4 @@ public enum EventType {
public String toString() {
return displayName;
}
-}
+}
\ No newline at end of file
diff --git a/main/src/main/java/sd/logging/VehicleTracer.java b/main/src/main/java/sd/logging/VehicleTracer.java
index 194f319..611e6aa 100644
--- a/main/src/main/java/sd/logging/VehicleTracer.java
+++ b/main/src/main/java/sd/logging/VehicleTracer.java
@@ -12,16 +12,18 @@ import java.util.concurrent.ConcurrentHashMap;
import sd.model.Vehicle;
/**
- * Rastreia e regista a viagem completa de veículos individuais.
- *
+ * Subsistema de auditoria granular responsável pelo rastreio detalhado (Tracing) de veículos individuais.
*
- *
*/
public class VehicleTracer {
@@ -29,12 +31,18 @@ public class VehicleTracer {
private static VehicleTracer instance;
private static final Object instanceLock = new Object();
+ /** Mapa thread-safe de sessões de trace ativas (VehicleID -> TraceHandler). */
private final Map
+ *
*/
public class SocketConnection implements Closeable {
@@ -28,20 +35,24 @@ public class SocketConnection implements Closeable {
private final InputStream inputStream;
private final MessageSerializer serializer;
- /** Número máximo de tentativas de ligação */
+ /** Número máximo de tentativas de ligação antes de desistir (Fail-fast). */
private static final int MAX_RETRIES = 5;
- /** Atraso entre tentativas (milissegundos) */
+
+ /** Janela de espera (backoff) linear entre tentativas (em milissegundos). */
private static final long RETRY_DELAY_MS = 1000;
/**
- * Construtor do cliente que inicia a ligação.
- * Tenta ligar a um servidor já em escuta, com retry.
+ * Construtor para clientes (Active Open).
+ * Tenta estabelecer uma conexão TCP com um servidor, aplicando lógica de retry.
+ *
+ *
+ *
+ *
+ * * @see JsonMessageSerializer
*/
public interface MessageSerializer {
/**
- * Serializes an object into a byte array for transmission.
- *
- * @param object The object to serialize (must not be null)
- * @return A byte array containing the serialized representation
- * @throws SerializationException If serialization fails
- * @throws IllegalArgumentException If object is null
+ * Converte (Marshals) um objeto em memória para uma sequência de bytes para transmissão.
+ * * @param object O objeto de domínio a ser serializado (não pode ser nulo).
+ * @return Um array de bytes contendo a representação codificada do objeto.
+ * @throws SerializationException Se ocorrer um erro durante a codificação (ex: ciclo de referências).
+ * @throws IllegalArgumentException Se o objeto fornecido for nulo.
*/
byte[] serialize(Object object) throws SerializationException;
/**
- * Deserializes a byte array back into an object of the specified type.
- *
- * @param
* MessageSerializer serializer = SerializerFactory.createDefault();
* byte[] data = serializer.serialize(myObject);
@@ -17,28 +17,27 @@ package sd.serialization;
public class SerializerFactory {
/**
- * System property key for enabling pretty-print in JSON serialization.
- * Set to "true" for debugging, "false" for production.
+ * Chave da propriedade de sistema para ativar a formatação JSON legível (Pretty Print).
+ * Defina {@code -Dsd.serialization.json.prettyPrint=true} na JVM para ativar.
*/
public static final String JSON_PRETTY_PRINT_PROPERTY = "sd.serialization.json.prettyPrint";
- // Default configuration
+ // Default configuration (Production-ready)
private static final boolean DEFAULT_JSON_PRETTY_PRINT = false;
/**
- * Private constructor to prevent instantiation.
+ * Construtor privado para prevenir instanciação acidental desta classe utilitária.
*/
private SerializerFactory() {
throw new UnsupportedOperationException("Factory class cannot be instantiated");
}
/**
- * Creates a JSON serializer based on system configuration.
- *
- * Pretty-print is determined by checking the system property
- * {@value #JSON_PRETTY_PRINT_PROPERTY}. If not set, defaults to false.
- *
- * @return A configured JsonMessageSerializer instance
+ * Cria um serializador JSON configurado dinamicamente pelo ambiente.
+ *
- *
- *
- *
- * onde U é um número aleatório uniforme [0, 1) e λ (lambda) é a taxa média de chegada.
Onde:
+ *
+ *
*
- * @param lambda taxa média de chegada λ (ex: 0.5 veículos por segundo)
- * @return intervalo de tempo (segundos) até à próxima chegada
+ * @param lambda A taxa média de chegada (λ > 0).
+ * @return O intervalo de tempo (delta t) até o próximo evento, em segundos.
*/
public static double generateExponentialInterval(double lambda) {
return Math.log(1 - random.nextDouble()) / -lambda;
}
/**
- * Retorna um inteiro aleatório entre {@code min} e {@code max}, inclusive.
+ * Gera um número inteiro uniformemente distribuído no intervalo fechado {@code [min, max]}.
*
- * @param min valor mínimo possível
- * @param max valor máximo possível
- * @return inteiro aleatório no intervalo [min, max]
+ * @param min Limite inferior (inclusivo).
+ * @param max Limite superior (inclusivo).
+ * @return Um inteiro aleatório I tal que {@code min <= I <= max}.
*/
public static int generateRandomInt(int min, int max) {
return random.nextInt(max - min + 1) + min;
}
/**
- * Retorna um double aleatório entre {@code min} (inclusive) e {@code max} (exclusivo).
+ * Gera um número de ponto flutuante uniformemente distribuído no intervalo semi-aberto {@code [min, max)}.
*
- * @param min valor mínimo possível
- * @param max valor máximo possível
- * @return double aleatório no intervalo [min, max)
+ * @param min Limite inferior (inclusivo).
+ * @param max Limite superior (exclusivo).
+ * @return Um double aleatório D tal que {@code min <= D < max}.
*/
public static double generateRandomDouble(double min, double max) {
return min + (max - min) * random.nextDouble();
}
/**
- * Retorna {@code true} com uma dada probabilidade.
- *
- *
- *
- *
- *
- *
*
- * @param vehicleId identificador único do novo veículo (ex: "V123")
- * @param entryTime tempo de simulação em que o veículo é criado
- * @param queueSizes mapa com tamanho das filas (opcional, pode ser null)
- * @return novo objeto {@link Vehicle} configurado
+ * @param vehicleId O identificador único sequencial (ex: "V104").
+ * @param entryTime O timestamp de criação.
+ * @param queueSizes Snapshot atual das filas (usado apenas por estratégias dinâmicas como LEAST_CONGESTED).
+ * @return A entidade {@link Vehicle} pronta para inserção na malha.
*/
public Vehicle generateVehicle(String vehicleId, double entryTime, Map
- *
+ * Seleciona o tipo de veículo usando Amostragem por Probabilidade Cumulativa.
+ *