mirror of
https://github.com/davidalves04/Trabalho-Pratico-SD.git
synced 2025-12-08 20:43:32 +00:00
Create IntersectionProcess main class
This commit is contained in:
565
main/src/main/java/sd/IntersectionProcess.java
Normal file
565
main/src/main/java/sd/IntersectionProcess.java
Normal file
@@ -0,0 +1,565 @@
|
||||
package sd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import sd.config.SimulationConfig;
|
||||
import sd.model.Intersection;
|
||||
import sd.model.MessageType;
|
||||
import sd.model.TrafficLight;
|
||||
import sd.model.TrafficLightState;
|
||||
import sd.model.Vehicle;
|
||||
import sd.protocol.MessageProtocol;
|
||||
import sd.protocol.SocketConnection;
|
||||
|
||||
/**
|
||||
* Main class for an Intersection Process in the distributed traffic simulation.
|
||||
* * Each IntersectionProcess runs as an independent Java application (JVM instance)
|
||||
* representing one of the five intersections (Cr1-Cr5) in the network.
|
||||
*/
|
||||
public class IntersectionProcess {
|
||||
|
||||
private final String intersectionId;
|
||||
|
||||
private final SimulationConfig config;
|
||||
|
||||
private final Intersection intersection;
|
||||
|
||||
private ServerSocket serverSocket;
|
||||
|
||||
private final Map<String, SocketConnection> outgoingConnections;
|
||||
|
||||
private final ExecutorService connectionHandlerPool;
|
||||
|
||||
private final ExecutorService trafficLightPool;
|
||||
|
||||
private volatile boolean running; //Quando uma thread escreve um valor volatile, todas as outras
|
||||
//threads veem a mudança imediatamente.
|
||||
|
||||
/**
|
||||
* Constructs a new IntersectionProcess.
|
||||
*
|
||||
* @param intersectionId The ID of this intersection (e.g., "Cr1").
|
||||
* @param configFilePath Path to the simulation.properties file.
|
||||
* @throws IOException If configuration cannot be loaded.
|
||||
*/
|
||||
public IntersectionProcess(String intersectionId, String configFilePath) throws IOException {
|
||||
this.intersectionId = intersectionId;
|
||||
this.config = new SimulationConfig(configFilePath);
|
||||
this.intersection = new Intersection(intersectionId);
|
||||
this.outgoingConnections = new HashMap<>();
|
||||
this.connectionHandlerPool = Executors.newCachedThreadPool();
|
||||
this.trafficLightPool = Executors.newFixedThreadPool(4); // Max 4 directions
|
||||
this.running = false;
|
||||
|
||||
System.out.println("=".repeat(60));
|
||||
System.out.println("INTERSECTION PROCESS: " + intersectionId);
|
||||
System.out.println("=".repeat(60));
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
System.out.println("\n[" + intersectionId + "] Initializing intersection...");
|
||||
|
||||
createTrafficLights();
|
||||
|
||||
configureRouting();
|
||||
|
||||
startTrafficLights();
|
||||
|
||||
System.out.println("[" + intersectionId + "] Initialization complete.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates traffic lights for this intersection based on its physical connections.
|
||||
* Each intersection has different number and directions of traffic lights
|
||||
* according to the network topology.
|
||||
*/
|
||||
private void createTrafficLights() {
|
||||
System.out.println("\n[" + intersectionId + "] Creating traffic lights...");
|
||||
|
||||
// Define directions based on the actual network topology
|
||||
String[] directions;
|
||||
switch (intersectionId) {
|
||||
case "Cr1":
|
||||
// Cr1: East (to Cr2), South (to Cr4), West (from Cr2)
|
||||
directions = new String[]{"East", "South", "West"};
|
||||
break;
|
||||
case "Cr2":
|
||||
// Cr2: West (to Cr1), East (to Cr3), South (to Cr5)
|
||||
// Plus receiving from Cr1 and Cr3
|
||||
directions = new String[]{"West", "East", "South"};
|
||||
break;
|
||||
case "Cr3":
|
||||
// Cr3: West (to Cr2), East (to S)
|
||||
directions = new String[]{"West", "East"};
|
||||
break;
|
||||
case "Cr4":
|
||||
// Cr4: East (to Cr5), plus pedestrian crossing
|
||||
directions = new String[]{"East"};
|
||||
break;
|
||||
case "Cr5":
|
||||
// Cr5: East (to S), receives from Cr2 and Cr4
|
||||
directions = new String[]{"East"};
|
||||
break;
|
||||
default:
|
||||
// Fallback to all directions
|
||||
directions = new String[]{"North", "South", "East", "West"};
|
||||
}
|
||||
|
||||
for (String direction : directions) {
|
||||
double greenTime = config.getTrafficLightGreenTime(intersectionId, direction);
|
||||
double redTime = config.getTrafficLightRedTime(intersectionId, direction);
|
||||
|
||||
TrafficLight light = new TrafficLight(
|
||||
intersectionId + "-" + direction,
|
||||
direction,
|
||||
greenTime,
|
||||
redTime
|
||||
);
|
||||
|
||||
intersection.addTrafficLight(light);
|
||||
System.out.println(" Created traffic light: " + direction +
|
||||
" (Green: " + greenTime + "s, Red: " + redTime + "s)");
|
||||
}
|
||||
}
|
||||
|
||||
private void configureRouting() {
|
||||
System.out.println("\n[" + intersectionId + "] Configuring routing...");
|
||||
|
||||
switch (intersectionId) {
|
||||
case "Cr1":
|
||||
// Cr1 connections: → Cr2 (East), → Cr4 (South), ← Cr2 (West)
|
||||
intersection.configureRoute("Cr2", "East"); // Go to Cr2
|
||||
intersection.configureRoute("Cr4", "South"); // Go to Cr4
|
||||
// Routes through other intersections to reach S
|
||||
intersection.configureRoute("S", "East"); // S via Cr2
|
||||
break;
|
||||
|
||||
case "Cr2":
|
||||
// Cr2 connections: ↔ Cr1 (West/East), ↔ Cr3 (East/West), → Cr5 (South)
|
||||
intersection.configureRoute("Cr1", "West"); // Go to Cr1
|
||||
intersection.configureRoute("Cr3", "East"); // Go to Cr3
|
||||
intersection.configureRoute("Cr5", "South"); // Go to Cr5
|
||||
intersection.configureRoute("S", "South"); // S via Cr5 or direct
|
||||
break;
|
||||
|
||||
case "Cr3":
|
||||
// Cr3 connections: ← Cr2 (West), → S (South/East)
|
||||
intersection.configureRoute("Cr2", "West"); // Go back to Cr2
|
||||
intersection.configureRoute("S", "East"); // Go to exit S
|
||||
break;
|
||||
|
||||
case "Cr4":
|
||||
// Cr4 connections: → Cr5 (East)
|
||||
intersection.configureRoute("Cr5", "East"); // Go to Cr5
|
||||
intersection.configureRoute("S", "East"); // S via Cr5
|
||||
break;
|
||||
|
||||
case "Cr5":
|
||||
// Cr5 connections: → S (East/South)
|
||||
intersection.configureRoute("S", "East"); // Go to exit S
|
||||
// Cr5 might also receive from Cr2 and Cr4 but doesn't route back
|
||||
break;
|
||||
|
||||
default:
|
||||
System.err.println(" Warning: Unknown intersection ID: " + intersectionId);
|
||||
}
|
||||
|
||||
System.out.println(" Routing configured.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts all traffic light threads.
|
||||
*/
|
||||
private void startTrafficLights() {
|
||||
System.out.println("\n[" + intersectionId + "] Starting traffic light threads...");
|
||||
|
||||
for (TrafficLight light : intersection.getTrafficLights()) {
|
||||
trafficLightPool.submit(() -> runTrafficLightCycle(light));
|
||||
System.out.println(" Started thread for: " + light.getDirection());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main loop for a traffic light thread.
|
||||
* Continuously cycles between GREEN and RED states.
|
||||
*
|
||||
* @param light The traffic light to control.
|
||||
*/
|
||||
private void runTrafficLightCycle(TrafficLight light) {
|
||||
System.out.println("[" + light.getId() + "] Traffic light thread started.");
|
||||
|
||||
while (running) {
|
||||
try {
|
||||
// GREEN phase
|
||||
light.changeState(TrafficLightState.GREEN);
|
||||
System.out.println("[" + light.getId() + "] State: GREEN");
|
||||
|
||||
// Process vehicles while green
|
||||
processGreenLight(light);
|
||||
|
||||
// Wait for green duration
|
||||
Thread.sleep((long) (light.getGreenTime() * 1000));
|
||||
|
||||
// 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) {
|
||||
System.out.println("[" + light.getId() + "] Traffic light thread interrupted.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("[" + light.getId() + "] Traffic light thread stopped.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes vehicles when a traffic light is GREEN.
|
||||
* Dequeues vehicles and sends them to their next destination.
|
||||
*
|
||||
* @param light The traffic light that is currently green.
|
||||
*/
|
||||
private void processGreenLight(TrafficLight light) {
|
||||
while (light.getState() == TrafficLightState.GREEN && light.getQueueSize() > 0) {
|
||||
Vehicle vehicle = light.removeVehicle();
|
||||
|
||||
if (vehicle != null) {
|
||||
// Get crossing time based on vehicle type
|
||||
double crossingTime = getCrossingTimeForVehicle(vehicle);
|
||||
|
||||
// Simulate crossing time
|
||||
try {
|
||||
Thread.sleep((long) (crossingTime * 1000));
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break;
|
||||
}
|
||||
|
||||
// Update vehicle statistics
|
||||
vehicle.addCrossingTime(crossingTime);
|
||||
|
||||
// Update intersection statistics
|
||||
intersection.incrementVehiclesSent();
|
||||
|
||||
// Send vehicle to next destination
|
||||
sendVehicleToNextDestination(vehicle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the crossing time for a vehicle based on its type.
|
||||
*
|
||||
* @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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a vehicle to its next destination via socket connection.
|
||||
*
|
||||
* @param vehicle The vehicle that has crossed this intersection.
|
||||
*/
|
||||
private void sendVehicleToNextDestination(Vehicle vehicle) {
|
||||
String nextDestination = vehicle.getCurrentDestination();
|
||||
|
||||
try {
|
||||
// Get or create connection to next destination
|
||||
SocketConnection connection = getOrCreateConnection(nextDestination);
|
||||
|
||||
// Create and send message
|
||||
MessageProtocol message = new VehicleTransferMessage(
|
||||
intersectionId,
|
||||
nextDestination,
|
||||
vehicle
|
||||
);
|
||||
|
||||
connection.sendMessage(message);
|
||||
|
||||
System.out.println("[" + intersectionId + "] Sent vehicle " + vehicle.getId() +
|
||||
" to " + nextDestination);
|
||||
|
||||
// Update vehicle's path - advance to next destination in route
|
||||
vehicle.advanceRoute();
|
||||
|
||||
} catch (IOException | InterruptedException e) {
|
||||
System.err.println("[" + intersectionId + "] Failed to send vehicle " +
|
||||
vehicle.getId() + " to " + nextDestination + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an existing connection to a destination or creates a new one.
|
||||
*
|
||||
* @param destinationId The ID of the destination node.
|
||||
* @return The SocketConnection to that destination.
|
||||
* @throws IOException If connection cannot be established.
|
||||
* @throws InterruptedException If connection attempt is interrupted.
|
||||
*/
|
||||
private synchronized SocketConnection getOrCreateConnection(String destinationId)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
if (!outgoingConnections.containsKey(destinationId)) {
|
||||
String host = getHostForDestination(destinationId);
|
||||
int port = getPortForDestination(destinationId);
|
||||
|
||||
System.out.println("[" + intersectionId + "] Creating connection to " +
|
||||
destinationId + " at " + host + ":" + port);
|
||||
|
||||
SocketConnection connection = new SocketConnection(host, port);
|
||||
outgoingConnections.put(destinationId, connection);
|
||||
}
|
||||
|
||||
return outgoingConnections.get(destinationId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the host address for a destination node from configuration.
|
||||
*
|
||||
* @param destinationId The destination node ID.
|
||||
* @return The host address.
|
||||
*/
|
||||
private String getHostForDestination(String destinationId) {
|
||||
if (destinationId.equals("S")) {
|
||||
return config.getExitHost();
|
||||
} else if (destinationId.startsWith("Cr")) {
|
||||
return config.getIntersectionHost(destinationId);
|
||||
} else {
|
||||
return config.getDashboardHost();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the port number for a destination node from configuration.
|
||||
*
|
||||
* @param destinationId The destination node ID.
|
||||
* @return The port number.
|
||||
*/
|
||||
private int getPortForDestination(String destinationId) {
|
||||
if (destinationId.equals("S")) {
|
||||
return config.getExitPort();
|
||||
} else if (destinationId.startsWith("Cr")) {
|
||||
return config.getIntersectionPort(destinationId);
|
||||
} else {
|
||||
return config.getDashboardPort();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server socket and begins accepting incoming connections.
|
||||
* This is the main listening loop of the process.
|
||||
*
|
||||
* @throws IOException If the server socket cannot be created.
|
||||
*/
|
||||
public void start() throws IOException {
|
||||
int port = config.getIntersectionPort(intersectionId);
|
||||
serverSocket = new ServerSocket(port);
|
||||
running = true;
|
||||
|
||||
System.out.println("\n[" + intersectionId + "] Server started on port " + port);
|
||||
System.out.println("[" + intersectionId + "] Waiting for incoming connections...\n");
|
||||
|
||||
// Main accept loop
|
||||
while (running) {
|
||||
try {
|
||||
Socket clientSocket = serverSocket.accept();
|
||||
|
||||
// Handle each connection in a separate thread
|
||||
connectionHandlerPool.submit(() -> handleIncomingConnection(clientSocket));
|
||||
|
||||
} catch (IOException e) {
|
||||
if (running) {
|
||||
System.err.println("[" + intersectionId + "] Error accepting connection: " +
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an incoming connection from another process.
|
||||
* Continuously listens for vehicle transfer messages.
|
||||
*
|
||||
* @param clientSocket The accepted socket connection.
|
||||
*/
|
||||
private void handleIncomingConnection(Socket clientSocket) {
|
||||
try (SocketConnection connection = new SocketConnection(clientSocket)) {
|
||||
|
||||
System.out.println("[" + intersectionId + "] New connection accepted from " +
|
||||
clientSocket.getInetAddress().getHostAddress());
|
||||
|
||||
// Continuously receive messages while connection is active
|
||||
while (running && connection.isConnected()) {
|
||||
try {
|
||||
MessageProtocol message = connection.receiveMessage();
|
||||
|
||||
if (message.getType() == MessageType.VEHICLE_TRANSFER) {
|
||||
Vehicle vehicle = (Vehicle) message.getPayload();
|
||||
|
||||
System.out.println("[" + intersectionId + "] Received vehicle: " +
|
||||
vehicle.getId() + " from " + message.getSourceNode());
|
||||
|
||||
// Add vehicle to appropriate queue
|
||||
intersection.receiveVehicle(vehicle);
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("[" + intersectionId + "] Unknown message type received: " +
|
||||
e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
if (running) {
|
||||
System.err.println("[" + intersectionId + "] Connection error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the intersection process gracefully.
|
||||
* Shuts down all threads and closes all connections.
|
||||
*/
|
||||
public void shutdown() {
|
||||
System.out.println("\n[" + intersectionId + "] Shutting down...");
|
||||
running = false;
|
||||
|
||||
// Close server socket
|
||||
try {
|
||||
if (serverSocket != null && !serverSocket.isClosed()) {
|
||||
serverSocket.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("[" + intersectionId + "] Error closing server socket: " +
|
||||
e.getMessage());
|
||||
}
|
||||
|
||||
// Shutdown thread pools
|
||||
trafficLightPool.shutdown();
|
||||
connectionHandlerPool.shutdown();
|
||||
|
||||
try {
|
||||
if (!trafficLightPool.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
trafficLightPool.shutdownNow();
|
||||
}
|
||||
if (!connectionHandlerPool.awaitTermination(5, TimeUnit.SECONDS)) {
|
||||
connectionHandlerPool.shutdownNow();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
trafficLightPool.shutdownNow();
|
||||
connectionHandlerPool.shutdownNow();
|
||||
}
|
||||
|
||||
// Close all outgoing connections
|
||||
for (Map.Entry<String, SocketConnection> entry : outgoingConnections.entrySet()) {
|
||||
try {
|
||||
entry.getValue().close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("[" + intersectionId + "] Error closing connection to " +
|
||||
entry.getKey() + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("[" + intersectionId + "] Shutdown complete.");
|
||||
System.out.println("=".repeat(60));
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method to start an intersection process.
|
||||
*
|
||||
* @param args Command-line arguments:
|
||||
* args[0] - Intersection ID (required, e.g., "Cr1")
|
||||
* args[1] - Config file path (optional, defaults to "simulation.properties")
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if (args.length < 1) {
|
||||
System.err.println("Usage: java IntersectionProcess <intersectionId> [configFile]");
|
||||
System.err.println("Example: java IntersectionProcess Cr1");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String intersectionId = args[0];
|
||||
String configFile = args.length > 1 ? args[1] : "simulation.properties";
|
||||
|
||||
IntersectionProcess process = null;
|
||||
|
||||
try {
|
||||
process = new IntersectionProcess(intersectionId, configFile);
|
||||
process.initialize();
|
||||
|
||||
// Add shutdown hook for graceful termination
|
||||
final IntersectionProcess finalProcess = process;
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
finalProcess.shutdown();
|
||||
}));
|
||||
|
||||
// Start the process
|
||||
process.start();
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error starting intersection process: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Inner class for Vehicle Transfer Messages ---
|
||||
|
||||
/**
|
||||
* Implementation of MessageProtocol for vehicle transfers between processes.
|
||||
*/
|
||||
private static class VehicleTransferMessage implements MessageProtocol {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String sourceNode;
|
||||
private final String destinationNode;
|
||||
private final Vehicle payload;
|
||||
|
||||
public VehicleTransferMessage(String sourceNode, String destinationNode, Vehicle vehicle) {
|
||||
this.sourceNode = sourceNode;
|
||||
this.destinationNode = destinationNode;
|
||||
this.payload = vehicle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageType getType() {
|
||||
return MessageType.VEHICLE_TRANSFER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceNode() {
|
||||
return sourceNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDestinationNode() {
|
||||
return destinationNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user