slight sim change and engine code fomat

This commit is contained in:
2025-11-22 21:45:16 +00:00
parent 8f97aab836
commit ce7f642246
2 changed files with 196 additions and 181 deletions

View File

@@ -28,49 +28,51 @@ import sd.util.VehicleGenerator;
* - The {@link StatisticsCollector} for tracking metrics. * - The {@link StatisticsCollector} for tracking metrics.
*/ */
public class SimulationEngine { public class SimulationEngine {
/** /**
* Holds all simulation parameters loaded from the properties file. * Holds all simulation parameters loaded from the properties file.
*/ */
private final SimulationConfig config; private final SimulationConfig config;
/** /**
* The core of the discrete event simulation. Events are pulled from this * The core of the discrete event simulation. Events are pulled from this
* queue in order of their timestamp. * queue in order of their timestamp.
*/ */
private final PriorityQueue<Event> eventQueue; private final PriorityQueue<Event> eventQueue;
/** /**
* A map storing all intersections in the simulation, keyed by their ID (e.g., "Cr1"). * A map storing all intersections in the simulation, keyed by their ID (e.g.,
* "Cr1").
*/ */
private final Map<String, Intersection> intersections; private final Map<String, Intersection> intersections;
/** /**
* Responsible for creating new vehicles according to the configured arrival model. * Responsible for creating new vehicles according to the configured arrival
* model.
*/ */
private final VehicleGenerator vehicleGenerator; private final VehicleGenerator vehicleGenerator;
/** /**
* Collects and calculates statistics throughout the simulation. * Collects and calculates statistics throughout the simulation.
*/ */
private final StatisticsCollector statisticsCollector; private final StatisticsCollector statisticsCollector;
/** /**
* The current time in the simulation (in virtual seconds). * The current time in the simulation (in virtual seconds).
* This time advances based on the timestamp of the event being processed. * This time advances based on the timestamp of the event being processed.
*/ */
private double currentTime; private double currentTime;
/** /**
* A simple counter to generate unique IDs for vehicles. * A simple counter to generate unique IDs for vehicles.
*/ */
private int vehicleCounter; private int vehicleCounter;
/** /**
* Constructs a new SimulationEngine. * Constructs a new SimulationEngine.
* *
* @param config The {@link SimulationConfig} object containing all * @param config The {@link SimulationConfig} object containing all
* simulation parameters. * simulation parameters.
*/ */
public SimulationEngine(SimulationConfig config) { public SimulationEngine(SimulationConfig config) {
this.config = config; this.config = config;
@@ -89,19 +91,19 @@ public class SimulationEngine {
* @return The travel time in seconds. * @return The travel time in seconds.
*/ */
private double calculateTravelTime(VehicleType vehicleType) { private double calculateTravelTime(VehicleType vehicleType) {
double baseTime = config.getBaseTravelTime(); double baseTime = config.getBaseTravelTime();
switch (vehicleType) { switch (vehicleType) {
case BIKE: case BIKE:
return baseTime * config.getBikeTravelTimeMultiplier(); return baseTime * config.getBikeTravelTimeMultiplier();
case HEAVY: case HEAVY:
return baseTime * config.getHeavyTravelTimeMultiplier(); return baseTime * config.getHeavyTravelTimeMultiplier();
case LIGHT: case LIGHT:
default: default:
return baseTime; return baseTime;
}
} }
}
/** /**
* Initializes the simulation. This involves: * Initializes the simulation. This involves:
* 1. Creating all {@link Intersection} and {@link TrafficLight} objects. * 1. Creating all {@link Intersection} and {@link TrafficLight} objects.
@@ -111,53 +113,53 @@ public class SimulationEngine {
*/ */
public void initialize() { public void initialize() {
System.out.println("Initializing simulation..."); System.out.println("Initializing simulation...");
setupIntersections(); setupIntersections();
setupRouting(); setupRouting();
// Schedule initial events to "bootstrap" the simulation // Schedule initial events to "bootstrap" the simulation
scheduleTrafficLightEvents(); scheduleTrafficLightEvents();
scheduleNextVehicleGeneration(0.0); scheduleNextVehicleGeneration(0.0);
scheduleStatisticsUpdates(); scheduleStatisticsUpdates();
System.out.println("Simulation initialized with " + intersections.size() + " intersections"); System.out.println("Simulation initialized with " + intersections.size() + " intersections");
} }
/** /**
* Creates all intersections defined in the configuration * Creates all intersections defined in the configuration
* and adds their corresponding traffic lights. * and adds their corresponding traffic lights.
*/ */
private void setupIntersections() { private void setupIntersections() {
String[] intersectionIds = {"Cr1", "Cr2", "Cr3", "Cr4", "Cr5"}; String[] intersectionIds = { "Cr1", "Cr2", "Cr3", "Cr4", "Cr5" };
// Note: "North" is commented out, so it won't be created. // Note: "North" is commented out, so it won't be created.
String[] directions = {/*"North",*/ "South", "East", "West"}; String[] directions = { /* "North", */ "South", "East", "West" };
for (String id : intersectionIds) { for (String id : intersectionIds) {
Intersection intersection = new Intersection(id); Intersection intersection = new Intersection(id);
// Add traffic lights for each configured direction // Add traffic lights for each configured direction
for (String direction : directions) { for (String direction : directions) {
double greenTime = config.getTrafficLightGreenTime(id, direction); double greenTime = config.getTrafficLightGreenTime(id, direction);
double redTime = config.getTrafficLightRedTime(id, direction); double redTime = config.getTrafficLightRedTime(id, direction);
TrafficLight light = new TrafficLight( TrafficLight light = new TrafficLight(
id + "-" + direction, id + "-" + direction,
direction, direction,
greenTime, greenTime,
redTime redTime);
);
intersection.addTrafficLight(light); intersection.addTrafficLight(light);
} }
intersections.put(id, intersection); intersections.put(id, intersection);
} }
} }
/** /**
* Configures how vehicles should be routed between intersections. * Configures how vehicles should be routed between intersections.
* This hardcoded logic defines the "map" of the city. * This hardcoded logic defines the "map" of the city.
* * For example, `intersections.get("Cr1").configureRoute("Cr2", "East");` means * * For example, `intersections.get("Cr1").configureRoute("Cr2", "East");`
* means
* "at intersection Cr1, any vehicle whose *next* destination is Cr2 * "at intersection Cr1, any vehicle whose *next* destination is Cr2
* should be sent to the 'East' traffic light queue." * should be sent to the 'East' traffic light queue."
*/ */
@@ -165,26 +167,26 @@ public class SimulationEngine {
// Cr1 routing // Cr1 routing
intersections.get("Cr1").configureRoute("Cr2", "East"); intersections.get("Cr1").configureRoute("Cr2", "East");
intersections.get("Cr1").configureRoute("Cr4", "South"); intersections.get("Cr1").configureRoute("Cr4", "South");
// Cr2 routing // Cr2 routing
intersections.get("Cr2").configureRoute("Cr1", "West"); intersections.get("Cr2").configureRoute("Cr1", "West");
intersections.get("Cr2").configureRoute("Cr3", "East"); intersections.get("Cr2").configureRoute("Cr3", "East");
intersections.get("Cr2").configureRoute("Cr5", "South"); intersections.get("Cr2").configureRoute("Cr5", "South");
// Cr3 routing // Cr3 routing
intersections.get("Cr3").configureRoute("Cr2", "West"); intersections.get("Cr3").configureRoute("Cr2", "West");
intersections.get("Cr3").configureRoute("S", "South"); // "S" is the exit intersections.get("Cr3").configureRoute("S", "South"); // "S" is the exit
// Cr4 routing // Cr4 routing
//intersections.get("Cr4").configureRoute("Cr1", "North"); // intersections.get("Cr4").configureRoute("Cr1", "North");
intersections.get("Cr4").configureRoute("Cr5", "East"); intersections.get("Cr4").configureRoute("Cr5", "East");
// Cr5 routing // Cr5 routing
//intersections.get("Cr5").configureRoute("Cr2", "North"); // intersections.get("Cr5").configureRoute("Cr2", "North");
//intersections.get("Cr5").configureRoute("Cr4", "West"); // intersections.get("Cr5").configureRoute("Cr4", "West");
intersections.get("Cr5").configureRoute("S", "East"); // "S" is the exit intersections.get("Cr5").configureRoute("S", "East"); // "S" is the exit
} }
/** /**
* Schedules the initial {@link EventType#TRAFFIC_LIGHT_CHANGE} event * Schedules the initial {@link EventType#TRAFFIC_LIGHT_CHANGE} event
* for every traffic light in the simulation. * for every traffic light in the simulation.
@@ -201,38 +203,40 @@ public class SimulationEngine {
} }
} }
} }
/** /**
* Creates and schedules a new {@link EventType#TRAFFIC_LIGHT_CHANGE} event. * Creates and schedules a new {@link EventType#TRAFFIC_LIGHT_CHANGE} event.
* The event is scheduled to occur at {@code currentTime + delay}. * The event is scheduled to occur at {@code currentTime + delay}.
* *
* @param light The {@link TrafficLight} that will change state. * @param light The {@link TrafficLight} that will change state.
* @param intersectionId The ID of the intersection where the light is located. * @param intersectionId The ID of the intersection where the light is located.
* @param delay The time (in seconds) from {@code currentTime} when the change should occur. * @param delay The time (in seconds) from {@code currentTime} when the
* change should occur.
*/ */
private void scheduleTrafficLightChange(TrafficLight light, String intersectionId, double delay) { private void scheduleTrafficLightChange(TrafficLight light, String intersectionId, double delay) {
double changeTime = currentTime + delay; double changeTime = currentTime + delay;
Event event = new Event(changeTime, EventType.TRAFFIC_LIGHT_CHANGE, light, intersectionId); Event event = new Event(changeTime, EventType.TRAFFIC_LIGHT_CHANGE, light, intersectionId);
eventQueue.offer(event); eventQueue.offer(event);
} }
/** /**
* Schedules the next {@link EventType#VEHICLE_GENERATION} event. * Schedules the next {@link EventType#VEHICLE_GENERATION} event.
* The time of the next arrival is determined by the {@link VehicleGenerator}. * The time of the next arrival is determined by the {@link VehicleGenerator}.
* *
* @param baseTime The time from which to calculate the next arrival (usually {@code currentTime}). * @param baseTime The time from which to calculate the next arrival (usually
* {@code currentTime}).
*/ */
private void scheduleNextVehicleGeneration(double baseTime) { private void scheduleNextVehicleGeneration(double baseTime) {
// Get the absolute time for the next arrival. // Get the absolute time for the next arrival.
double nextArrivalTime = vehicleGenerator.getNextArrivalTime(baseTime); double nextArrivalTime = vehicleGenerator.getNextArrivalTime(baseTime);
// Only schedule the event if it's within the simulation's total duration. // Only schedule the event if it's within the simulation's total duration.
if (nextArrivalTime < config.getSimulationDuration()) { if (nextArrivalTime < config.getSimulationDuration()) {
Event event = new Event(nextArrivalTime, EventType.VEHICLE_GENERATION, null, null); Event event = new Event(nextArrivalTime, EventType.VEHICLE_GENERATION, null, null);
eventQueue.offer(event); eventQueue.offer(event);
} }
} }
/** /**
* Schedules all periodic {@link EventType#STATISTICS_UPDATE} events * Schedules all periodic {@link EventType#STATISTICS_UPDATE} events
* for the entire duration of the simulation. * for the entire duration of the simulation.
@@ -240,13 +244,13 @@ public class SimulationEngine {
private void scheduleStatisticsUpdates() { private void scheduleStatisticsUpdates() {
double interval = config.getStatisticsUpdateInterval(); double interval = config.getStatisticsUpdateInterval();
double duration = config.getSimulationDuration(); double duration = config.getSimulationDuration();
for (double time = interval; time < duration; time += interval) { for (double time = interval; time < duration; time += interval) {
Event event = new Event(time, EventType.STATISTICS_UPDATE, null, null); Event event = new Event(time, EventType.STATISTICS_UPDATE, null, null);
eventQueue.offer(event); eventQueue.offer(event);
} }
} }
/** /**
* Runs the main simulation loop. * Runs the main simulation loop.
* The loop continues as long as there are events in the queue and * The loop continues as long as there are events in the queue and
@@ -260,46 +264,47 @@ public class SimulationEngine {
public void run() { public void run() {
System.out.println("Starting simulation..."); System.out.println("Starting simulation...");
double duration = config.getSimulationDuration(); double duration = config.getSimulationDuration();
while (!eventQueue.isEmpty() && currentTime < duration) { while (!eventQueue.isEmpty() && currentTime < duration) {
// Get the next event in chronological order // Get the next event in chronological order
Event event = eventQueue.poll(); Event event = eventQueue.poll();
// Advance simulation time to this event's time // Advance simulation time to this event's time
currentTime = event.getTimestamp(); currentTime = event.getTimestamp();
// Process the event // Process the event
processEvent(event); processEvent(event);
} }
System.out.println("\nSimulation completed at t=" + String.format("%.2f", currentTime) + "s"); System.out.println("\nSimulation completed at t=" + String.format("%.2f", currentTime) + "s");
printFinalStatistics(); printFinalStatistics();
} }
/** /**
* Main event processing logic. * Main event processing logic.
* Delegates the event to the appropriate handler method based on its {@link EventType}. * Delegates the event to the appropriate handler method based on its
* {@link EventType}.
* *
* @param event The {@link Event} to be processed. * @param event The {@link Event} to be processed.
*/ */
private void processEvent(Event event) { private void processEvent(Event event) {
switch (event.getType()) { switch (event.getType()) {
case VEHICLE_GENERATION -> handleVehicleGeneration(); case VEHICLE_GENERATION -> handleVehicleGeneration();
case VEHICLE_ARRIVAL -> handleVehicleArrival(event); case VEHICLE_ARRIVAL -> handleVehicleArrival(event);
case TRAFFIC_LIGHT_CHANGE -> handleTrafficLightChange(event); case TRAFFIC_LIGHT_CHANGE -> handleTrafficLightChange(event);
case CROSSING_START -> handleCrossingStart(event); case CROSSING_START -> handleCrossingStart(event);
case CROSSING_END -> handleCrossingEnd(event); case CROSSING_END -> handleCrossingEnd(event);
case STATISTICS_UPDATE -> handleStatisticsUpdate(); case STATISTICS_UPDATE -> handleStatisticsUpdate();
default -> System.err.println("Unknown event type: " + event.getType()); default -> System.err.println("Unknown event type: " + event.getType());
} }
} }
/** /**
* Handles {@link EventType#VEHICLE_GENERATION}. * Handles {@link EventType#VEHICLE_GENERATION}.
* 1. Creates a new {@link Vehicle} using the {@link #vehicleGenerator}. * 1. Creates a new {@link Vehicle} using the {@link #vehicleGenerator}.
@@ -308,17 +313,18 @@ public class SimulationEngine {
* at its first destination intersection. * at its first destination intersection.
* 4. Schedules the *next* {@link EventType#VEHICLE_GENERATION} event. * 4. Schedules the *next* {@link EventType#VEHICLE_GENERATION} event.
* (Note: This line is commented out in the original, which might be a bug, * (Note: This line is commented out in the original, which might be a bug,
* as it implies only one vehicle is ever generated. It should likely be active.) * as it implies only one vehicle is ever generated. It should likely be
* active.)
*/ */
private void handleVehicleGeneration() { private void handleVehicleGeneration() {
Vehicle vehicle = vehicleGenerator.generateVehicle("V" + (++vehicleCounter), currentTime); Vehicle vehicle = vehicleGenerator.generateVehicle("V" + (++vehicleCounter), currentTime);
System.out.printf("[t=%.2f] Vehicle %s generated (type=%s, route=%s)%n", 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());
// Register with statistics collector // Register with statistics collector
statisticsCollector.recordVehicleGeneration(vehicle, currentTime); statisticsCollector.recordVehicleGeneration(vehicle, currentTime);
// Schedule arrival at first intersection // Schedule arrival at first intersection
String firstIntersection = vehicle.getCurrentDestination(); String firstIntersection = vehicle.getCurrentDestination();
if (firstIntersection != null && !firstIntersection.equals("S")) { if (firstIntersection != null && !firstIntersection.equals("S")) {
@@ -327,13 +333,13 @@ public class SimulationEngine {
Event arrivalEvent = new Event(arrivalTime, EventType.VEHICLE_ARRIVAL, vehicle, firstIntersection); Event arrivalEvent = new Event(arrivalTime, EventType.VEHICLE_ARRIVAL, vehicle, firstIntersection);
eventQueue.offer(arrivalEvent); eventQueue.offer(arrivalEvent);
} }
// Schedule next vehicle generation // Schedule next vehicle generation
// This was commented out in the original file. // This was commented out in the original file.
// For a continuous simulation, it should be enabled: // For a continuous simulation, it should be enabled:
scheduleNextVehicleGeneration(currentTime); scheduleNextVehicleGeneration(currentTime);
} }
/** /**
* Handles {@link EventType#VEHICLE_ARRIVAL} at an intersection. * Handles {@link EventType#VEHICLE_ARRIVAL} at an intersection.
* 1. Records the arrival for statistics. * 1. Records the arrival for statistics.
@@ -344,65 +350,67 @@ public class SimulationEngine {
* current intersection using {@link Intersection#receiveVehicle(Vehicle)}. * current intersection using {@link Intersection#receiveVehicle(Vehicle)}.
* 5. Attempts to process the vehicle immediately if its light is green. * 5. Attempts to process the vehicle immediately if its light is green.
* *
* @param event The arrival event, containing the {@link Vehicle} and intersection ID. * @param event The arrival event, containing the {@link Vehicle} and
* intersection ID.
*/ */
private void handleVehicleArrival(Event event) { private void handleVehicleArrival(Event event) {
Vehicle vehicle = (Vehicle) event.getData(); Vehicle vehicle = (Vehicle) event.getData();
String intersectionId = event.getLocation(); String intersectionId = event.getLocation();
Intersection intersection = intersections.get(intersectionId); Intersection intersection = intersections.get(intersectionId);
if (intersection == null) { if (intersection == null) {
System.err.println("Unknown intersection: " + intersectionId); System.err.println("Unknown intersection: " + intersectionId);
return; return;
} }
System.out.printf("[t=%.2f] Vehicle %s arrived at %s%n", System.out.printf("[t=%.2f] Vehicle %s arrived at %s%n",
currentTime, vehicle.getId(), intersectionId); currentTime, vehicle.getId(), intersectionId);
// Record arrival time (used to calculate waiting time later) // Record arrival time (used to calculate waiting time later)
statisticsCollector.recordVehicleArrival(vehicle, intersectionId, currentTime); statisticsCollector.recordVehicleArrival(vehicle, intersectionId, currentTime);
// Advance the vehicle's route to the *next* stop // Advance the vehicle's route to the *next* stop
// (it has now arrived at its *current* destination) // (it has now arrived at its *current* destination)
boolean hasNext = vehicle.advanceRoute(); boolean hasNext = vehicle.advanceRoute();
if (!hasNext) { if (!hasNext) {
// This was the last stop // This was the last stop
handleVehicleExit(vehicle); handleVehicleExit(vehicle);
return; return;
} }
String nextDestination = vehicle.getCurrentDestination(); String nextDestination = vehicle.getCurrentDestination();
if (nextDestination == null || "S".equals(nextDestination)) { if (nextDestination == null || "S".equals(nextDestination)) {
// Next stop is the exit // Next stop is the exit
handleVehicleExit(vehicle); handleVehicleExit(vehicle);
return; return;
} }
// Add vehicle to the appropriate traffic light queue based on its next destination // Add vehicle to the appropriate traffic light queue based on its next
// destination
intersection.receiveVehicle(vehicle); intersection.receiveVehicle(vehicle);
// Try to process the vehicle immediately if its light is already green // Try to process the vehicle immediately if its light is already green
tryProcessVehicle(vehicle, intersection); tryProcessVehicle(vehicle, intersection);
} }
/** /**
* Checks if a newly arrived vehicle (or a vehicle in a queue * Checks if a newly arrived vehicle (or a vehicle in a queue
* that just turned green) can start crossing. * that just turned green) can start crossing.
* *
* @param vehicle The vehicle to process. * @param vehicle The vehicle to process.
* @param intersection The intersection where the vehicle is. * @param intersection The intersection where the vehicle is.
*/ */
private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) { //FIXME private void tryProcessVehicle(Vehicle vehicle, Intersection intersection) { // FIXME
// Find the direction (and light) this vehicle is queued at // Find the direction (and light) this vehicle is queued at
// This logic is a bit flawed: it just finds the *first* non-empty queue // This logic is a bit flawed: it just finds the *first* non-empty queue
// A better approach would be to get the light from the vehicle's route // A better approach would be to get the light from the vehicle's route
String direction = intersection.getTrafficLights().stream() String direction = intersection.getTrafficLights().stream()
.filter(tl -> tl.getQueueSize() > 0) .filter(tl -> tl.getQueueSize() > 0)
.map(TrafficLight::getDirection) .map(TrafficLight::getDirection)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
if (direction != null) { if (direction != null) {
TrafficLight light = intersection.getTrafficLight(direction); TrafficLight light = intersection.getTrafficLight(direction);
// If the light is green and it's the correct one... // If the light is green and it's the correct one...
@@ -416,26 +424,26 @@ public class SimulationEngine {
} }
} }
} }
/** /**
* Schedules the crossing for a vehicle that has just been dequeued * Schedules the crossing for a vehicle that has just been dequeued
* from a green light. * from a green light.
* 1. Calculates and records the vehicle's waiting time. * 1. Calculates and records the vehicle's waiting time.
* 2. Schedules an immediate {@link EventType#CROSSING_START} event. * 2. Schedules an immediate {@link EventType#CROSSING_START} event.
* *
* @param vehicle The {@link Vehicle} that is crossing. * @param vehicle The {@link Vehicle} that is crossing.
* @param intersection The {@link Intersection} it is crossing. * @param intersection The {@link Intersection} it is crossing.
*/ */
private void scheduleCrossing(Vehicle vehicle, Intersection intersection) { private void scheduleCrossing(Vehicle vehicle, Intersection intersection) {
// Calculate time spent waiting at the red light // Calculate time spent waiting at the red light
double waitTime = currentTime - statisticsCollector.getArrivalTime(vehicle); double waitTime = currentTime - statisticsCollector.getArrivalTime(vehicle);
vehicle.addWaitingTime(waitTime); vehicle.addWaitingTime(waitTime);
// Schedule crossing start event *now* // Schedule crossing start event *now*
Event crossingStart = new Event(currentTime, EventType.CROSSING_START, vehicle, intersection.getId()); Event crossingStart = new Event(currentTime, EventType.CROSSING_START, vehicle, intersection.getId());
processEvent(crossingStart); // Process immediately processEvent(crossingStart); // Process immediately
} }
/** /**
* Handles {@link EventType#CROSSING_START}. * Handles {@link EventType#CROSSING_START}.
* 1. Determines the crossing time based on vehicle type. * 1. Determines the crossing time based on vehicle type.
@@ -447,23 +455,24 @@ public class SimulationEngine {
private void handleCrossingStart(Event event) { private void handleCrossingStart(Event event) {
Vehicle vehicle = (Vehicle) event.getData(); Vehicle vehicle = (Vehicle) event.getData();
String intersectionId = event.getLocation(); String intersectionId = event.getLocation();
double crossingTime = getCrossingTime(vehicle.getType()); double crossingTime = getCrossingTime(vehicle.getType());
System.out.printf("[t=%.2f] Vehicle %s started crossing at %s (duration=%.2fs)%n", System.out.printf("[t=%.2f] Vehicle %s started crossing at %s (duration=%.2fs)%n",
currentTime, vehicle.getId(), intersectionId, crossingTime); currentTime, vehicle.getId(), intersectionId, crossingTime);
// Schedule the *end* of the crossing // Schedule the *end* of the crossing
double endTime = currentTime + crossingTime; double endTime = currentTime + crossingTime;
Event crossingEnd = new Event(endTime, EventType.CROSSING_END, vehicle, intersectionId); Event crossingEnd = new Event(endTime, EventType.CROSSING_END, vehicle, intersectionId);
eventQueue.offer(crossingEnd); eventQueue.offer(crossingEnd);
} }
/** /**
* Handles {@link EventType#CROSSING_END}. * Handles {@link EventType#CROSSING_END}.
* 1. Updates intersection and vehicle statistics. * 1. Updates intersection and vehicle statistics.
* 2. Checks the vehicle's *next* destination. * 2. Checks the vehicle's *next* destination.
* 3. If the next destination is the exit ("S"), call {@link #handleVehicleExit(Vehicle)}. * 3. If the next destination is the exit ("S"), call
* {@link #handleVehicleExit(Vehicle)}.
* 4. Otherwise, schedule a {@link EventType#VEHICLE_ARRIVAL} event at the * 4. Otherwise, schedule a {@link EventType#VEHICLE_ARRIVAL} event at the
* *next* intersection, after some travel time. * *next* intersection, after some travel time.
* *
@@ -472,24 +481,25 @@ public class SimulationEngine {
private void handleCrossingEnd(Event event) { private void handleCrossingEnd(Event event) {
Vehicle vehicle = (Vehicle) event.getData(); Vehicle vehicle = (Vehicle) event.getData();
String intersectionId = event.getLocation(); String intersectionId = event.getLocation();
// Update stats // Update stats
Intersection intersection = intersections.get(intersectionId); Intersection intersection = intersections.get(intersectionId);
if (intersection != null) { if (intersection != null) {
intersection.incrementVehiclesSent(); intersection.incrementVehiclesSent();
} }
double crossingTime = getCrossingTime(vehicle.getType()); double crossingTime = getCrossingTime(vehicle.getType());
vehicle.addCrossingTime(crossingTime); vehicle.addCrossingTime(crossingTime);
System.out.printf("[t=%.2f] Vehicle %s finished crossing at %s%n", System.out.printf("[t=%.2f] Vehicle %s finished crossing at %s%n",
currentTime, vehicle.getId(), intersectionId); currentTime, vehicle.getId(), intersectionId);
// Decide what to do next // Decide what to do next
String nextDest = vehicle.getCurrentDestination(); String nextDest = vehicle.getCurrentDestination();
if (nextDest != null && !nextDest.equals("S")) { if (nextDest != null && !nextDest.equals("S")) {
// Route to the *next* intersection // Route to the *next* intersection
// Travel time varies by vehicle type: tmoto = 0.5 × tcarro, tcaminhão = 4 × tmoto // Travel time varies by vehicle type: tmoto = 0.5 × tcarro, tcaminhão = 4 ×
// tmoto
double travelTime = calculateTravelTime(vehicle.getType()); double travelTime = calculateTravelTime(vehicle.getType());
double arrivalTime = currentTime + travelTime; double arrivalTime = currentTime + travelTime;
Event arrivalEvent = new Event(arrivalTime, EventType.VEHICLE_ARRIVAL, vehicle, nextDest); Event arrivalEvent = new Event(arrivalTime, EventType.VEHICLE_ARRIVAL, vehicle, nextDest);
@@ -499,7 +509,7 @@ public class SimulationEngine {
handleVehicleExit(vehicle); handleVehicleExit(vehicle);
} }
} }
/** /**
* Handles a vehicle exiting the simulation. * Handles a vehicle exiting the simulation.
* Records final statistics for the vehicle. * Records final statistics for the vehicle.
@@ -508,18 +518,19 @@ public class SimulationEngine {
*/ */
private void handleVehicleExit(Vehicle vehicle) { private void handleVehicleExit(Vehicle vehicle) {
System.out.printf("[t=%.2f] Vehicle %s exited the system (wait=%.2fs, travel=%.2fs)%n", System.out.printf("[t=%.2f] Vehicle %s exited the system (wait=%.2fs, travel=%.2fs)%n",
currentTime, vehicle.getId(), currentTime, vehicle.getId(),
vehicle.getTotalWaitingTime(), vehicle.getTotalWaitingTime(),
vehicle.getTotalTravelTime(currentTime)); vehicle.getTotalTravelTime(currentTime));
// Record the exit for final statistics calculation // Record the exit for final statistics calculation
statisticsCollector.recordVehicleExit(vehicle, currentTime); statisticsCollector.recordVehicleExit(vehicle, currentTime);
} }
/** /**
* Handles {@link EventType#TRAFFIC_LIGHT_CHANGE}. * Handles {@link EventType#TRAFFIC_LIGHT_CHANGE}.
* 1. Toggles the light's state (RED to GREEN or GREEN to RED). * 1. Toggles the light's state (RED to GREEN or GREEN to RED).
* 2. If the light just turned GREEN, call {@link #processGreenLight(TrafficLight, Intersection)} * 2. If the light just turned GREEN, call
* {@link #processGreenLight(TrafficLight, Intersection)}
* to process any waiting vehicles. * to process any waiting vehicles.
* 3. Schedules the *next* state change for this light based on its * 3. Schedules the *next* state change for this light based on its
* green/red time duration. * green/red time duration.
@@ -529,17 +540,17 @@ public class SimulationEngine {
private void handleTrafficLightChange(Event event) { private void handleTrafficLightChange(Event event) {
TrafficLight light = (TrafficLight) event.getData(); TrafficLight light = (TrafficLight) event.getData();
String intersectionId = event.getLocation(); String intersectionId = event.getLocation();
// Toggle state // Toggle state
TrafficLightState newState = (light.getState() == TrafficLightState.RED) TrafficLightState newState = (light.getState() == TrafficLightState.RED)
? TrafficLightState.GREEN ? TrafficLightState.GREEN
: TrafficLightState.RED; : TrafficLightState.RED;
light.changeState(newState); light.changeState(newState);
System.out.printf("[t=%.2f] Traffic light %s changed to %s%n", System.out.printf("[t=%.2f] Traffic light %s changed to %s%n",
currentTime, light.getId(), newState); currentTime, light.getId(), newState);
// If changed to GREEN, process waiting vehicles // If changed to GREEN, process waiting vehicles
if (newState == TrafficLightState.GREEN) { if (newState == TrafficLightState.GREEN) {
Intersection intersection = intersections.get(intersectionId); Intersection intersection = intersections.get(intersectionId);
@@ -547,15 +558,15 @@ public class SimulationEngine {
processGreenLight(light, intersection); processGreenLight(light, intersection);
} }
} }
// Schedule the *next* state change for this same light // Schedule the *next* state change for this same light
double nextChangeDelay = (newState == TrafficLightState.GREEN) double nextChangeDelay = (newState == TrafficLightState.GREEN)
? light.getGreenTime() ? light.getGreenTime()
: light.getRedTime(); : light.getRedTime();
scheduleTrafficLightChange(light, intersectionId, nextChangeDelay); scheduleTrafficLightChange(light, intersectionId, nextChangeDelay);
} }
/** /**
* Processes vehicles when a light turns green. * Processes vehicles when a light turns green.
* It loops as long as the light is green and there are vehicles in the queue, * It loops as long as the light is green and there are vehicles in the queue,
@@ -566,7 +577,7 @@ public class SimulationEngine {
* processes the entire queue "instantaneously" at the moment * processes the entire queue "instantaneously" at the moment
* the light turns green. * the light turns green.
* *
* @param light The {@link TrafficLight} that just turned green. * @param light The {@link TrafficLight} that just turned green.
* @param intersection The {@link Intersection} where the light is. * @param intersection The {@link Intersection} where the light is.
*/ */
private void processGreenLight(TrafficLight light, Intersection intersection) { private void processGreenLight(TrafficLight light, Intersection intersection) {
@@ -579,7 +590,7 @@ public class SimulationEngine {
} }
} }
} }
/** /**
* Handles {@link EventType#STATISTICS_UPDATE}. * Handles {@link EventType#STATISTICS_UPDATE}.
* Calls the {@link StatisticsCollector} to print the current * Calls the {@link StatisticsCollector} to print the current
@@ -590,9 +601,10 @@ public class SimulationEngine {
statisticsCollector.printCurrentStatistics(intersections, currentTime); statisticsCollector.printCurrentStatistics(intersections, currentTime);
System.out.println(); System.out.println();
} }
/** /**
* Utility method to get the configured crossing time for a given {@link VehicleType}. * Utility method to get the configured crossing time for a given
* {@link VehicleType}.
* *
* @param type The type of vehicle. * @param type The type of vehicle.
* @return The crossing time in seconds. * @return The crossing time in seconds.
@@ -605,7 +617,7 @@ public class SimulationEngine {
default -> 2.0; default -> 2.0;
}; // Default fallback }; // Default fallback
} }
/** /**
* Prints the final summary of statistics at the end of the simulation. * Prints the final summary of statistics at the end of the simulation.
*/ */
@@ -613,33 +625,36 @@ public class SimulationEngine {
System.out.println("\n" + "=".repeat(60)); System.out.println("\n" + "=".repeat(60));
System.out.println("FINAL SIMULATION STATISTICS"); System.out.println("FINAL SIMULATION STATISTICS");
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
statisticsCollector.printFinalStatistics(intersections, currentTime); statisticsCollector.printFinalStatistics(intersections, currentTime);
System.out.println("=".repeat(60)); System.out.println("=".repeat(60));
} }
// --- Public Getters --- // --- Public Getters ---
/** /**
* Gets the current simulation time. * Gets the current simulation time.
*
* @return The time in virtual seconds. * @return The time in virtual seconds.
*/ */
public double getCurrentTime() { public double getCurrentTime() {
return currentTime; return currentTime;
} }
/** /**
* Gets a map of all intersections in the simulation. * Gets a map of all intersections in the simulation.
* Returns a copy to prevent external modification. * Returns a copy to prevent external modification.
*
* @return A {@link Map} of intersection IDs to {@link Intersection} objects. * @return A {@link Map} of intersection IDs to {@link Intersection} objects.
*/ */
public Map<String, Intersection> getIntersections() { public Map<String, Intersection> getIntersections() {
return new HashMap<>(intersections); return new HashMap<>(intersections);
} }
/** /**
* Gets the statistics collector instance. * Gets the statistics collector instance.
*
* @return The {@link StatisticsCollector}. * @return The {@link StatisticsCollector}.
*/ */
public StatisticsCollector getStatisticsCollector() { public StatisticsCollector getStatisticsCollector() {

View File

@@ -46,54 +46,54 @@ simulation.arrival.fixed.interval=2.0
# === TRAFFIC LIGHT TIMINGS === # === TRAFFIC LIGHT TIMINGS ===
# Format: trafficlight.<intersection>.<direction>.<state>=<seconds> # Format: trafficlight.<intersection>.<direction>.<state>=<seconds>
# Intersection 1 # Intersection 1 (Entry point - balanced)
trafficlight.Cr1.North.green=30.0 trafficlight.Cr1.North.green=20.0
trafficlight.Cr1.North.red=30.0 trafficlight.Cr1.North.red=40.0
trafficlight.Cr1.South.green=30.0 trafficlight.Cr1.South.green=20.0
trafficlight.Cr1.South.red=30.0 trafficlight.Cr1.South.red=40.0
trafficlight.Cr1.East.green=30.0 trafficlight.Cr1.East.green=20.0
trafficlight.Cr1.East.red=30.0 trafficlight.Cr1.East.red=40.0
trafficlight.Cr1.West.green=30.0 trafficlight.Cr1.West.green=20.0
trafficlight.Cr1.West.red=30.0 trafficlight.Cr1.West.red=40.0
# Intersection 2 # Intersection 2 (Main hub - shorter cycles, favor East-West)
trafficlight.Cr2.North.green=25.0 trafficlight.Cr2.North.green=12.0
trafficlight.Cr2.North.red=35.0 trafficlight.Cr2.North.red=36.0
trafficlight.Cr2.South.green=25.0 trafficlight.Cr2.South.green=12.0
trafficlight.Cr2.South.red=35.0 trafficlight.Cr2.South.red=36.0
trafficlight.Cr2.East.green=35.0 trafficlight.Cr2.East.green=18.0
trafficlight.Cr2.East.red=25.0 trafficlight.Cr2.East.red=30.0
trafficlight.Cr2.West.green=35.0 trafficlight.Cr2.West.green=18.0
trafficlight.Cr2.West.red=25.0 trafficlight.Cr2.West.red=30.0
# Intersection 3 # Intersection 3 (Path to exit - favor East)
trafficlight.Cr3.North.green=30.0 trafficlight.Cr3.North.green=15.0
trafficlight.Cr3.North.red=30.0 trafficlight.Cr3.North.red=30.0
trafficlight.Cr3.South.green=30.0 trafficlight.Cr3.South.green=15.0
trafficlight.Cr3.South.red=30.0 trafficlight.Cr3.South.red=30.0
trafficlight.Cr3.East.green=30.0 trafficlight.Cr3.East.green=20.0
trafficlight.Cr3.East.red=30.0 trafficlight.Cr3.East.red=25.0
trafficlight.Cr3.West.green=30.0 trafficlight.Cr3.West.green=15.0
trafficlight.Cr3.West.red=30.0 trafficlight.Cr3.West.red=30.0
# Intersection 4 # Intersection 4 (Favor East toward Cr5)
trafficlight.Cr4.North.green=30.0 trafficlight.Cr4.North.green=15.0
trafficlight.Cr4.North.red=30.0 trafficlight.Cr4.North.red=30.0
trafficlight.Cr4.South.green=30.0 trafficlight.Cr4.South.green=15.0
trafficlight.Cr4.South.red=30.0 trafficlight.Cr4.South.red=30.0
trafficlight.Cr4.East.green=30.0 trafficlight.Cr4.East.green=20.0
trafficlight.Cr4.East.red=30.0 trafficlight.Cr4.East.red=25.0
trafficlight.Cr4.West.green=30.0 trafficlight.Cr4.West.green=15.0
trafficlight.Cr4.West.red=30.0 trafficlight.Cr4.West.red=30.0
# Intersection 5 # Intersection 5 (Near exit - favor East)
trafficlight.Cr5.North.green=30.0 trafficlight.Cr5.North.green=15.0
trafficlight.Cr5.North.red=30.0 trafficlight.Cr5.North.red=30.0
trafficlight.Cr5.South.green=30.0 trafficlight.Cr5.South.green=15.0
trafficlight.Cr5.South.red=30.0 trafficlight.Cr5.South.red=30.0
trafficlight.Cr5.East.green=30.0 trafficlight.Cr5.East.green=22.0
trafficlight.Cr5.East.red=30.0 trafficlight.Cr5.East.red=23.0
trafficlight.Cr5.West.green=30.0 trafficlight.Cr5.West.green=15.0
trafficlight.Cr5.West.red=30.0 trafficlight.Cr5.West.red=30.0
# === VEHICLE CONFIGURATION === # === VEHICLE CONFIGURATION ===
@@ -118,4 +118,4 @@ vehicle.travel.time.heavy.multiplier=2.0
# === STATISTICS === # === STATISTICS ===
# Interval between dashboard updates (seconds) # Interval between dashboard updates (seconds)
statistics.update.interval=10.0 statistics.update.interval=1.0