import java.io.*; import java.util.*; public class ICT { public static final int NUM_OF_FLOORS = 7; public static final double MEAN_WORK = 3600.0; //Mean time spent working is 60.0 minutes (converted to seconds) public static final double LAMBDA_WORK = 1.0 / MEAN_WORK; //Times are in minutes public static final double START = 0.0; public static final double END = 86400; //Run simulation for 24 hours (converted to seconds) //Scheduling algorithms public static final int FCFS = 0; public static final int SSTF = 1; //Idling policies public static final int STAY = 0; public static final int BOTTOM = 1; //Random number generators private static Random r1 = new Random(12345); //Used for interarrival times private static Random r2 = new Random(54321); //Used for time spent working private static Random r3 = new Random(98765); //Used to pick a random floor private static Random r4 = new Random(35917); //Used for coin flips //User-configurable settings private static int numOfElevators = 1; private static int schedulingAlgorithm = FCFS; private static int idlingPolicy = STAY; private static double lambda = 0.5 / 60.0; //0.5 people arrive per minute (converted into seconds) /* These matricies will keep a running sum of the response times for each floor as well as the number of requests for each floor. The row index is the destination floor, and the column index is the calling floor. For example, responseTimes[4][1] is the running sum of the response times for boarding an elevator on floor 1 and getting off at floor 4, and responseCount[4][1] is the number of times someone rode the elevator from floor 1 to floor 4. */ private static double[][] responseTimes = new double[NUM_OF_FLOORS + 1][NUM_OF_FLOORS + 1];; private static int[][] responseCount = new int[NUM_OF_FLOORS + 1][NUM_OF_FLOORS + 1]; private static ArrayList allResponseTimes = new ArrayList(); /* The key is the floor number (between 1 and NUM_OF_FLOORS), and the value is the Floor object associated with that floor number. */ private static HashMap floors = new HashMap(); private static EventList eventList = new EventList(); private static RequestList requestList = new RequestList(); private static Elevator elevator1 = null; private static Elevator elevator2 = null; private static Elevator elevator3 = null; private static double clock; public static void main(String[] args) { //Get simulation configuration from command line arguments initialize(args); //Initialize elevators if (numOfElevators == 1) { elevator1 = new Elevator(); } else if (numOfElevators == 2) { elevator1 = new Elevator(); elevator2 = new Elevator(); } else { elevator1 = new Elevator(); elevator2 = new Elevator(); elevator3 = new Elevator(); } //Initialize floors for (int i = 1; i <= NUM_OF_FLOORS; i++) { floors.put(i, new Floor(i)); } clock = START; //Schedule first person arriving on the ground floor Person p = new Person(1, clock + getInterarrivalTime(), getWorkDuration(), getRandomFloor()); eventList.addNewEvent(new Event(p.getArrivalTime(), p.getOriginFloor(), p)); //Simulation loop Event event = eventList.getNextEvent(); while (event.getTime() < END) { //Update the clock clock = event.getTime(); if (event.isPersonEvent()) { //A person is approaching the elevator area on some floor Person person = event.getPerson(); Floor floor = floors.get(event.getFloor()); //If nobody else is waiting at the elevator area, request an elevator if (!floor.hasPeopleWaiting()) { requestList.addPendingRequest(new Request(clock, floor.getId())); } floor.joinPeopleWaiting(person); if (floor.getId() == 1) { //Schedule another person arriving on the ground floor p = new Person(1, clock + getInterarrivalTime(), getWorkDuration(), getRandomFloor()); eventList.addNewEvent(new Event(p.getArrivalTime(), p.getOriginFloor(), p)); } } else { //An elevator is arriving on some floor Elevator elevator = event.getElevator(); Floor floor = floors.get(event.getFloor()); //Update elevator's floor to this floor elevator.setCurrentFloor(floor.getId()); if (event.isPickUpEvent()) { //Let people on elevator.load(floor.removePeopleWaiting()); //Schedule the event for the elevator's next dropoff eventList.addNewEvent(new Event(clock + elevator.getTravelTime(elevator.getNextFloor()), elevator.getNextFloor(), elevator, Event.DROPOFF_EVENT)); //Set elevator's status to busy elevator.setBusy(); } else if (event.isDropOffEvent()) { //Let passengers off ArrayList exitingPassengers = elevator.unload(); for (Person person : exitingPassengers) { //Record user-perceived response time addResponseTime(person.getOriginFloor(), floor.getId(), clock - person.getArrivalTime()); if (floor.getId() > 1) { //Schedule this person's leaving event person.setOriginFloor(floor.getId()); person.setArrivalTime(clock + person.getWorkDuration()); person.setDestination(1); eventList.addNewEvent(new Event(person.getArrivalTime(), person.getOriginFloor(), person)); } } //Record that this elevator successfully brought passengers to this floor elevator.incrementRequestsServed(); if (!elevator.isEmpty()) { //Schedule the event for the elevator's next dropoff eventList.addNewEvent(new Event(clock + elevator.getTravelTime(elevator.getNextFloor()), elevator.getNextFloor(), elevator, Event.DROPOFF_EVENT)); } else { //Check for pending requests if (requestList.hasPendingRequest()) { int nextRequestFloor; if (schedulingAlgorithm == FCFS) { nextRequestFloor = requestList.getFirstPendingRequest(); } else { nextRequestFloor = requestList.getClosestPendingRequest(elevator.getCurrentFloor()); } //Schedule the elevator arriving on the requested floor eventList.addNewEvent(new Event(clock + elevator.getTravelTime(nextRequestFloor), nextRequestFloor, elevator, Event.PICKUP_EVENT)); //Remove the request from the request list to avoid double booking requestList.removePendingRequest(nextRequestFloor); } else { if (idlingPolicy == STAY) { elevator.setIdle(); } else { //Schedule event for elevator arriving at bottom floor eventList.addNewEvent(new Event(clock + elevator.getTravelTime(1), 1, elevator, Event.IDLE_EVENT)); } } } } else { /* Elevator has arrived at the bottom floor to idle. If there are no pending requests (some may have popped up while the elevator was traveling to its idling location), set the elevator's status to idle. */ if (requestList.hasPendingRequest()) { int nextRequestFloor; if (schedulingAlgorithm == FCFS) { nextRequestFloor = requestList.getFirstPendingRequest(); } else { nextRequestFloor = requestList.getClosestPendingRequest(elevator.getCurrentFloor()); } //Schedule the elevator arriving on the requested floor eventList.addNewEvent(new Event(clock + elevator.getTravelTime(nextRequestFloor), nextRequestFloor, elevator, Event.PICKUP_EVENT)); //Remove the request from the request list to avoid double booking requestList.removePendingRequest(nextRequestFloor); } else { elevator.setIdle(); } } } /* Check if there are any pending requests. If there is a pending request, check if there is an idle elevator that can service the request. If an idle elevator is found, schedule the pick up event. */ if (requestList.hasPendingRequest()) { Elevator availableElevator = getIdleElevator(); if (availableElevator != null) { int nextRequestFloor; if (schedulingAlgorithm == FCFS) { nextRequestFloor = requestList.getFirstPendingRequest(); } else { nextRequestFloor = requestList.getClosestPendingRequest(availableElevator.getCurrentFloor()); } //Schedule the elevator arriving on the requested floor eventList.addNewEvent(new Event(clock + availableElevator.getTravelTime(nextRequestFloor), nextRequestFloor, availableElevator, Event.PICKUP_EVENT)); availableElevator.setBusy(); //Remove the request from the request list to avoid double booking requestList.removePendingRequest(nextRequestFloor); } } //Get the next event event = eventList.getNextEvent(); } printStatistics(); writeToFile(); } /** * Generates a random interarrival time following an Exponential distribution. * * @return an interarrival time */ public static double getInterarrivalTime() { double u = r1.nextDouble(); double x = (-1.0 / lambda) * Math.log(1 - u); return x; } /** * Generates a random work duration following an Exponential distribution. * * @return a work duration */ public static double getWorkDuration() { double u = r2.nextDouble(); double x = (-1.0 / LAMBDA_WORK) * Math.log(1 - u); return x; } /** * Generates a random floor number that is not the ground floor. * * @return a random integer between 2 and NUM_OF_FLOORS */ public static int getRandomFloor() { return r3.nextInt(NUM_OF_FLOORS - 1) + 2; } /** * This method checks for an idle elevator. If all elevators are idle, * one is chosen at random. If all elevators are busy, the method returns null. * * @return an idle elevator, or null if all elevators are busy */ public static Elevator getIdleElevator() { if (numOfElevators == 1) { if (elevator1.isIdle()) { return elevator1; } else { return null; } } else if (numOfElevators == 2) { if (elevator1.isIdle() && elevator2.isIdle()) { //Flip a coin if (r4.nextInt(2) == 0) { return elevator1; } else { return elevator2; } } else if (!elevator1.isIdle() && !elevator2.isIdle()) { return null; } else if (elevator1.isIdle()) { return elevator1; } else { return elevator2; } } else { if (elevator1.isIdle() && elevator2.isIdle() && elevator3.isIdle()) { //Choose an elevator at random int choice = r4.nextInt(3); if (choice == 0) { return elevator1; } else if (choice == 1) { return elevator2; } else { return elevator3; } } else if (!elevator1.isIdle() && !elevator2.isIdle() && !elevator3.isIdle()) { return null; } else if (elevator1.isIdle()) { if (elevator2.isIdle()) { //Flip a coin between elevator1 and elevator2 if (r4.nextInt(2) == 0) { return elevator1; } else { return elevator2; } } else if (elevator3.isIdle()) { //Flip a coin between elevator1 and elevator3 if (r4.nextInt(2) == 0) { return elevator1; } else { return elevator3; } } else { return elevator1; } } else if (elevator2.isIdle()) { if (elevator3.isIdle()) { //Flip a coin between elevator2 and elevator3 if (r4.nextInt(2) == 0) { return elevator2; } else { return elevator3; } } else { return elevator2; } } else { return elevator3; } } } /** * Update statistical counters. * * @param callingFloor the floor where the person boarded the elevator * @param destinationFloor the floor where the person got off the elevator * @param the time that the person arrived at the destination floor */ public static void addResponseTime(int callingFloor, int destinationFloor, double responseTime) { allResponseTimes.add(responseTime); responseTimes[destinationFloor][callingFloor] = responseTimes[destinationFloor][callingFloor] + responseTime; responseCount[destinationFloor][callingFloor] = responseCount[destinationFloor][callingFloor] + 1; } /** * Output the average user-perceived response time, the number of requests * served by each elevator, and a breakdown of the reponse times by individual floor. */ public static void printStatistics() { if (numOfElevators == 1) { System.out.println("Requests served by elevator: " + elevator1.getRequestsServed()); } else if (numOfElevators == 2) { System.out.println("Requests served by elevator #1: " + elevator1.getRequestsServed()); System.out.println("Requests served by elevator #2: " + elevator2.getRequestsServed()); } else { System.out.println("Requests served by elevator #1: " + elevator1.getRequestsServed()); System.out.println("Requests served by elevator #2: " + elevator2.getRequestsServed()); System.out.println("Requests served by elevator #3: " + elevator3.getRequestsServed()); } double sum = 0.0; for (Double responseTime : allResponseTimes) { sum += responseTime; } String num = String.format("%.3f", sum / allResponseTimes.size()); System.out.println("\nAverage user-perceived response time: " + num + " seconds"); //Breakdown by floor System.out.println("\nAverage user-perceived response time-- going up:"); for (int i = 2; i < responseTimes.length; i++) { num = String.format("%.3f", responseTimes[i][1] / responseCount[i][1]); System.out.println(" floor 1 ---> floor " + i + ": " + num + " seconds" + " (" + responseCount[i][1] + " users)"); } System.out.println("\nAverage user-perceived response time-- going down:"); for (int j = 2; j < responseTimes[1].length; j++) { num = String.format("%.3f", responseTimes[1][j] / responseCount[1][j]); System.out.println(" floor " + j + " ---> floor 1: " + num + " seconds" + " (" + responseCount[1][j] + " users)"); } } /** * Writes a record of the response times to a file named response.txt * which can then be used to create a histogram. */ public static void writeToFile() { try { File file = new File("response.txt"); PrintWriter output = new PrintWriter(file); for (Double responseTime : allResponseTimes) { output.println(responseTime); output.flush(); } output.close(); } catch (Exception e) { System.out.println("Could not write to file response.txt"); } } /** * Update the simulation settings to user-set values from the command line arguments * * @param args the command line arguments */ public static void initialize(String[] args) { try { numOfElevators = Integer.parseInt(args[0]); if (numOfElevators < 1 || numOfElevators > 3) { throw new Exception(); } String tmp = args[1]; if (tmp.equalsIgnoreCase("fcfs")) { schedulingAlgorithm = FCFS; } else if (tmp.equalsIgnoreCase("sstf")) { schedulingAlgorithm = SSTF; } else { throw new Exception(); } tmp = args[2]; if (tmp.equalsIgnoreCase("stay")) { idlingPolicy = STAY; } else if (tmp.equalsIgnoreCase("bottom")) { idlingPolicy = BOTTOM; } else { throw new Exception(); } lambda = Double.parseDouble(args[3]) / 60.0; //Convert to arrivals per second } catch (Exception e) { System.out.println("Error: invalid command line arguments."); System.out.println("usage: java ICT numOfElevators schedulingAlgorithm idlingPolicy lambda"); System.out.println("choices for numOfElevators are 1, 2, and 3"); System.out.println("choices for schedulingAlgorithm are fcfs and sstf"); System.out.println("choices for idlingPolicy are stay and bottom"); System.out.println("example: java ICT 1 fcfs stay 0.5"); System.exit(0); } } }