mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 12:33:31 +00:00
more javadoc - dashboard, des, logging
This commit is contained in:
@@ -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.
|
||||
* <p>
|
||||
* Esta classe atua como a camada de apresentação (View) do sistema. Implementa o padrão
|
||||
* <i>Observer</i> (via polling) para refletir o estado do modelo {@link DashboardStatistics}
|
||||
* nos componentes visuais.
|
||||
* <p>
|
||||
* <b>Aspetos Técnicos Relevantes:</b>
|
||||
* <ul>
|
||||
* <li><b>Concorrência de UI:</b> Utiliza um {@link ScheduledExecutorService} para buscar dados
|
||||
* em background e {@link Platform#runLater(Runnable)} para injetar atualizações na
|
||||
* <i>JavaFX Application Thread</i>, evitando exceções de "Not on FX application thread".</li>
|
||||
* <li><b>Data Binding:</b> Utiliza {@link TableView} com classes internas (DTOs) para
|
||||
* renderização tabular eficiente de tipos de veículos e interseções.</li>
|
||||
* <li><b>Controlo de Processos:</b> Integra com {@link SimulationProcessManager} para orquestrar
|
||||
* o ciclo de vida (spawn/kill) dos processos externos da simulação.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class DashboardUI extends Application {
|
||||
|
||||
@@ -52,7 +66,7 @@ public class DashboardUI extends Application {
|
||||
// Intersection Table
|
||||
private TableView<IntersectionRow> intersectionTable;
|
||||
|
||||
// Update scheduler
|
||||
// Update scheduler (Background Thread)
|
||||
private ScheduledExecutorService updateScheduler;
|
||||
|
||||
// Configuration controls
|
||||
@@ -60,6 +74,10 @@ public class DashboardUI extends Application {
|
||||
private String selectedConfigFile = "simulation.properties";
|
||||
private Label configInfoLabel;
|
||||
|
||||
/**
|
||||
* Ponto de entrada da aplicação JavaFX.
|
||||
* Configura o Stage primário, inicializa o servidor de backend e constrói a árvore de cena (Scene Graph).
|
||||
*/
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
try {
|
||||
@@ -72,29 +90,27 @@ public class DashboardUI extends Application {
|
||||
server = new DashboardServer(config);
|
||||
statistics = server.getStatistics();
|
||||
|
||||
// Start the dashboard server
|
||||
// Start the dashboard server (Backend listening port)
|
||||
server.start();
|
||||
|
||||
// Build UI
|
||||
// Build UI Layout
|
||||
BorderPane root = new BorderPane();
|
||||
root.getStyleClass().add("root");
|
||||
|
||||
// Header
|
||||
// Header (Top)
|
||||
VBox header = createHeader();
|
||||
root.setTop(header);
|
||||
|
||||
// Main content
|
||||
// Main content (Center)
|
||||
VBox mainContent = createMainContent();
|
||||
root.setCenter(mainContent);
|
||||
|
||||
// Footer
|
||||
// Footer (Bottom)
|
||||
HBox footer = createFooter();
|
||||
root.setBottom(footer);
|
||||
|
||||
// Create scene
|
||||
// Create scene & apply CSS
|
||||
Scene scene = new Scene(root, 1200, 850);
|
||||
|
||||
// Load CSS
|
||||
String cssUrl = getClass().getResource("/dashboard.css").toExternalForm();
|
||||
scene.getStylesheets().add(cssUrl);
|
||||
|
||||
@@ -102,10 +118,10 @@ public class DashboardUI extends Application {
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
|
||||
// Start periodic updates
|
||||
// Start periodic updates loop
|
||||
startPeriodicUpdates();
|
||||
|
||||
// Handle window close
|
||||
// Handle window close (Graceful shutdown)
|
||||
primaryStage.setOnCloseRequest(event -> {
|
||||
shutdown();
|
||||
});
|
||||
@@ -149,6 +165,8 @@ public class DashboardUI extends Application {
|
||||
// Passar o ficheiro de configuração selecionado
|
||||
processManager.setConfigFile(selectedConfigFile);
|
||||
processManager.startSimulation();
|
||||
|
||||
// Toggle UI state
|
||||
btnStart.setDisable(true);
|
||||
btnStop.setDisable(false);
|
||||
configFileSelector.setDisable(true); // Bloquear mudanças durante simulação
|
||||
@@ -159,6 +177,8 @@ public class DashboardUI extends Application {
|
||||
|
||||
btnStop.setOnAction(e -> {
|
||||
processManager.stopSimulation();
|
||||
|
||||
// Toggle UI state
|
||||
btnStart.setDisable(false);
|
||||
btnStop.setDisable(true);
|
||||
configFileSelector.setDisable(false); // Desbloquear para nova simulação
|
||||
@@ -435,13 +455,23 @@ public class DashboardUI extends Application {
|
||||
grid.add(container, colGroup, row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inicia o ciclo de polling em background.
|
||||
* Atualiza a UI a cada 100ms.
|
||||
*/
|
||||
private void startPeriodicUpdates() {
|
||||
updateScheduler = Executors.newSingleThreadScheduledExecutor();
|
||||
updateScheduler.scheduleAtFixedRate(() -> {
|
||||
// Crucial: Encapsular atualização de UI em Platform.runLater
|
||||
// para garantir execução na JavaFX Application Thread
|
||||
Platform.runLater(this::updateUI);
|
||||
}, 0, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sincroniza o estado atual do objeto Statistics com os controlos JavaFX.
|
||||
* Chamado periodicamente pela thread de UI.
|
||||
*/
|
||||
private void updateUI() {
|
||||
// Update global statistics
|
||||
lblVehiclesGenerated.setText(String.valueOf(statistics.getTotalVehiclesGenerated()));
|
||||
@@ -548,7 +578,9 @@ public class DashboardUI extends Application {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
// Inner classes for TableView data models
|
||||
// --- DTOs para Data Binding nas Tabelas ---
|
||||
|
||||
/** DTO para linhas da tabela de Tipos de Veículo. */
|
||||
public static class VehicleTypeRow {
|
||||
private final String vehicleType;
|
||||
private final int count;
|
||||
@@ -560,19 +592,12 @@ public class DashboardUI extends Application {
|
||||
this.avgWaitTime = avgWaitTime;
|
||||
}
|
||||
|
||||
public String getVehicleType() {
|
||||
return vehicleType;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public String getAvgWaitTime() {
|
||||
return avgWaitTime;
|
||||
}
|
||||
public String getVehicleType() { return vehicleType; }
|
||||
public int getCount() { return count; }
|
||||
public String getAvgWaitTime() { return avgWaitTime; }
|
||||
}
|
||||
|
||||
/** DTO para linhas da tabela de Interseções. */
|
||||
public static class IntersectionRow {
|
||||
private final String intersectionId;
|
||||
private final int arrivals;
|
||||
@@ -586,20 +611,9 @@ public class DashboardUI extends Application {
|
||||
this.queueSize = queueSize;
|
||||
}
|
||||
|
||||
public String getIntersectionId() {
|
||||
return intersectionId;
|
||||
}
|
||||
|
||||
public int getArrivals() {
|
||||
return arrivals;
|
||||
}
|
||||
|
||||
public int getDepartures() {
|
||||
return departures;
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
return queueSize;
|
||||
}
|
||||
public String getIntersectionId() { return intersectionId; }
|
||||
public int getArrivals() { return arrivals; }
|
||||
public int getDepartures() { return departures; }
|
||||
public int getQueueSize() { return queueSize; }
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,17 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Gere o ciclo de vida dos processos de simulação (Intersections, Exit Node,
|
||||
* Coordinator).
|
||||
* Permite iniciar e parar a simulação distribuída dentro da aplicação Java.
|
||||
* Orquestrador de processos para o ambiente de simulação distribuída.
|
||||
* <p>
|
||||
* Esta classe atua como um supervisor (Process Manager), responsável pelo <i>bootstrapping</i>
|
||||
* e <i>teardown</i> das múltiplas Java Virtual Machines (JVMs) que compõem o sistema.
|
||||
* <p>
|
||||
* Funcionalidades principais:
|
||||
* <ul>
|
||||
* <li><b>Isolamento:</b> Cada nó (Interseção, Coordinator, ExitNode) corre no seu próprio processo OS.</li>
|
||||
* <li><b>Ordem de Arranque:</b> Garante que os servidores (Interseções) estão online antes dos clientes (Coordenador).</li>
|
||||
* <li><b>Gestão de Logs:</b> Redireciona stdout/stderr de cada processo filho para ficheiros temporários para facilitar o debug.</li>
|
||||
* </ul>
|
||||
*/
|
||||
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.
|
||||
* <p>
|
||||
* A ordem de inicialização é crítica para evitar <i>Race Conditions</i> na conexão TCP:
|
||||
* <ol>
|
||||
* <li><b>Workers (Interseções):</b> Iniciam os ServerSockets.</li>
|
||||
* <li><b>Sink (Exit Node):</b> Prepara-se para receber métricas finais.</li>
|
||||
* <li><b>Delay de Estabilização:</b> Pausa de 1s para garantir que os sockets estão em LISTENING.</li>
|
||||
* <li><b>Source (Coordinator):</b> Inicia a geração de carga e conecta-se aos nós.</li>
|
||||
* </ol>
|
||||
* * @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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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";
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* 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;
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* 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 <b>atualizações parciais</b>
|
||||
* (Sparse Updates):
|
||||
* <ul>
|
||||
* <li>Campos globais inicializados com {@code -1} indicam "sem alteração" (no-op). O Dashboard
|
||||
* deve ignorar estes campos e manter o valor acumulado anterior.</li>
|
||||
* <li>Campos de interseção ({@code arrivals}, {@code departures}) representam deltas ou snapshots
|
||||
* específicos do nó remetente.</li>
|
||||
* </ul>
|
||||
* 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<VehicleType, Integer> vehicleTypeCounts;
|
||||
|
||||
/** Tempos de espera acumulados por tipo de veículo. */
|
||||
private Map<VehicleType, Long> vehicleTypeWaitTimes;
|
||||
|
||||
/**
|
||||
* Inicializa o payload com os mapas vazios e contadores globais a -1 (estado neutro).
|
||||
*/
|
||||
public StatsUpdatePayload() {
|
||||
this.vehicleTypeCounts = new HashMap<>();
|
||||
this.vehicleTypeWaitTimes = new HashMap<>();
|
||||
@@ -67,6 +102,8 @@ public class StatsUpdatePayload implements Serializable {
|
||||
return vehicleTypeWaitTimes;
|
||||
}
|
||||
|
||||
// Setters implementam Fluent Interface para construção encadeada
|
||||
|
||||
public StatsUpdatePayload setTotalVehiclesGenerated(int totalVehiclesGenerated) {
|
||||
this.totalVehiclesGenerated = totalVehiclesGenerated;
|
||||
return this;
|
||||
|
||||
@@ -3,31 +3,46 @@ package sd.des;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Evento discreto da simulação.
|
||||
*
|
||||
* <p>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).
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Características principais:
|
||||
* <ul>
|
||||
* <li>timestamp - quando ocorre
|
||||
* <li>type - o que acontece
|
||||
* <li>payload - dados associados
|
||||
* <li>location - qual processo o trata
|
||||
* <li><b>Ordenação Temporal:</b> Implementa {@link Comparable} para ser armazenado numa Fila de
|
||||
* Eventos Futuros (FEL - Future Event List), garantindo execução cronológica.</li>
|
||||
* <li><b>Distribuído:</b> Implementa {@link Serializable} para permitir que eventos gerados num nó
|
||||
* (ex: Coordenador) sejam transmitidos e executados noutro (ex: Interseção).</li>
|
||||
* <li><b>Polimórfico:</b> Transporta um {@code payload} genérico, permitindo associar qualquer
|
||||
* entidade (Veículo, Sinal, etc.) ao evento.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SimulationEvent implements Comparable<SimulationEvent>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** O instante virtual exato em que o evento deve ser processado. */
|
||||
private final double timestamp;
|
||||
|
||||
/** A categoria do evento (ex: VEHICLE_ARRIVAL, LIGHT_CHANGE). */
|
||||
private final DESEventType type;
|
||||
|
||||
/** Dados contextuais associados (ex: o objeto Vehicle que chegou). */
|
||||
private final Object payload;
|
||||
private final String location; // Process ID (e.g., "Cr1", "Coordinator", "Exit")
|
||||
|
||||
/**
|
||||
* Cria um novo evento de simulação.
|
||||
* O identificador do nó de destino onde o evento deve ser executado.
|
||||
* (ex: "Cr1", "Coordinator", "ExitNode"). Se null, é um evento local.
|
||||
*/
|
||||
private final String location;
|
||||
|
||||
/**
|
||||
* Instancia um novo evento de simulação completo.
|
||||
*
|
||||
* @param timestamp instante do evento (tempo de simulação em segundos)
|
||||
* @param type tipo de evento
|
||||
* @param payload dados associados (ex: objeto Vehicle)
|
||||
* @param location processo que trata o evento
|
||||
* @param timestamp Instante de execução (segundos virtuais).
|
||||
* @param type Tipo enumerado do evento.
|
||||
* @param payload Objeto de dados associado (pode ser null).
|
||||
* @param location ID do processo alvo para execução distribuída.
|
||||
*/
|
||||
public SimulationEvent(double timestamp, DESEventType type, Object payload, String location) {
|
||||
this.timestamp = timestamp;
|
||||
@@ -36,7 +51,14 @@ public class SimulationEvent implements Comparable<SimulationEvent>, Serializabl
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
/** Cria evento sem localização (para eventos locais) */
|
||||
/**
|
||||
* Construtor de conveniência para eventos locais (dentro do mesmo processo).
|
||||
* Define {@code location} como null.
|
||||
*
|
||||
* @param timestamp Instante de execução.
|
||||
* @param type Tipo do evento.
|
||||
* @param payload Objeto de dados associado.
|
||||
*/
|
||||
public SimulationEvent(double timestamp, DESEventType type, Object payload) {
|
||||
this(timestamp, type, payload, null);
|
||||
}
|
||||
@@ -58,8 +80,18 @@ public class SimulationEvent implements Comparable<SimulationEvent>, Serializabl
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordena eventos por timestamp (mais cedo primeiro).
|
||||
* Em caso de empate, ordena por tipo para determinismo.
|
||||
* Define a ordem natural de processamento na Fila de Prioridade.
|
||||
* <p>
|
||||
* <b>Lógica de Ordenação:</b>
|
||||
* <ol>
|
||||
* <li><b>Primária (Tempo):</b> Eventos com menor timestamp ocorrem primeiro.</li>
|
||||
* <li><b>Secundária (Determinismo):</b> Em caso de empate temporal (simultaneidade),
|
||||
* ordena alfabeticamente pelo nome do tipo. Isto garante que execuções repetidas
|
||||
* da simulação produzam exatamente o mesmo resultado (determinismo estrito).</li>
|
||||
* </ol>
|
||||
*
|
||||
* @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<SimulationEvent>, Serializabl
|
||||
if (timeComparison != 0) {
|
||||
return timeComparison;
|
||||
}
|
||||
// Tie-breaker: order by event type name
|
||||
// Tie-breaker: order by event type name to ensure reproducible runs
|
||||
return this.type.name().compareTo(other.type.name());
|
||||
}
|
||||
|
||||
|
||||
@@ -3,28 +3,47 @@ package sd.des;
|
||||
import sd.model.TrafficLight;
|
||||
|
||||
/**
|
||||
* Payload for traffic light change events.
|
||||
* Contains the traffic light and its direction.
|
||||
* Encapsula o contexto de dados para eventos de mudança de estado de semáforos.
|
||||
* <p>
|
||||
* Este objeto atua como o <i>payload</i> 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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
* <p>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.</p>
|
||||
* Motor de logging assíncrono e thread-safe para a simulação distribuída.
|
||||
* <p>
|
||||
* Implementa o padrão <i>Singleton</i> para garantir um ponto centralizado de registo.
|
||||
* Utiliza o padrão <i>Producer-Consumer</i> 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).
|
||||
* <p>
|
||||
* <b>Garantias:</b>
|
||||
* <ul>
|
||||
* <li>Non-blocking writes (para a thread chamadora, na maioria dos casos).</li>
|
||||
* <li>Ordering cronológico aproximado (FIFO na fila).</li>
|
||||
* <li>Graceful Shutdown (flush de logs pendentes ao terminar).</li>
|
||||
* </ul>
|
||||
*/
|
||||
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<LogEntry> logQueue;
|
||||
|
||||
/** Thread dedicada (Consumer) para escrita em ficheiro. */
|
||||
private final Thread writerThread;
|
||||
|
||||
private final AtomicBoolean running;
|
||||
private final SimpleDateFormat timestampFormat;
|
||||
private final long simulationStartMillis;
|
||||
|
||||
/** Construtor privado para padrão singleton */
|
||||
/**
|
||||
* Inicializa o sistema de logs.
|
||||
* Abre o ficheiro, escreve o cabeçalho e inicia a thread consumidora.
|
||||
*
|
||||
* @param logFilePath Caminho relativo ou absoluto do ficheiro de log.
|
||||
* @throws IOException Se não for possível criar ou escrever no ficheiro.
|
||||
*/
|
||||
private EventLogger(String logFilePath) throws IOException {
|
||||
// Auto-flush ativado para garantir persistência, mas gerido pelo buffer do BufferedWriter
|
||||
this.writer = new PrintWriter(new BufferedWriter(new FileWriter(logFilePath, false)), true);
|
||||
this.logQueue = new LinkedBlockingQueue<>(10000);
|
||||
this.logQueue = new LinkedBlockingQueue<>(10000); // Backpressure: limita a 10k eventos pendentes
|
||||
this.running = new AtomicBoolean(true);
|
||||
this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
this.simulationStartMillis = System.currentTimeMillis();
|
||||
|
||||
// Header inicial do log
|
||||
writer.println("=".repeat(80));
|
||||
writer.println("SIMULATION EVENT LOG");
|
||||
writer.println("Started: " + timestampFormat.format(new Date()));
|
||||
@@ -47,11 +69,16 @@ public class EventLogger {
|
||||
writer.flush();
|
||||
|
||||
this.writerThread = new Thread(this::processLogQueue, "EventLogger-Writer");
|
||||
this.writerThread.setDaemon(true);
|
||||
this.writerThread.setDaemon(true); // Permite que a JVM termine se apenas esta thread sobrar
|
||||
this.writerThread.start();
|
||||
}
|
||||
|
||||
/** Obtém ou cria a instância singleton */
|
||||
/**
|
||||
* Obtém a instância única do logger (Lazy Initialization).
|
||||
* Se não existir, cria uma predefinida em "logs/simulation-events.log".
|
||||
*
|
||||
* @return A instância singleton.
|
||||
*/
|
||||
public static EventLogger getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (instanceLock) {
|
||||
@@ -72,7 +99,8 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize with custom log file path.
|
||||
* Reinicializa o logger com um ficheiro específico.
|
||||
* Útil para testes ou configurações personalizadas.
|
||||
*/
|
||||
public static void initialize(String logFilePath) throws IOException {
|
||||
synchronized (instanceLock) {
|
||||
@@ -84,7 +112,13 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event (non-blocking).
|
||||
* Regista um evento genérico.
|
||||
* Esta operação é não-bloqueante (retorna imediatamente após colocar na fila),
|
||||
* exceto se a fila estiver cheia (backpressure).
|
||||
*
|
||||
* @param eventType Categoria do evento.
|
||||
* @param component Nome do componente (ex: "Coordinator", "IntersectionProcess").
|
||||
* @param description Detalhes do evento.
|
||||
*/
|
||||
public void log(EventType eventType, String component, String description) {
|
||||
if (!running.get()) return;
|
||||
@@ -96,7 +130,7 @@ public class EventLogger {
|
||||
description
|
||||
);
|
||||
|
||||
// Non-blocking offer - if queue is full, drop oldest
|
||||
// Non-blocking offer - if queue is full, drop oldest or warn
|
||||
if (!logQueue.offer(entry)) {
|
||||
// Queue full - this shouldn't happen with 10k buffer, but handle gracefully
|
||||
System.err.println("EventLogger queue full - dropping event: " + eventType);
|
||||
@@ -104,14 +138,14 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event with vehicle context.
|
||||
* Regista um evento associado a um veículo específico (Helper method).
|
||||
*/
|
||||
public void logVehicle(EventType eventType, String component, String vehicleId, String description) {
|
||||
log(eventType, component, "[" + vehicleId + "] " + description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error event.
|
||||
* Regista um erro ou exceção com formatação apropriada.
|
||||
*/
|
||||
public void logError(String component, String description, Exception e) {
|
||||
String fullDescription = description + (e != null ? ": " + e.getMessage() : "");
|
||||
@@ -119,11 +153,13 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Background thread that writes log entries to file.
|
||||
* Lógica da thread consumidora (Worker Thread).
|
||||
* Retira eventos da fila e escreve no disco continuamente.
|
||||
*/
|
||||
private void processLogQueue() {
|
||||
while (running.get() || !logQueue.isEmpty()) {
|
||||
try {
|
||||
// Poll com timeout para permitir verificar a flag 'running' periodicamente
|
||||
LogEntry entry = logQueue.poll(100, java.util.concurrent.TimeUnit.MILLISECONDS);
|
||||
if (entry != null) {
|
||||
writeEntry(entry);
|
||||
@@ -134,7 +170,7 @@ public class EventLogger {
|
||||
}
|
||||
}
|
||||
|
||||
// Flush remaining entries
|
||||
// Flush final: garantir que eventos restantes na fila são escritos antes de morrer
|
||||
while (!logQueue.isEmpty()) {
|
||||
LogEntry entry = logQueue.poll();
|
||||
if (entry != null) {
|
||||
@@ -144,7 +180,7 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a single log entry to file.
|
||||
* Formata e escreve uma entrada de log no PrintWriter.
|
||||
*/
|
||||
private void writeEntry(LogEntry entry) {
|
||||
String timestamp = timestampFormat.format(new Date(entry.timestampMillis));
|
||||
@@ -158,7 +194,7 @@ public class EventLogger {
|
||||
entry.description
|
||||
);
|
||||
|
||||
// Flush periodically for real-time viewing
|
||||
// Flush periódico inteligente: se a carga for baixa, garante que vemos logs em tempo real
|
||||
if (logQueue.size() < 10) {
|
||||
writer.flush();
|
||||
}
|
||||
@@ -170,15 +206,17 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the logger and flushes all pending entries.
|
||||
* Encerra o logger de forma segura.
|
||||
* Desativa a aceitação de novos eventos, aguarda que a fila esvazie (flush)
|
||||
* e fecha o ficheiro.
|
||||
*/
|
||||
public void shutdown() {
|
||||
if (!running.compareAndSet(true, false)) {
|
||||
return; // Already shut down
|
||||
return; // Já encerrado
|
||||
}
|
||||
|
||||
try {
|
||||
// Wait for writer thread to finish
|
||||
// Wait for writer thread to finish flushing
|
||||
writerThread.join(5000); // Wait up to 5 seconds
|
||||
|
||||
// Write footer
|
||||
@@ -195,7 +233,7 @@ public class EventLogger {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class to represent a log entry.
|
||||
* DTO interno imutável para armazenar dados do evento na fila.
|
||||
*/
|
||||
private static class LogEntry {
|
||||
final long timestampMillis;
|
||||
|
||||
@@ -1,34 +1,46 @@
|
||||
package sd.logging;
|
||||
|
||||
/**
|
||||
* Tipos de eventos que podem ocorrer na simulação.
|
||||
* Usados para categorizar e filtrar logs.
|
||||
* Taxonomia oficial de eventos para o subsistema de logging centralizado.
|
||||
* <p>
|
||||
* Este enumerado padroniza a categorização de todas as ocorrências na simulação, permitindo:
|
||||
* <ul>
|
||||
* <li>Filtragem granular de logs (ex: ver apenas erros ou apenas tráfego de rede).</li>
|
||||
* <li>Análise estatística post-mortem (parsear logs para calcular latências).</li>
|
||||
* <li>Correlação de eventos distribuídos (seguir o rastro de um veículo através de vários nós).</li>
|
||||
* </ul>
|
||||
*/
|
||||
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;
|
||||
|
||||
@@ -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.
|
||||
* <p>
|
||||
* Cria ficheiros de trace detalhados com:
|
||||
* Diferente do {@link EventLogger} (que regista eventos globais do sistema), esta classe foca-se
|
||||
* na perspetiva do <b>agente</b>. 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).
|
||||
* <p>
|
||||
* <b>Funcionalidades:</b>
|
||||
* <ul>
|
||||
* <li>Timestamps de todos os eventos
|
||||
* <li>Localizações (interseções)
|
||||
* <li>Tempos de espera em cada semáforo
|
||||
* <li>Tempos de travessia
|
||||
* <li>Tempo total no sistema
|
||||
* <li>Análise forense de percursos individuais.</li>
|
||||
* <li>Validação de tempos de espera e travessia por nó.</li>
|
||||
* <li>Cálculo de eficiência de rota (tempo em movimento vs. tempo parado).</li>
|
||||
* </ul>
|
||||
*/
|
||||
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<String, VehicleTrace> trackedVehicles;
|
||||
|
||||
private final SimpleDateFormat timestampFormat;
|
||||
private final long simulationStartMillis;
|
||||
private final String traceDirectory;
|
||||
|
||||
/** Construtor privado (singleton) */
|
||||
/**
|
||||
* Inicializa o tracer e prepara o diretório de saída.
|
||||
*
|
||||
* @param traceDirectory Caminho para armazenamento dos ficheiros .trace.
|
||||
*/
|
||||
private VehicleTracer(String traceDirectory) {
|
||||
this.trackedVehicles = new ConcurrentHashMap<>();
|
||||
this.timestampFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
|
||||
@@ -48,7 +56,10 @@ public class VehicleTracer {
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtém ou cria a instância singleton */
|
||||
/**
|
||||
* Obtém a instância única do tracer (Singleton).
|
||||
* @return A instância global.
|
||||
*/
|
||||
public static VehicleTracer getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (instanceLock) {
|
||||
@@ -60,7 +71,10 @@ public class VehicleTracer {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** Inicializa com diretório de trace customizado */
|
||||
/**
|
||||
* Reinicializa o tracer com um diretório personalizado.
|
||||
* Útil para isolar logs de diferentes execuções em lote.
|
||||
*/
|
||||
public static void initialize(String traceDirectory) {
|
||||
synchronized (instanceLock) {
|
||||
if (instance != null) {
|
||||
@@ -71,12 +85,14 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Começa a rastrear um veículo específico.
|
||||
* Cria ficheiro de trace para este veículo.
|
||||
* Inicia a sessão de rastreio para um veículo específico.
|
||||
* Cria o ficheiro {@code logs/traces/vehicle-{id}.trace} e escreve o cabeçalho.
|
||||
*
|
||||
* @param vehicleId O identificador único do veículo.
|
||||
*/
|
||||
public void startTracking(String vehicleId) {
|
||||
if (trackedVehicles.containsKey(vehicleId)) {
|
||||
return; // Already tracking
|
||||
return; // Já está a ser rastreado
|
||||
}
|
||||
|
||||
VehicleTrace trace = new VehicleTrace(vehicleId, traceDirectory);
|
||||
@@ -86,7 +102,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops tracking a vehicle and closes its trace file.
|
||||
* Encerra a sessão de rastreio, fecha o descritor de ficheiro e remove da memória.
|
||||
*/
|
||||
public void stopTracking(String vehicleId) {
|
||||
VehicleTrace trace = trackedVehicles.remove(vehicleId);
|
||||
@@ -97,14 +113,14 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a vehicle is being tracked.
|
||||
* Verifica se um veículo está atualmente sob auditoria.
|
||||
*/
|
||||
public boolean isTracking(String vehicleId) {
|
||||
return trackedVehicles.containsKey(vehicleId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle is generated.
|
||||
* Regista o evento de instanciação do veículo pelo Coordenador.
|
||||
*/
|
||||
public void logGenerated(Vehicle vehicle) {
|
||||
if (!isTracking(vehicle.getId()))
|
||||
@@ -119,7 +135,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle arrives at an intersection.
|
||||
* Regista a chegada física do veículo à zona de deteção de uma interseção.
|
||||
*/
|
||||
public void logArrival(String vehicleId, String intersection, double simulationTime) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -133,7 +149,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle is queued at a traffic light.
|
||||
* Regista a entrada do veículo na estrutura de fila de um semáforo.
|
||||
*/
|
||||
public void logQueued(String vehicleId, String intersection, String direction, int queuePosition) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -147,7 +163,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle starts waiting at a red light.
|
||||
* Regista o início da espera ativa (veículo parado no Vermelho).
|
||||
*/
|
||||
public void logWaitingStart(String vehicleId, String intersection, String direction) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -161,7 +177,8 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle finishes waiting (light turns green).
|
||||
* Regista o fim da espera (Sinal Verde).
|
||||
* @param waitTime Duração total da paragem nesta instância.
|
||||
*/
|
||||
public void logWaitingEnd(String vehicleId, String intersection, String direction, double waitTime) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -175,7 +192,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle starts crossing an intersection.
|
||||
* Regista o início da travessia da interseção (ocupação da zona crítica).
|
||||
*/
|
||||
public void logCrossingStart(String vehicleId, String intersection, String direction) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -189,7 +206,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle finishes crossing an intersection.
|
||||
* Regista a libertação da zona crítica da interseção.
|
||||
*/
|
||||
public void logCrossingEnd(String vehicleId, String intersection, double crossingTime) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -203,7 +220,7 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle departs from an intersection.
|
||||
* Regista a partida da interseção em direção ao próximo nó.
|
||||
*/
|
||||
public void logDeparture(String vehicleId, String intersection, String nextDestination) {
|
||||
if (!isTracking(vehicleId))
|
||||
@@ -217,7 +234,10 @@ public class VehicleTracer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs when a vehicle exits the system.
|
||||
* Regista a saída do sistema (no Exit Node).
|
||||
* <p>
|
||||
* Este método também desencadeia a escrita do <b>Sumário de Viagem</b> 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;
|
||||
|
||||
Reference in New Issue
Block a user