diff --git a/main/src/main/java/sd/IntersectionProcess.java b/main/src/main/java/sd/IntersectionProcess.java index 95ed592..3942f0d 100644 --- a/main/src/main/java/sd/IntersectionProcess.java +++ b/main/src/main/java/sd/IntersectionProcess.java @@ -303,7 +303,7 @@ public class IntersectionProcess { * * @param vehicle The vehicle that has crossed this intersection. */ - private void sendVehicleToNextDestination(Vehicle vehicle) { + public void sendVehicleToNextDestination(Vehicle vehicle) { String nextDestination = vehicle.getCurrentDestination(); try { diff --git a/main/src/main/java/sd/engine/TrafficLightThread.java b/main/src/main/java/sd/engine/TrafficLightThread.java new file mode 100644 index 0000000..6d40fdf --- /dev/null +++ b/main/src/main/java/sd/engine/TrafficLightThread.java @@ -0,0 +1,158 @@ +package sd.engine; + +import sd.IntersectionProcess; +import sd.config.SimulationConfig; +import sd.model.TrafficLight; +import sd.model.TrafficLightState; +import sd.model.Vehicle; + +/** + * Implements the control logic for a single TrafficLight + * as a Runnable task that runs in its own Thread. + * + */ +public class TrafficLightThread implements Runnable { + + /** + * The TrafficLight object (the *model*) that this thread controls. + * Contains the queue and the state. + */ + private final TrafficLight light; + + /** + * The IntersectionProcess (the Process) that "owns" this thread. + * Used to call methods on the process, such as sendVehicleToNextDestination(). + */ + private final IntersectionProcess process; + + /** + * The simulation configuration, used to get timings (e.g., crossing time). + */ + private final SimulationConfig config; + + /** + * Volatile flag to control the graceful shutdown mechanism. + * When set to 'false', the 'run()' loop terminates. + */ + private volatile boolean running; + + /** + * Constructor for the Traffic Light Thread. + * + * @param light The TrafficLight object (model) to be controlled. + * @param process The parent IntersectionProcess (for callbacks). + * @param config The simulation configuration (to get timings). + */ + public TrafficLightThread(TrafficLight light, IntersectionProcess process, SimulationConfig config) { + this.light = light; + this.process = process; + this.config = config; + this.running = false; // Starts as 'stopped' + } + + /** + * The main entry point for the thread. + * Implements the GREEN/RED cycle logic extracted from IntersectionProcess. + * + */ + @Override + public void run() { + this.running = true; + System.out.println("[" + light.getId() + "] Traffic light thread started."); + + try { + // Main thread loop, continues while 'running' is true + // This 'running' flag is controlled by the parent IntersectionProcess + while (running) { + + // --- GREEN Phase --- + light.changeState(TrafficLightState.GREEN); // + System.out.println("[" + light.getId() + "] State: GREEN"); + + // Process vehicles in the queue + processGreenLightQueue(); + + // Wait for green duration + Thread.sleep((long) (light.getGreenTime() * 1000)); // + + if (!running) break; // Check flag after sleep + + // --- RED Phase --- + light.changeState(TrafficLightState.RED); // + System.out.println("[" + light.getId() + "] State: RED"); + + // Wait for red duration + Thread.sleep((long) (light.getRedTime() * 1000)); // + + } + } catch (InterruptedException e) { + // Apanha a InterruptedException (outra forma de parar a thread) + System.out.println("[" + light.getId() + "] Traffic light thread interrupted."); + this.running = false; // Garante que o loop termina + } + + System.out.println("[" + light.getId() + "] Traffic light thread stopped."); + } + + /** + * Processes vehicles in the queue while the traffic light is GREEN. + * Logic extracted from IntersectionProcess.processGreenLight() + * + */ + private void processGreenLightQueue() throws InterruptedException { + // + while (running && light.getState() == TrafficLightState.GREEN && light.getQueueSize() > 0) { + + Vehicle vehicle = light.removeVehicle(); // + + if (vehicle != null) { + // 1. Get the crossing time (t_sem) + double crossingTime = getCrossingTimeForVehicle(vehicle); // + + // 2. Simulate the time the vehicle takes to cross + Thread.sleep((long) (crossingTime * 1000)); // + + // 3. Update vehicle statistics + vehicle.addCrossingTime(crossingTime); // + + // 4. Update intersection statistics + + process.getIntersection().incrementVehiclesSent(); // + + // 5. Call the parent Process to send the vehicle + + process.sendVehicleToNextDestination(vehicle); // + } + } + } + + /** + * Gets the crossing time for a vehicle based on its type. + * Logic extracted from IntersectionProcess.getCrossingTimeForVehicle() + * + * + * @param vehicle The vehicle. + * @return The crossing time in seconds. + */ + private double getCrossingTimeForVehicle(Vehicle vehicle) { + switch (vehicle.getType()) { // + case BIKE: + return config.getBikeVehicleCrossingTime(); // + case LIGHT: + return config.getLightVehicleCrossingTime(); // + case HEAVY: + return config.getHeavyVehicleCrossingTime(); // + default: + return config.getLightVehicleCrossingTime(); // + } + } + + /** + * Requests the thread to stop gracefully (graceful shutdown). + * Sets the 'running' flag to false. The thread will finish + * its current sleep cycle and exit the 'run()' loop. + */ + public void shutdown() { + this.running = false; + } +} \ No newline at end of file