From 96c5680f41f6f715854eb7c6aac24bfff41a50fa Mon Sep 17 00:00:00 2001 From: Leandro Afonso Date: Sat, 22 Nov 2025 23:52:51 +0000 Subject: [PATCH] moved start to dashboard + fixed holding queue - looped sleep might be fine in this case + better customization via CSS file --- .../src/main/java/sd/IntersectionProcess.java | 34 +- .../sd/coordinator/CoordinatorProcess.java | 157 +-- .../main/java/sd/dashboard/DashboardUI.java | 412 ++++--- .../dashboard/SimulationProcessManager.java | 112 ++ .../java/sd/protocol/SocketConnection.java | 3 +- main/src/main/resources/dashboard.css | 142 +++ .../java/sd/TrafficLightCoordinationTest.java | 69 +- main/testing.txt | 1055 +++++++++++++++++ 8 files changed, 1681 insertions(+), 303 deletions(-) create mode 100644 main/src/main/java/sd/dashboard/SimulationProcessManager.java create mode 100644 main/src/main/resources/dashboard.css create mode 100644 main/testing.txt diff --git a/main/src/main/java/sd/IntersectionProcess.java b/main/src/main/java/sd/IntersectionProcess.java index b909bfc..6694aa8 100644 --- a/main/src/main/java/sd/IntersectionProcess.java +++ b/main/src/main/java/sd/IntersectionProcess.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -47,6 +48,8 @@ public class IntersectionProcess { private final ExecutorService trafficLightPool; + private ScheduledExecutorService statsExecutor; + private volatile boolean running; // Quando uma thread escreve um valor volatile, todas as outras // threads veem a mudança imediatamente. @@ -67,7 +70,6 @@ public class IntersectionProcess { private SocketClient dashboardClient; private volatile int totalArrivals = 0; private volatile int totalDepartures = 0; - private long lastStatsUpdateTime; /** * Constructs a new IntersectionProcess. @@ -83,8 +85,9 @@ public class IntersectionProcess { this.outgoingConnections = new HashMap<>(); this.connectionHandlerPool = Executors.newCachedThreadPool(); this.trafficLightPool = Executors.newFixedThreadPool(4); // Max 4 directions + this.statsExecutor = Executors.newSingleThreadScheduledExecutor(); this.running = false; - this.trafficCoordinationLock = new ReentrantLock(); + this.trafficCoordinationLock = new ReentrantLock(true); // Fair lock to prevent starvation this.currentGreenDirection = null; System.out.println("=".repeat(60)); @@ -148,7 +151,6 @@ public class IntersectionProcess { dashboardClient.connect(); System.out.println("[" + intersectionId + "] Connected to dashboard."); - lastStatsUpdateTime = System.currentTimeMillis(); } catch (IOException e) { System.err.println("[" + intersectionId + "] Failed to connect to dashboard: " + @@ -365,6 +367,9 @@ public class IntersectionProcess { // Start traffic light threads when running is true startTrafficLights(); + // Start stats updater + statsExecutor.scheduleAtFixedRate(this::sendStatsToDashboard, 1, 1, TimeUnit.SECONDS); + System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n"); // Main accept loop @@ -523,6 +528,9 @@ public class IntersectionProcess { if (connectionHandlerPool != null && !connectionHandlerPool.isShutdown()) { connectionHandlerPool.shutdownNow(); } + if (statsExecutor != null && !statsExecutor.isShutdown()) { + statsExecutor.shutdownNow(); + } // 3. Wait briefly for termination (don't block forever) try { @@ -532,6 +540,9 @@ public class IntersectionProcess { if (connectionHandlerPool != null) { connectionHandlerPool.awaitTermination(1, TimeUnit.SECONDS); } + if (statsExecutor != null) { + statsExecutor.awaitTermination(1, TimeUnit.SECONDS); + } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } @@ -572,7 +583,6 @@ public class IntersectionProcess { */ public void recordVehicleArrival() { totalArrivals++; - checkAndSendStats(); } /** @@ -580,22 +590,6 @@ public class IntersectionProcess { */ public void recordVehicleDeparture() { totalDepartures++; - checkAndSendStats(); - } - - /** - * Checks if it's time to send statistics to the dashboard and sends them if - * needed. - */ - private void checkAndSendStats() { - long now = System.currentTimeMillis(); - long elapsed = now - lastStatsUpdateTime; - - // Send stats every 5 seconds - if (elapsed >= 5000) { - sendStatsToDashboard(); - lastStatsUpdateTime = now; - } } /** diff --git a/main/src/main/java/sd/coordinator/CoordinatorProcess.java b/main/src/main/java/sd/coordinator/CoordinatorProcess.java index 163dc5a..e7cb5b6 100644 --- a/main/src/main/java/sd/coordinator/CoordinatorProcess.java +++ b/main/src/main/java/sd/coordinator/CoordinatorProcess.java @@ -21,7 +21,7 @@ import sd.util.VehicleGenerator; * This is the main entry point for the distributed simulation architecture. */ public class CoordinatorProcess { - + private final SimulationConfig config; private final VehicleGenerator vehicleGenerator; private final Map intersectionClients; @@ -30,28 +30,28 @@ public class CoordinatorProcess { private int vehicleCounter; private boolean running; private double nextGenerationTime; - + public static void main(String[] args) { System.out.println("=".repeat(60)); System.out.println("COORDINATOR PROCESS - DISTRIBUTED TRAFFIC SIMULATION"); System.out.println("=".repeat(60)); - + try { // 1. Load configuration String configFile = args.length > 0 ? args[0] : "src/main/resources/simulation.properties"; System.out.println("Loading configuration from: " + configFile); - + SimulationConfig config = new SimulationConfig(configFile); CoordinatorProcess coordinator = new CoordinatorProcess(config); - + // 2. Connect to intersection processes System.out.println("\n" + "=".repeat(60)); coordinator.initialize(); - + // 3. Run the sim System.out.println("\n" + "=".repeat(60)); coordinator.run(); - + } catch (IOException e) { System.err.println("Failed to load configuration: " + e.getMessage()); System.exit(1); @@ -60,7 +60,7 @@ public class CoordinatorProcess { System.exit(1); } } - + public CoordinatorProcess(SimulationConfig config) { this.config = config; this.vehicleGenerator = new VehicleGenerator(config); @@ -69,131 +69,137 @@ public class CoordinatorProcess { this.vehicleCounter = 0; this.running = false; this.nextGenerationTime = 0.0; - + System.out.println("Coordinator initialized with configuration:"); System.out.println(" - Simulation duration: " + config.getSimulationDuration() + "s"); System.out.println(" - Arrival model: " + config.getArrivalModel()); System.out.println(" - Arrival rate: " + config.getArrivalRate() + " vehicles/s"); } - + public void initialize() { // Connect to dashboard first connectToDashboard(); - + System.out.println("Connecting to intersection processes..."); - - String[] intersectionIds = {"Cr1", "Cr2", "Cr3", "Cr4", "Cr5"}; - + + String[] intersectionIds = { "Cr1", "Cr2", "Cr3", "Cr4", "Cr5" }; + for (String intersectionId : intersectionIds) { try { String host = config.getIntersectionHost(intersectionId); int port = config.getIntersectionPort(intersectionId); - + SocketClient client = new SocketClient(intersectionId, host, port); client.connect(); intersectionClients.put(intersectionId, client); - + } catch (IOException e) { System.err.println("Failed to connect to " + intersectionId + ": " + e.getMessage()); } } - + System.out.println("Successfully connected to " + intersectionClients.size() + " intersection(s)"); - + if (intersectionClients.isEmpty()) { System.err.println("WARNING: No intersections connected. Simulation cannot proceed."); } } - + public void run() { double duration = config.getSimulationDuration(); running = true; - + System.out.println("Starting vehicle generation simulation..."); System.out.println("Duration: " + duration + " seconds"); System.out.println(); - + // Send simulation start time to all processes for synchronization sendSimulationStartTime(); - + nextGenerationTime = vehicleGenerator.getNextArrivalTime(currentTime); final double TIME_STEP = 0.1; - + while (running && currentTime < duration) { if (currentTime >= nextGenerationTime) { generateAndSendVehicle(); nextGenerationTime = vehicleGenerator.getNextArrivalTime(currentTime); - } + } + + try { + Thread.sleep((long) (TIME_STEP * 1000)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + currentTime += TIME_STEP; } - + System.out.println(); System.out.println("Simulation complete at t=" + String.format("%.2f", currentTime) + "s"); System.out.println("Total vehicles generated: " + vehicleCounter); - + shutdown(); } - + private void generateAndSendVehicle() { Vehicle vehicle = vehicleGenerator.generateVehicle("V" + (++vehicleCounter), currentTime); - + System.out.printf("[t=%.2f] Vehicle %s generated (type=%s, route=%s)%n", - currentTime, vehicle.getId(), vehicle.getType(), vehicle.getRoute()); - + currentTime, vehicle.getId(), vehicle.getType(), vehicle.getRoute()); + // Send generation count to dashboard sendGenerationStatsToDashboard(); - + if (vehicle.getRoute().isEmpty()) { System.err.println("ERROR: Vehicle " + vehicle.getId() + " has empty route!"); return; } - + String entryIntersection = vehicle.getRoute().get(0); sendVehicleToIntersection(vehicle, entryIntersection); } - + private void sendVehicleToIntersection(Vehicle vehicle, String intersectionId) { SocketClient client = intersectionClients.get(intersectionId); - + if (client == null || !client.isConnected()) { System.err.println("ERROR: No connection to " + intersectionId + " for vehicle " + vehicle.getId()); return; } - + try { Message message = new Message( - MessageType.VEHICLE_SPAWN, - "COORDINATOR", - intersectionId, - vehicle - ); - + MessageType.VEHICLE_SPAWN, + "COORDINATOR", + intersectionId, + vehicle); + client.send(message); System.out.printf("->Sent to %s%n", intersectionId); - + } catch (SerializationException | IOException e) { System.err.println("ERROR: Failed to send vehicle " + vehicle.getId() + " to " + intersectionId); System.err.println("Reason: " + e.getMessage()); } } - + public void shutdown() { System.out.println(); System.out.println("=".repeat(60)); System.out.println("Shutting down coordinator..."); - + for (Map.Entry entry : intersectionClients.entrySet()) { String intersectionId = entry.getKey(); SocketClient client = entry.getValue(); - + try { if (client.isConnected()) { Message personalizedShutdown = new Message( - MessageType.SHUTDOWN, - "COORDINATOR", - intersectionId, - "Simulation complete" - ); + MessageType.SHUTDOWN, + "COORDINATOR", + intersectionId, + "Simulation complete"); client.send(personalizedShutdown); System.out.println("Sent shutdown message to " + intersectionId); } @@ -203,21 +209,21 @@ public class CoordinatorProcess { client.close(); } } - + System.out.println("Coordinator shutdown complete"); System.out.println("=".repeat(60)); } - + public void stop() { System.out.println("\nStop signal received..."); running = false; } - + private void connectToDashboard() { try { String host = config.getDashboardHost(); int port = config.getDashboardPort(); - + System.out.println("Connecting to dashboard at " + host + ":" + port); dashboardClient = new SocketClient("Dashboard", host, port); dashboardClient.connect(); @@ -227,58 +233,55 @@ public class CoordinatorProcess { System.err.println("Coordinator will continue without dashboard connection\n"); } } - + private void sendGenerationStatsToDashboard() { if (dashboardClient == null || !dashboardClient.isConnected()) { return; } - + try { // Create stats payload with vehicle generation count StatsUpdatePayload payload = new StatsUpdatePayload(); payload.setTotalVehiclesGenerated(vehicleCounter); - + Message message = new Message( - MessageType.STATS_UPDATE, - "COORDINATOR", - "Dashboard", - payload - ); - + MessageType.STATS_UPDATE, + "COORDINATOR", + "Dashboard", + payload); + dashboardClient.send(message); - } catch (Exception e) { //This is fine - can add IOException if need be + } catch (Exception e) { // This is fine - can add IOException if need be // Don't crash if dashboard update fails System.err.println("Failed to send stats to dashboard: " + e.getMessage()); } } - + private void sendSimulationStartTime() { long startTimeMillis = System.currentTimeMillis(); - + // Send to all intersections for (Map.Entry entry : intersectionClients.entrySet()) { try { Message message = new Message( - MessageType.SIMULATION_START, - "COORDINATOR", - entry.getKey(), - startTimeMillis - ); + MessageType.SIMULATION_START, + "COORDINATOR", + entry.getKey(), + startTimeMillis); entry.getValue().send(message); } catch (Exception e) { // Same thing here System.err.println("Failed to send start time to " + entry.getKey() + ": " + e.getMessage()); } } - + // Send to dashboard if (dashboardClient != null && dashboardClient.isConnected()) { try { Message message = new Message( - MessageType.SIMULATION_START, - "COORDINATOR", - "Dashboard", - startTimeMillis - ); + MessageType.SIMULATION_START, + "COORDINATOR", + "Dashboard", + startTimeMillis); dashboardClient.send(message); } catch (Exception e) { // And here // Don't crash diff --git a/main/src/main/java/sd/dashboard/DashboardUI.java b/main/src/main/java/sd/dashboard/DashboardUI.java index df544d7..cb4cfcf 100644 --- a/main/src/main/java/sd/dashboard/DashboardUI.java +++ b/main/src/main/java/sd/dashboard/DashboardUI.java @@ -12,10 +12,10 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Alert; +import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import javafx.scene.control.TitledPane; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; @@ -23,10 +23,7 @@ import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.Region; import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; import javafx.scene.shape.Circle; -import javafx.scene.text.Font; -import javafx.scene.text.FontWeight; import javafx.stage.Stage; import sd.config.SimulationConfig; import sd.model.VehicleType; @@ -36,10 +33,10 @@ import sd.model.VehicleType; * Provides a graphical interface with auto-updating statistics panels. */ public class DashboardUI extends Application { - + private DashboardServer server; private DashboardStatistics statistics; - + // Global Statistics Labels private Label lblVehiclesGenerated; private Label lblVehiclesCompleted; @@ -47,287 +44,343 @@ public class DashboardUI extends Application { private Label lblAvgSystemTime; private Label lblAvgWaitingTime; private Label lblLastUpdate; - + // Vehicle Type Table private TableView vehicleTypeTable; - + // Intersection Table private TableView intersectionTable; - + // Update scheduler private ScheduledExecutorService updateScheduler; - + @Override public void start(Stage primaryStage) { try { // Initialize server - String configFile = getParameters().getRaw().isEmpty() - ? "src/main/resources/simulation.properties" - : getParameters().getRaw().get(0); - + String configFile = getParameters().getRaw().isEmpty() + ? "src/main/resources/simulation.properties" + : getParameters().getRaw().get(0); + SimulationConfig config = new SimulationConfig(configFile); server = new DashboardServer(config); statistics = server.getStatistics(); - + // Start the dashboard server server.start(); - + // Build UI BorderPane root = new BorderPane(); - root.setStyle("-fx-background-color: #f5f5f5;"); - + root.getStyleClass().add("root"); + // Header VBox header = createHeader(); root.setTop(header); - + // Main content VBox mainContent = createMainContent(); root.setCenter(mainContent); - + // Footer HBox footer = createFooter(); root.setBottom(footer); - + // Create scene - Scene scene = new Scene(root, 1200, 800); + Scene scene = new Scene(root, 1200, 850); + + // Load CSS + String cssUrl = getClass().getResource("/dashboard.css").toExternalForm(); + scene.getStylesheets().add(cssUrl); + primaryStage.setTitle("Traffic Simulation Dashboard - Real-time Statistics"); primaryStage.setScene(scene); primaryStage.show(); - + // Start periodic updates startPeriodicUpdates(); - + // Handle window close primaryStage.setOnCloseRequest(event -> { shutdown(); }); - - } catch (IOException e) { + + } catch (Exception e) { showErrorAlert("Failed to start Dashboard Server", e.getMessage()); + e.printStackTrace(); Platform.exit(); } } - + private VBox createHeader() { VBox header = new VBox(10); - header.setPadding(new Insets(20)); - header.setStyle("-fx-background-color: linear-gradient(to right, #2c3e50, #3498db);"); - - Label title = new Label("DISTRIBUTED TRAFFIC SIMULATION DASHBOARD"); - title.setFont(Font.font("Arial", FontWeight.BOLD, 28)); - title.setTextFill(Color.WHITE); - - Label subtitle = new Label("Real-time Statistics and Monitoring"); - subtitle.setFont(Font.font("Arial", FontWeight.NORMAL, 16)); - subtitle.setTextFill(Color.web("#ecf0f1")); - - header.getChildren().addAll(title, subtitle); + header.getStyleClass().add("header"); header.setAlignment(Pos.CENTER); - + + Label title = new Label("DISTRIBUTED TRAFFIC SIMULATION DASHBOARD"); + title.getStyleClass().add("header-title"); + + Label subtitle = new Label("Real-time Statistics and Monitoring"); + subtitle.getStyleClass().add("header-subtitle"); + + // Control Buttons + HBox controls = new HBox(15); + controls.setAlignment(Pos.CENTER); + + Button btnStart = new Button("START SIMULATION"); + btnStart.getStyleClass().add("button-start"); + + Button btnStop = new Button("STOP SIMULATION"); + btnStop.getStyleClass().add("button-stop"); + btnStop.setDisable(true); + + SimulationProcessManager processManager = new SimulationProcessManager(); + + btnStart.setOnAction(e -> { + try { + processManager.startSimulation(); + btnStart.setDisable(true); + btnStop.setDisable(false); + } catch (IOException ex) { + showErrorAlert("Start Failed", "Could not start simulation processes: " + ex.getMessage()); + } + }); + + btnStop.setOnAction(e -> { + processManager.stopSimulation(); + btnStart.setDisable(false); + btnStop.setDisable(true); + }); + + controls.getChildren().addAll(btnStart, btnStop); + + header.getChildren().addAll(title, subtitle, controls); + return header; } - + private VBox createMainContent() { - VBox mainContent = new VBox(15); + VBox mainContent = new VBox(20); mainContent.setPadding(new Insets(20)); - + // Global Statistics Panel - TitledPane globalStatsPane = createGlobalStatisticsPanel(); - + VBox globalStatsCard = createGlobalStatisticsPanel(); + + // Tables Container + HBox tablesContainer = new HBox(20); + tablesContainer.setAlignment(Pos.TOP_CENTER); + // Vehicle Type Statistics Panel - TitledPane vehicleTypePane = createVehicleTypePanel(); - + VBox vehicleTypeCard = createVehicleTypePanel(); + HBox.setHgrow(vehicleTypeCard, Priority.ALWAYS); + // Intersection Statistics Panel - TitledPane intersectionPane = createIntersectionPanel(); - - mainContent.getChildren().addAll(globalStatsPane, vehicleTypePane, intersectionPane); - + VBox intersectionCard = createIntersectionPanel(); + HBox.setHgrow(intersectionCard, Priority.ALWAYS); + + tablesContainer.getChildren().addAll(vehicleTypeCard, intersectionCard); + + mainContent.getChildren().addAll(globalStatsCard, tablesContainer); + return mainContent; } - - private TitledPane createGlobalStatisticsPanel() { + + private VBox createGlobalStatisticsPanel() { + VBox card = new VBox(); + card.getStyleClass().add("card"); + + // Card Header + HBox cardHeader = new HBox(); + cardHeader.getStyleClass().add("card-header"); + Label cardTitle = new Label("Global Statistics"); + cardTitle.getStyleClass().add("card-title"); + cardHeader.getChildren().add(cardTitle); + + // Card Content GridPane grid = new GridPane(); - grid.setPadding(new Insets(15)); - grid.setHgap(20); + grid.getStyleClass().add("card-content"); + grid.setHgap(40); grid.setVgap(15); - grid.setStyle("-fx-background-color: white; -fx-border-radius: 5;"); - + grid.setAlignment(Pos.CENTER); + // Initialize labels - lblVehiclesGenerated = createStatLabel("0"); - lblVehiclesCompleted = createStatLabel("0"); - lblVehiclesInTransit = createStatLabel("0"); - lblAvgSystemTime = createStatLabel("0.00 ms"); - lblAvgWaitingTime = createStatLabel("0.00 ms"); - + lblVehiclesGenerated = createStatValueLabel("0"); + lblVehiclesCompleted = createStatValueLabel("0"); + lblVehiclesInTransit = createStatValueLabel("0"); + lblAvgSystemTime = createStatValueLabel("0.00 s"); + lblAvgWaitingTime = createStatValueLabel("0.00 s"); + // Add labels with descriptions - addStatRow(grid, 0, "Total Vehicles Generated:", lblVehiclesGenerated); - addStatRow(grid, 1, "Total Vehicles Completed:", lblVehiclesCompleted); - addStatRow(grid, 2, "Vehicles In Transit:", lblVehiclesInTransit); - addStatRow(grid, 3, "Average System Time:", lblAvgSystemTime); - addStatRow(grid, 4, "Average Waiting Time:", lblAvgWaitingTime); - - TitledPane pane = new TitledPane("Global Statistics", grid); - pane.setCollapsible(false); - pane.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - - return pane; + addStatRow(grid, 0, 0, "Total Vehicles Generated", lblVehiclesGenerated); + addStatRow(grid, 1, 0, "Total Vehicles Completed", lblVehiclesCompleted); + addStatRow(grid, 2, 0, "Vehicles In Transit", lblVehiclesInTransit); + addStatRow(grid, 0, 1, "Average System Time", lblAvgSystemTime); + addStatRow(grid, 1, 1, "Average Waiting Time", lblAvgWaitingTime); + + card.getChildren().addAll(cardHeader, grid); + return card; } - - private TitledPane createVehicleTypePanel() { + + private VBox createVehicleTypePanel() { + VBox card = new VBox(); + card.getStyleClass().add("card"); + + // Card Header + HBox cardHeader = new HBox(); + cardHeader.getStyleClass().add("card-header"); + Label cardTitle = new Label("Vehicle Type Statistics"); + cardTitle.getStyleClass().add("card-title"); + cardHeader.getChildren().add(cardTitle); + + // Table vehicleTypeTable = new TableView<>(); vehicleTypeTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - vehicleTypeTable.setPrefHeight(200); - + vehicleTypeTable.setPrefHeight(300); + TableColumn typeCol = new TableColumn<>("Vehicle Type"); typeCol.setCellValueFactory(new PropertyValueFactory<>("vehicleType")); - typeCol.setPrefWidth(200); - + TableColumn countCol = new TableColumn<>("Count"); countCol.setCellValueFactory(new PropertyValueFactory<>("count")); - countCol.setPrefWidth(150); - + TableColumn avgWaitCol = new TableColumn<>("Avg Wait Time"); avgWaitCol.setCellValueFactory(new PropertyValueFactory<>("avgWaitTime")); - avgWaitCol.setPrefWidth(150); - + vehicleTypeTable.getColumns().addAll(typeCol, countCol, avgWaitCol); - - TitledPane pane = new TitledPane("Vehicle Type Statistics", vehicleTypeTable); - pane.setCollapsible(false); - pane.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - - return pane; + + card.getChildren().addAll(cardHeader, vehicleTypeTable); + return card; } - - private TitledPane createIntersectionPanel() { + + private VBox createIntersectionPanel() { + VBox card = new VBox(); + card.getStyleClass().add("card"); + + // Card Header + HBox cardHeader = new HBox(); + cardHeader.getStyleClass().add("card-header"); + Label cardTitle = new Label("Intersection Statistics"); + cardTitle.getStyleClass().add("card-title"); + cardHeader.getChildren().add(cardTitle); + + // Table intersectionTable = new TableView<>(); intersectionTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - intersectionTable.setPrefHeight(250); - + intersectionTable.setPrefHeight(300); + TableColumn idCol = new TableColumn<>("Intersection ID"); idCol.setCellValueFactory(new PropertyValueFactory<>("intersectionId")); - idCol.setPrefWidth(200); - + TableColumn arrivalsCol = new TableColumn<>("Total Arrivals"); arrivalsCol.setCellValueFactory(new PropertyValueFactory<>("arrivals")); - arrivalsCol.setPrefWidth(150); - + TableColumn departuresCol = new TableColumn<>("Total Departures"); departuresCol.setCellValueFactory(new PropertyValueFactory<>("departures")); - departuresCol.setPrefWidth(150); - + TableColumn queueCol = new TableColumn<>("Current Queue"); queueCol.setCellValueFactory(new PropertyValueFactory<>("queueSize")); - queueCol.setPrefWidth(150); - + intersectionTable.getColumns().addAll(idCol, arrivalsCol, departuresCol, queueCol); - - TitledPane pane = new TitledPane("Intersection Statistics", intersectionTable); - pane.setCollapsible(false); - pane.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - - return pane; + + card.getChildren().addAll(cardHeader, intersectionTable); + return card; } - + private HBox createFooter() { HBox footer = new HBox(10); - footer.setPadding(new Insets(10, 20, 10, 20)); - footer.setStyle("-fx-background-color: #34495e;"); + footer.getStyleClass().add("footer"); footer.setAlignment(Pos.CENTER_LEFT); - + Label statusLabel = new Label("Status:"); - statusLabel.setTextFill(Color.WHITE); - statusLabel.setFont(Font.font("Arial", FontWeight.BOLD, 12)); - + statusLabel.getStyleClass().add("footer-text"); + statusLabel.setStyle("-fx-font-weight: bold;"); + Circle statusIndicator = new Circle(6); - statusIndicator.setFill(Color.LIME); - + statusIndicator.setFill(javafx.scene.paint.Color.LIME); + Label statusText = new Label("Connected and Receiving Data"); - statusText.setTextFill(Color.WHITE); - statusText.setFont(Font.font("Arial", 12)); - + statusText.getStyleClass().add("footer-text"); + lblLastUpdate = new Label("Last Update: --:--:--"); - lblLastUpdate.setTextFill(Color.web("#ecf0f1")); - lblLastUpdate.setFont(Font.font("Arial", 12)); - + lblLastUpdate.getStyleClass().add("footer-text"); + Region spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); - + footer.getChildren().addAll(statusLabel, statusIndicator, statusText, spacer, lblLastUpdate); - + return footer; } - - private Label createStatLabel(String initialValue) { + + private Label createStatValueLabel(String initialValue) { Label label = new Label(initialValue); - label.setFont(Font.font("Arial", FontWeight.BOLD, 20)); - label.setTextFill(Color.web("#2980b9")); + label.getStyleClass().add("stat-value"); return label; } - - private void addStatRow(GridPane grid, int row, String description, Label valueLabel) { + + private void addStatRow(GridPane grid, int row, int colGroup, String description, Label valueLabel) { + VBox container = new VBox(5); + container.setAlignment(Pos.CENTER_LEFT); + Label descLabel = new Label(description); - descLabel.setFont(Font.font("Arial", FontWeight.NORMAL, 14)); - descLabel.setTextFill(Color.web("#34495e")); - - grid.add(descLabel, 0, row); - grid.add(valueLabel, 1, row); + descLabel.getStyleClass().add("stat-label"); + + container.getChildren().addAll(descLabel, valueLabel); + + grid.add(container, colGroup, row); } - + private void startPeriodicUpdates() { updateScheduler = Executors.newSingleThreadScheduledExecutor(); updateScheduler.scheduleAtFixedRate(() -> { Platform.runLater(this::updateUI); }, 0, 5, TimeUnit.SECONDS); } - + private void updateUI() { // Update global statistics lblVehiclesGenerated.setText(String.valueOf(statistics.getTotalVehiclesGenerated())); lblVehiclesCompleted.setText(String.valueOf(statistics.getTotalVehiclesCompleted())); lblVehiclesInTransit.setText(String.valueOf( - statistics.getTotalVehiclesGenerated() - statistics.getTotalVehiclesCompleted())); - lblAvgSystemTime.setText(String.format("%.2f ms", statistics.getAverageSystemTime())); - lblAvgWaitingTime.setText(String.format("%.2f ms", statistics.getAverageWaitingTime())); + statistics.getTotalVehiclesGenerated() - statistics.getTotalVehiclesCompleted())); + lblAvgSystemTime.setText(String.format("%.2f s", statistics.getAverageSystemTime() / 1000.0)); + lblAvgWaitingTime.setText(String.format("%.2f s", statistics.getAverageWaitingTime() / 1000.0)); lblLastUpdate.setText(String.format("Last Update: %tT", statistics.getLastUpdateTime())); - + // Update vehicle type table vehicleTypeTable.getItems().clear(); for (VehicleType type : VehicleType.values()) { int count = statistics.getVehicleTypeCount(type); double avgWait = statistics.getAverageWaitingTimeByType(type); vehicleTypeTable.getItems().add(new VehicleTypeRow( - type.toString(), count, String.format("%.2f ms", avgWait))); + type.toString(), count, String.format("%.2f s", avgWait / 1000.0))); } - + // Update intersection table intersectionTable.getItems().clear(); - Map intersectionStats = - statistics.getAllIntersectionStats(); + Map intersectionStats = statistics.getAllIntersectionStats(); for (DashboardStatistics.IntersectionStats stats : intersectionStats.values()) { intersectionTable.getItems().add(new IntersectionRow( - stats.getIntersectionId(), - stats.getTotalArrivals(), - stats.getTotalDepartures(), - stats.getCurrentQueueSize() - )); + stats.getIntersectionId(), + stats.getTotalArrivals(), + stats.getTotalDepartures(), + stats.getCurrentQueueSize())); } } - + private void shutdown() { System.out.println("Shutting down Dashboard UI..."); - + if (updateScheduler != null && !updateScheduler.isShutdown()) { updateScheduler.shutdownNow(); } - + if (server != null) { server.stop(); } - + Platform.exit(); } - + private void showErrorAlert(String title, String message) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle(title); @@ -335,44 +388,63 @@ public class DashboardUI extends Application { alert.setContentText(message); alert.showAndWait(); } - + public static void main(String[] args) { launch(args); } - + // Inner classes for TableView data models public static class VehicleTypeRow { private final String vehicleType; private final int count; private final String avgWaitTime; - + public VehicleTypeRow(String vehicleType, int count, String avgWaitTime) { this.vehicleType = vehicleType; this.count = count; 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; + } } - + public static class IntersectionRow { private final String intersectionId; private final int arrivals; private final int departures; private final int queueSize; - + public IntersectionRow(String intersectionId, int arrivals, int departures, int queueSize) { this.intersectionId = intersectionId; this.arrivals = arrivals; this.departures = departures; 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; + } } } diff --git a/main/src/main/java/sd/dashboard/SimulationProcessManager.java b/main/src/main/java/sd/dashboard/SimulationProcessManager.java new file mode 100644 index 0000000..a1818f3 --- /dev/null +++ b/main/src/main/java/sd/dashboard/SimulationProcessManager.java @@ -0,0 +1,112 @@ +package sd.dashboard; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Manages the lifecycle of simulation processes (Intersections, Exit Node, + * Coordinator). + * Allows starting and stopping the distributed simulation from within the Java + * application. + */ +public class SimulationProcessManager { + + private final List runningProcesses; + private final String classpath; + + public SimulationProcessManager() { + this.runningProcesses = new ArrayList<>(); + this.classpath = System.getProperty("java.class.path"); + } + + /** + * Starts the full simulation: 5 Intersections, 1 Exit Node, and 1 Coordinator. + * + * @throws IOException If a process fails to start. + */ + public void startSimulation() throws IOException { + if (!runningProcesses.isEmpty()) { + stopSimulation(); + } + + System.out.println("Starting simulation processes..."); + + // 1. Start Intersections (Cr1 - Cr5) + String[] intersectionIds = { "Cr1", "Cr2", "Cr3", "Cr4", "Cr5" }; + for (String id : intersectionIds) { + startProcess("sd.IntersectionProcess", id); + } + + // 2. Start Exit Node + startProcess("sd.ExitNodeProcess", null); + + // 3. Start Coordinator (Wait a bit for others to initialize) + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + startProcess("sd.coordinator.CoordinatorProcess", null); + + System.out.println("All simulation processes started."); + } + + /** + * Stops all running simulation processes. + */ + public void stopSimulation() { + System.out.println("Stopping simulation processes..."); + + for (Process process : runningProcesses) { + if (process.isAlive()) { + process.destroy(); // Try graceful termination first + } + } + + // Wait a bit and force kill if necessary + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + + for (Process process : runningProcesses) { + if (process.isAlive()) { + process.destroyForcibly(); + } + } + + runningProcesses.clear(); + System.out.println("All simulation processes stopped."); + } + + /** + * Helper to start a single Java process. + */ + private void startProcess(String className, String arg) throws IOException { + String javaBin = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; + + ProcessBuilder builder; + if (arg != null) { + builder = new ProcessBuilder(javaBin, "-cp", classpath, className, arg); + } else { + builder = new ProcessBuilder(javaBin, "-cp", classpath, className); + } + + // this is a linux thing - not sure about windows + String logName = className.substring(className.lastIndexOf('.') + 1) + (arg != null ? "-" + arg : "") + ".log"; + File logFile = new File("/tmp/" + logName); + builder.redirectOutput(logFile); + builder.redirectError(logFile); + + Process process = builder.start(); + runningProcesses.add(process); + System.out.println("Started " + className + (arg != null ? " " + arg : "")); + } + + public boolean isSimulationRunning() { + return !runningProcesses.isEmpty() && runningProcesses.stream().anyMatch(Process::isAlive); + } +} diff --git a/main/src/main/java/sd/protocol/SocketConnection.java b/main/src/main/java/sd/protocol/SocketConnection.java index 12feecb..c65680b 100644 --- a/main/src/main/java/sd/protocol/SocketConnection.java +++ b/main/src/main/java/sd/protocol/SocketConnection.java @@ -4,7 +4,6 @@ import java.io.Closeable; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; - import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; @@ -127,7 +126,7 @@ public class SocketConnection implements Closeable { * @param message The "envelope" (which contains the Vehicle) to be sent. * @throws IOException If writing to the stream fails or socket is not connected. */ - public void sendMessage(MessageProtocol message) throws IOException { + public synchronized void sendMessage(MessageProtocol message) throws IOException { if (socket == null || !socket.isConnected()) { throw new IOException("Socket is not connected"); } diff --git a/main/src/main/resources/dashboard.css b/main/src/main/resources/dashboard.css new file mode 100644 index 0000000..5cd7e57 --- /dev/null +++ b/main/src/main/resources/dashboard.css @@ -0,0 +1,142 @@ +/* Global Styles */ +.root { + -fx-background-color: #f4f7f6; + -fx-font-family: 'Segoe UI', sans-serif; +} + +/* Header */ +.header { + -fx-background-color: linear-gradient(to right, #2c3e50, #4ca1af); + -fx-padding: 20; + -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 10, 0, 0, 5); +} + +.header-title { + -fx-font-size: 28px; + -fx-font-weight: bold; + -fx-text-fill: white; +} + +.header-subtitle { + -fx-font-size: 16px; + -fx-text-fill: #ecf0f1; +} + +/* Buttons */ +.button-start { + -fx-background-color: #2ecc71; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-padding: 10 20; + -fx-background-radius: 5; + -fx-cursor: hand; + -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 5, 0, 0, 2); +} + +.button-start:hover { + -fx-background-color: #27ae60; +} + +.button-start:disabled { + -fx-background-color: #95a5a6; + -fx-opacity: 0.7; +} + +.button-stop { + -fx-background-color: #e74c3c; + -fx-text-fill: white; + -fx-font-weight: bold; + -fx-padding: 10 20; + -fx-background-radius: 5; + -fx-cursor: hand; + -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 5, 0, 0, 2); +} + +.button-stop:hover { + -fx-background-color: #c0392b; +} + +.button-stop:disabled { + -fx-background-color: #95a5a6; + -fx-opacity: 0.7; +} + +/* Cards / Panels */ +.card { + -fx-background-color: white; + -fx-background-radius: 8; + -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.05), 10, 0, 0, 2); + -fx-padding: 0; +} + +.card-header { + -fx-background-color: #ecf0f1; + -fx-background-radius: 8 8 0 0; + -fx-padding: 10 15; + -fx-border-color: #bdc3c7; + -fx-border-width: 0 0 1 0; +} + +.card-title { + -fx-font-size: 16px; + -fx-font-weight: bold; + -fx-text-fill: #2c3e50; +} + +.card-content { + -fx-padding: 15; +} + +/* Statistics Grid */ +.stat-label { + -fx-font-size: 14px; + -fx-text-fill: #7f8c8d; +} + +.stat-value { + -fx-font-size: 20px; + -fx-font-weight: bold; + -fx-text-fill: #2980b9; +} + +/* Tables */ +.table-view { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.table-view .column-header-background { + -fx-background-color: #ecf0f1; + -fx-border-color: #bdc3c7; + -fx-border-width: 0 0 1 0; +} + +.table-view .column-header .label { + -fx-text-fill: #2c3e50; + -fx-font-weight: bold; +} + +.table-row-cell { + -fx-background-color: white; + -fx-border-color: transparent; +} + +.table-row-cell:odd { + -fx-background-color: #f9f9f9; +} + +.table-row-cell:selected { + -fx-background-color: #3498db; + -fx-text-fill: white; +} + +/* Footer */ +.footer { + -fx-background-color: #34495e; + -fx-padding: 10 20; +} + +.footer-text { + -fx-text-fill: #ecf0f1; + -fx-font-size: 12px; +} diff --git a/main/src/test/java/sd/TrafficLightCoordinationTest.java b/main/src/test/java/sd/TrafficLightCoordinationTest.java index 347864d..80d5f09 100644 --- a/main/src/test/java/sd/TrafficLightCoordinationTest.java +++ b/main/src/test/java/sd/TrafficLightCoordinationTest.java @@ -44,7 +44,7 @@ public class TrafficLightCoordinationTest { @Test public void testOnlyOneGreenLightAtATime() throws InterruptedException { System.out.println("\n=== Testing Traffic Light Mutual Exclusion ==="); - + // Start the intersection Thread intersectionThread = new Thread(() -> { try { @@ -59,55 +59,55 @@ public class TrafficLightCoordinationTest { AtomicInteger maxGreenSimultaneously = new AtomicInteger(0); AtomicInteger violationCount = new AtomicInteger(0); List violations = new ArrayList<>(); - + // Monitor for 10 seconds long endTime = System.currentTimeMillis() + 10000; - + while (System.currentTimeMillis() < endTime) { int greenCount = 0; StringBuilder currentState = new StringBuilder("States: "); - + for (TrafficLight light : intersectionProcess.getIntersection().getTrafficLights()) { TrafficLightState state = light.getState(); currentState.append(light.getDirection()).append("=").append(state).append(" "); - + if (state == TrafficLightState.GREEN) { greenCount++; } } - + // Update maximum simultaneous green lights if (greenCount > maxGreenSimultaneously.get()) { maxGreenSimultaneously.set(greenCount); } - + // Check for violations (more than one green) if (greenCount > 1) { violationCount.incrementAndGet(); - String violation = String.format("[VIOLATION] %d lights GREEN simultaneously: %s", - greenCount, currentState.toString()); + String violation = String.format("[VIOLATION] %d lights GREEN simultaneously: %s", + greenCount, currentState.toString()); violations.add(violation); System.err.println(violation); } - + Thread.sleep(50); // Check every 50ms } - + System.out.println("\n=== Test Results ==="); System.out.println("Maximum simultaneous GREEN lights: " + maxGreenSimultaneously.get()); System.out.println("Total violations detected: " + violationCount.get()); - + if (!violations.isEmpty()) { System.err.println("\nViolation details:"); violations.forEach(System.err::println); } - + // Assert that we never had more than one green light - assertEquals(0, violationCount.get(), - "Traffic light coordination violated! Multiple lights were GREEN simultaneously."); - assertTrue(maxGreenSimultaneously.get() <= 1, - "At most ONE light should be GREEN at any time. Found: " + maxGreenSimultaneously.get()); - + assertEquals(0, violationCount.get(), + "Traffic light coordination violated! Multiple lights were GREEN simultaneously."); + assertTrue(maxGreenSimultaneously.get() <= 1, + "At most ONE light should be GREEN at any time. Found: " + maxGreenSimultaneously.get()); + System.out.println("\nTraffic light coordination working correctly!"); } @@ -118,7 +118,7 @@ public class TrafficLightCoordinationTest { @Test public void testAllLightsGetGreenTime() throws InterruptedException { System.out.println("\n=== Testing Traffic Light Fairness ==="); - + // Start the intersection Thread intersectionThread = new Thread(() -> { try { @@ -132,10 +132,10 @@ public class TrafficLightCoordinationTest { // Track which lights have been green List lights = intersectionProcess.getIntersection().getTrafficLights(); boolean[] hasBeenGreen = new boolean[lights.size()]; - - // Monitor for 15 seconds (enough time for all lights to cycle) - long endTime = System.currentTimeMillis() + 15000; - + + // Monitor for 60 seconds (enough time for all lights to cycle: 18+18+12 = 48s) + long endTime = System.currentTimeMillis() + 60000; + while (System.currentTimeMillis() < endTime) { for (int i = 0; i < lights.size(); i++) { if (lights.get(i).getState() == TrafficLightState.GREEN) { @@ -145,16 +145,17 @@ public class TrafficLightCoordinationTest { } Thread.sleep(100); } - + // Check if all lights got green time int greenCount = 0; System.out.println("\n=== Fairness Results ==="); for (int i = 0; i < lights.size(); i++) { String status = hasBeenGreen[i] ? "✓ YES" : "✗ NO"; System.out.println(lights.get(i).getDirection() + " got GREEN time: " + status); - if (hasBeenGreen[i]) greenCount++; + if (hasBeenGreen[i]) + greenCount++; } - + assertTrue(greenCount > 0, "At least one light should have been GREEN during the test"); System.out.println("\n" + greenCount + "/" + lights.size() + " lights were GREEN during test period"); } @@ -165,7 +166,7 @@ public class TrafficLightCoordinationTest { @Test public void testStateTransitionsAreConsistent() throws InterruptedException { System.out.println("\n=== Testing State Transition Consistency ==="); - + Thread intersectionThread = new Thread(() -> { try { intersectionProcess.start(); @@ -177,29 +178,29 @@ public class TrafficLightCoordinationTest { List lights = intersectionProcess.getIntersection().getTrafficLights(); TrafficLightState[] previousStates = new TrafficLightState[lights.size()]; - + // Initialize previous states for (int i = 0; i < lights.size(); i++) { previousStates[i] = lights.get(i).getState(); } - + int transitionCount = 0; long endTime = System.currentTimeMillis() + 8000; - + while (System.currentTimeMillis() < endTime) { for (int i = 0; i < lights.size(); i++) { TrafficLightState currentState = lights.get(i).getState(); - + if (currentState != previousStates[i]) { transitionCount++; - System.out.println(lights.get(i).getDirection() + " transitioned: " + - previousStates[i] + " → " + currentState); + System.out.println(lights.get(i).getDirection() + " transitioned: " + + previousStates[i] + " → " + currentState); previousStates[i] = currentState; } } Thread.sleep(100); } - + System.out.println("\nTotal state transitions observed: " + transitionCount); assertTrue(transitionCount > 0, "There should be state transitions during the test period"); } diff --git a/main/testing.txt b/main/testing.txt new file mode 100644 index 0000000..6636918 --- /dev/null +++ b/main/testing.txt @@ -0,0 +1,1055 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] ------------------------------< sd:main >------------------------------- +[INFO] Building main 1.0-SNAPSHOT +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[WARNING] 6 problems were encountered while building the effective model for org.openjfx:javafx-controls:jar:17.0.2 during dependency collection step for project (use -X to see details) +[INFO] +[INFO] --- resources:3.3.1:resources (default-resources) @ main --- +[INFO] Copying 2 resources from src/main/resources to target/classes +[INFO] +[INFO] --- compiler:3.13.0:compile (default-compile) @ main --- +[INFO] Nothing to compile - all classes are up to date. +[INFO] +[INFO] --- resources:3.3.1:testResources (default-testResources) @ main --- +[INFO] skip non existing resourceDirectory /home/leo/uni/SD/Trabalho-Pratico-SD/main/src/test/resources +[INFO] +[INFO] --- compiler:3.13.0:testCompile (default-testCompile) @ main --- +[INFO] Nothing to compile - all classes are up to date. +[INFO] +[INFO] --- surefire:3.2.5:test (default-test) @ main --- +[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running sd.coordinator.CoordinatorIntegrationTest +Mock Cr1 listening on port 9001 +Connected to Cr1 at localhost:9001 +Mock Cr1 received: VEHICLE_SPAWN +Mock Cr1 stopped +Mock Cr1 listening on port 9001 +Connected to Cr1 at localhost:9001 +Mock Cr1 stopped +Mock Cr1 listening on port 9001 +Connected to Cr1 at localhost:9001 +Mock Cr1 received: VEHICLE_SPAWN +Mock Cr1 stopped +Mock Cr1 listening on port 9001 +Mock Cr2 listening on port 9002 +Mock Cr3 listening on port 9003 +Connected to Cr1 at localhost:9001 +Connected to Cr2 at localhost:9002 +Connected to Cr3 at localhost:9003 +Mock Cr1 received: SHUTDOWN +Mock Cr2 received: SHUTDOWN +Mock Cr3 received: SHUTDOWN +Mock Cr1 stopped +Mock Cr2 stopped +Mock Cr3 stopped +[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.265 s -- in sd.coordinator.CoordinatorIntegrationTest +[INFO] Running sd.coordinator.CoordinatorProcessTest +Coordinator initialized with configuration: + - Simulation duration: 60.0s + - Arrival model: POISSON + - Arrival rate: 0.5 vehicles/s +[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.052 s -- in sd.coordinator.CoordinatorProcessTest +[INFO] Running sd.dashboard.DashboardTest +[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.020 s -- in sd.dashboard.DashboardTest +[INFO] Running sd.serialization.SerializationTest +JSON Vehicle: +{"id":"V001","type":"LIGHT","entryTime":10.5,"route":["Cr1","Cr2","Cr5","S"],"currentRouteIndex":0,"totalWaitingTime":0.0,"totalCrossingTime":0.0} + +JSON Message: +{"messageId":"2ee10daa-34c4-4629-9613-bfc4fbd03e46","type":"VEHICLE_TRANSFER","senderId":"Cr1","destinationId":"Cr2","payload":{"id":"V001","type":"LIGHT","entryTime":10.5,"route":["Cr1","Cr2","Cr5","S"],"currentRouteIndex":0,"totalWaitingTime":0.0,"totalCrossingTime":0.0},"timestamp":1763852220055} +[INFO] Tests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.019 s -- in sd.serialization.SerializationTest +[INFO] Running sd.ExitNodeProcessTest +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... +Exit node started on port 19001 +Waiting for vehicles...\n + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... +Exit node started on port 19001 +Waiting for vehicles...\n + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... +Exit node started on port 19001 +Waiting for vehicles...\n + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... +Exit node started on port 19001 +Waiting for vehicles...\n + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +New connection accepted from 127.0.0.1 +[Exit] Connection closed from 127.0.0.1 +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... +Exit node started on port 19001 +Waiting for vehicles...\n +New connection accepted from 127.0.0.1 +[Exit] Waiting for message from 127.0.0.1 +New connection accepted from 127.0.0.1 +[Exit] Waiting for message from 127.0.0.1 +New connection accepted from 127.0.0.1 +[Exit] Waiting for message from 127.0.0.1 +[Exit] Waiting for message from 127.0.0.1 + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: [Exit] Connection closed from 127.0.0.1 +0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Connection closed from 127.0.0.1 +[Exit] Connection closed from 127.0.0.1 +[Exit] Shutdown complete. +============================================================ +Exit node initialized + - Exit port: 19001 + - Dashboard: localhost:19000 +Connecting to dashboard... + +[Exit] Shutting down... + +=== EXIT NODE STATISTICS === +Total Vehicles Completed: 0 + +VEHICLE TYPE DISTRIBUTION: +[Exit] Shutdown complete. +============================================================ +[INFO] Tests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.273 s -- in sd.ExitNodeProcessTest +[INFO] Running sd.TrafficLightCoordinationTest +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 18.0s, Red: 30.0s) + Created traffic light: East (Green: 18.0s, Red: 30.0s) + Created traffic light: South (Green: 12.0s, Red: 36.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:9000... +[Cr2] Initialization complete. + +=== Testing Traffic Light Fairness === + +[Cr2] Server started on port 8002 + +[Cr2] Starting traffic light threads... + Started thread for: West + Started thread for: South +[Cr2-West] Traffic light thread started. + Started thread for: East +[Cr2-South] Traffic light thread started. +[Cr2-West] State: GREEN +[Cr2-East] Traffic light thread started. +[Cr2] Waiting for incoming connections... + +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN +✓ West has been GREEN + +=== Fairness Results === +West got GREEN time: ✓ YES +South got GREEN time: ✗ NO +East got GREEN time: ✗ NO + +1/3 lights were GREEN during test period + +[Cr2] Shutting down... +[Cr2-South] State: GREEN +[Cr2-East] State: GREEN +[Cr2-West] Traffic light thread interrupted. +[Cr2-South] Traffic light thread stopped. +[Cr2-East] Traffic light thread stopped. +[Cr2-West] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 18.0s, Red: 30.0s) + Created traffic light: East (Green: 18.0s, Red: 30.0s) + Created traffic light: South (Green: 12.0s, Red: 36.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:9000... +[Cr2] Initialization complete. + +=== Testing Traffic Light Mutual Exclusion === + +[Cr2] Server started on port 8002 + +[Cr2] Starting traffic light threads... + Started thread for: West + Started thread for: South +[Cr2-West] Traffic light thread started. +[Cr2-West] State: GREEN + Started thread for: East +[Cr2-South] Traffic light thread started. +[Cr2] Waiting for incoming connections... + +[Cr2-East] Traffic light thread started. + +=== Test Results === +Maximum simultaneous GREEN lights: 1 +Total violations detected: 0 + +Traffic light coordination working correctly! + +[Cr2] Shutting down... +[Cr2-West] Traffic light thread interrupted. +[Cr2-South] State: GREEN +[Cr2-West] Traffic light thread stopped. +[Cr2-East] State: GREEN +[Cr2-South] Traffic light thread stopped. +[Cr2-East] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 18.0s, Red: 30.0s) + Created traffic light: East (Green: 18.0s, Red: 30.0s) + Created traffic light: South (Green: 12.0s, Red: 36.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:9000... +[Cr2] Initialization complete. + +=== Testing State Transition Consistency === + +[Cr2] Server started on port 8002 + +[Cr2] Starting traffic light threads... + Started thread for: West + Started thread for: South +[Cr2-South] Traffic light thread started. +[Cr2-South] State: GREEN +[Cr2-West] Traffic light thread started. + Started thread for: East +[Cr2] Waiting for incoming connections... + +[Cr2-East] Traffic light thread started. +South transitioned: RED → GREEN + +Total state transitions observed: 1 + +[Cr2] Shutting down... +[Cr2-South] Traffic light thread interrupted. +[Cr2-South] Traffic light thread stopped. +[Cr2-West] State: GREEN +[Cr2-West] Traffic light thread stopped. +[Cr2-East] State: GREEN +[Cr2-East] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 33.14 s -- in sd.TrafficLightCoordinationTest +[INFO] Running IntersectionProcessTest +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + Started thread for: South + Started thread for: East +[Cr1-South] Traffic light thread started. +[Cr1] Waiting for incoming connections... + +[Cr1-South] State: GREEN +[Cr1-East] Traffic light thread started. + +[Cr1] Shutting down... +[Cr1] New connection accepted from 127.0.0.1 +[Cr1-South] Traffic light thread interrupted. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1-South] Traffic light thread stopped. +[Cr1] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + Started thread for: South + Started thread for: East +[Cr1-South] Traffic light thread started. +[Cr1] Waiting for incoming connections... + +[Cr1-South] State: GREEN +[Cr1-East] Traffic light thread started. + +[Cr1] Shutting down... +[Cr1-South] Traffic light thread interrupted. +[Cr1-South] Traffic light thread stopped. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:18100... +[Cr2] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + +[Cr2] Server started on port 18002 + +[Cr2] Starting traffic light threads... + Started thread for: South + Started thread for: West +[Cr1-South] Traffic light thread started. +[Cr1-South] State: GREEN + Started thread for: East +[Cr1] Waiting for incoming connections... + +[Cr1-East] Traffic light thread started. +[Cr2-West] Traffic light thread started. + Started thread for: South +[Cr2-West] State: GREEN +[Cr2-South] Traffic light thread started. + Started thread for: East +[Cr2] Waiting for incoming connections... + +[Cr2-East] Traffic light thread started. +[Cr1] New connection accepted from 127.0.0.1 +[Cr1] New connection accepted from 127.0.0.1 + +[Cr1] Shutting down... +[Cr1-South] Traffic light thread interrupted. +[Cr1-South] Traffic light thread stopped. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1] Shutdown complete. +============================================================ + + +[Cr2] Shutting down... +[Cr2-West] Traffic light thread interrupted. +[Cr2-South] State: GREEN +[Cr2-West] Traffic light thread stopped. +[Cr2-South] Traffic light thread stopped. +[Cr2-East] State: GREEN +[Cr2-East] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + Started thread for: South +[Cr1-South] Traffic light thread started. + Started thread for: East +[Cr1-South] State: GREEN +[Cr1] Waiting for incoming connections... + +[Cr1-East] Traffic light thread started. + +[Cr1] Shutting down... +[Cr1-South] Traffic light thread interrupted. +[Cr1-South] Traffic light thread stopped. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr4 +============================================================ + +[Cr4] Initializing intersection... + +[Cr4] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + +[Cr4] Configuring routing... + Route configured: To Cr5 -> Use East + Routing configured. +[Cr4] Connecting to dashboard at localhost:18100... +[Cr4] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:18100... +[Cr2] Initialization complete. + +[Cr2] Server started on port 18002 + +[Cr2] Starting traffic light threads... + Started thread for: West + Started thread for: South +[Cr2-West] Traffic light thread started. + Started thread for: East +[Cr2-West] State: GREEN +[Cr2] Waiting for incoming connections... + +[Cr2-South] Traffic light thread started. +[Cr2-East] Traffic light thread started. +[Cr2] New connection accepted from 127.0.0.1 +[Cr2] New connection accepted from 127.0.0.1 + +[Cr2] Shutting down... +[Cr2-South] State: GREEN +[Cr2-South] Traffic light thread stopped. +[Cr2-West] Traffic light thread interrupted. +[Cr2-West] Traffic light thread stopped. +[Cr2-East] State: GREEN +[Cr2-East] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr5 +============================================================ + +[Cr5] Initializing intersection... + +[Cr5] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + +[Cr5] Configuring routing... + Route configured: To S -> Use East + Routing configured. +[Cr5] Connecting to dashboard at localhost:18100... +[Cr5] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + Started thread for: South + Started thread for: East +[Cr1] Waiting for incoming connections... + +[Cr1-South] Traffic light thread started. +[Cr1-South] State: GREEN +[Cr1-East] Traffic light thread started. + +[Cr1] Shutting down... +[Cr1-South] Traffic light thread interrupted. +[Cr1-South] Traffic light thread stopped. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1] Shutdown complete. +============================================================ + +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr3 +============================================================ + +[Cr3] Initializing intersection... + +[Cr3] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr3] Configuring routing... + Route configured: To Cr2 -> Use West + Route configured: To S -> Use South + Routing configured. +[Cr3] Connecting to dashboard at localhost:18100... +[Cr3] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:18100... +[Cr2] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr3 +============================================================ + +[Cr3] Initializing intersection... + +[Cr3] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr3] Configuring routing... + Route configured: To Cr2 -> Use West + Route configured: To S -> Use South + Routing configured. +[Cr3] Connecting to dashboard at localhost:18100... +[Cr3] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr4 +============================================================ + +[Cr4] Initializing intersection... + +[Cr4] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + +[Cr4] Configuring routing... + Route configured: To Cr5 -> Use East + Routing configured. +[Cr4] Connecting to dashboard at localhost:18100... +[Cr4] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr5 +============================================================ + +[Cr5] Initializing intersection... + +[Cr5] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + +[Cr5] Configuring routing... + Route configured: To S -> Use East + Routing configured. +[Cr5] Connecting to dashboard at localhost:18100... +[Cr5] Initialization complete. +============================================================ +INTERSECTION PROCESS: Cr1 +============================================================ +============================================================ +INTERSECTION PROCESS: Cr2 +============================================================ + +[Cr1] Initializing intersection... + +[Cr1] Creating traffic lights... + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr1] Configuring routing... + Route configured: To Cr2 -> Use East + Route configured: To Cr4 -> Use South + Routing configured. +[Cr1] Connecting to dashboard at localhost:18100... +[Cr1] Initialization complete. + +[Cr2] Initializing intersection... + +[Cr2] Creating traffic lights... + Created traffic light: West (Green: 30.0s, Red: 30.0s) + Created traffic light: East (Green: 30.0s, Red: 30.0s) + Created traffic light: South (Green: 30.0s, Red: 30.0s) + +[Cr2] Configuring routing... + Route configured: To Cr1 -> Use West + Route configured: To Cr3 -> Use East + Route configured: To Cr5 -> Use South + Routing configured. +[Cr2] Connecting to dashboard at localhost:18100... +[Cr2] Initialization complete. + +[Cr1] Server started on port 18001 + +[Cr1] Starting traffic light threads... + +[Cr2] Server started on port 18002 + +[Cr2] Starting traffic light threads... + Started thread for: South + Started thread for: West +[Cr1-South] Traffic light thread started. +[Cr1-South] State: GREEN + Started thread for: East +[Cr1] Waiting for incoming connections... + +[Cr2-West] Traffic light thread started. +[Cr2-West] State: GREEN +[Cr1-East] Traffic light thread started. + Started thread for: South +[Cr2-South] Traffic light thread started. + Started thread for: East +[Cr2] Waiting for incoming connections... + +[Cr2-East] Traffic light thread started. +[Cr1] New connection accepted from 127.0.0.1 +[Cr2] New connection accepted from 127.0.0.1 + +[Cr1] Shutting down... +[Cr1-South] Traffic light thread interrupted. +[Cr1-South] Traffic light thread stopped. +[Cr1-East] State: GREEN +[Cr1-East] Traffic light thread stopped. +[Cr1] New connection accepted from 127.0.0.1 +[Cr2] New connection accepted from 127.0.0.1 +[Cr1] Shutdown complete. +============================================================ + + +[Cr2] Shutting down... +[Cr2-West] Traffic light thread interrupted. +[Cr2-West] Traffic light thread stopped. +[Cr2-South] State: GREEN +[Cr2-South] Traffic light thread stopped. +[Cr2-East] State: GREEN +[Cr2-East] Traffic light thread stopped. +[Cr2] Shutdown complete. +============================================================ + +[INFO] Tests run: 20, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.406 s -- in IntersectionProcessTest +[INFO] Running SimulationTest +[INFO] Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.014 s -- in SimulationTest +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 66, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 44.392 s +[INFO] Finished at: 2025-11-22T22:57:41Z +[INFO] ------------------------------------------------------------------------