// PART OF THE MACHINE SIMULATION. DO NOT CHANGE. package nachos.machine; import nachos.security.*; import nachos.threads.KThread; import nachos.threads.Semaphore; import java.util.Vector; import java.util.LinkedList; import java.util.Iterator; /** * A bank of elevators. */ public final class ElevatorBank implements Runnable { /** Indicates an elevator intends to move down. */ public static final int dirDown = -1; /** Indicates an elevator intends not to move. */ public static final int dirNeither = 0; /** Indicates an elevator intends to move up. */ public static final int dirUp = 1; /** * Allocate a new elevator bank. * * @param privilege encapsulates privileged access to the Nachos * machine. */ public ElevatorBank(Privilege privilege) { System.out.print(" elevators"); this.privilege = privilege; simulationStarted = false; } /** * Initialize this elevator bank with the specified number of elevators and * the specified number of floors. The software elevator controller must * also be specified. This elevator must not already be running a * simulation. * * @param numElevators the number of elevators in the bank. * @param numFloors the number of floors in the bank. * @param controller the elevator controller. */ public void init(int numElevators, int numFloors, ElevatorControllerInterface controller) { Lib.assertTrue(!simulationStarted); this.numElevators = numElevators; this.numFloors = numFloors; manager = new ElevatorManager(controller); elevators = new ElevatorState[numElevators]; for (int i=0; i<numElevators; i++) elevators[i] = new ElevatorState(0); numRiders = 0; ridersVector = new Vector<RiderControls>(); enableGui = false; gui = null; } /** * Add a rider to the simulation. This method must not be called after * <tt>run()</tt> is called. * * @param rider the rider to add. * @param floor the floor the rider will start on. * @param stops the array to pass to the rider's <tt>initialize()</tt> * method. * @return the controls that will be given to the rider. */ public RiderControls addRider(RiderInterface rider, int floor, int[] stops) { Lib.assertTrue(!simulationStarted); RiderControls controls = new RiderState(rider, floor, stops); ridersVector.addElement(controls); numRiders++; return controls; } /** * Create a GUI for this elevator bank. */ public void enableGui() { Lib.assertTrue(!simulationStarted); Lib.assertTrue(Config.getBoolean("ElevatorBank.allowElevatorGUI")); enableGui = true; } /** * Run a simulation. Initialize all elevators and riders, and then * fork threads to each of their <tt>run()</tt> methods. Return when the * simulation is finished. */ public void run() { Lib.assertTrue(!simulationStarted); simulationStarted = true; riders = new RiderState[numRiders]; ridersVector.toArray(riders); if (enableGui) { privilege.doPrivileged(new Runnable() { public void run() { initGui(); } }); } for (int i=0; i<numRiders; i++) riders[i].initialize(); manager.initialize(); for (int i=0; i<numRiders; i++) riders[i].run(); manager.run(); for (int i=0; i<numRiders; i++) riders[i].join(); manager.join(); simulationStarted = false; } private void initGui() { int[] numRidersPerFloor = new int[numFloors]; for (int floor=0; floor<numFloors; floor++) numRidersPerFloor[floor] = 0; for (int rider=0; rider<numRiders; rider++) numRidersPerFloor[riders[rider].floor]++; gui = new ElevatorGui(numFloors, numElevators, numRidersPerFloor); } /** * Tests whether this module is working. */ public static void selfTest() { new ElevatorTest().run(); } void postRiderEvent(int event, int floor, int elevator) { int direction = dirNeither; if (elevator != -1) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); direction = elevators[elevator].direction; } RiderEvent e = new RiderEvent(event, floor, elevator, direction); for (int i=0; i<numRiders; i++) { RiderState rider = riders[i]; if ((rider.inElevator && rider.elevator == e.elevator) || (!rider.inElevator && rider.floor == e.floor)) { rider.events.add(e); rider.schedule(1); } } } private class ElevatorManager implements ElevatorControls { ElevatorManager(ElevatorControllerInterface controller) { this.controller = controller; interrupt = new Runnable() { public void run() { interrupt(); } }; } public int getNumFloors() { return numFloors; } public int getNumElevators() { return numElevators; } public void setInterruptHandler(Runnable handler) { this.handler = handler; } public void openDoors(int elevator) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); postRiderEvent(RiderEvent.eventDoorsOpened, elevators[elevator].openDoors(), elevator); if (gui != null) { if (elevators[elevator].direction == dirUp) gui.clearUpButton(elevators[elevator].floor); else if (elevators[elevator].direction == dirDown) gui.clearDownButton(elevators[elevator].floor); gui.openDoors(elevator); } } public void closeDoors(int elevator) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); postRiderEvent(RiderEvent.eventDoorsClosed, elevators[elevator].closeDoors(), elevator); if (gui != null) gui.closeDoors(elevator); } public boolean moveTo(int floor, int elevator) { Lib.assertTrue(floor >= 0 && floor < numFloors); Lib.assertTrue(elevator >= 0 && elevator < numElevators); if (!elevators[elevator].moveTo(floor)) return false; schedule(Stats.ElevatorTicks); return true; } public int getFloor(int elevator) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); return elevators[elevator].floor; } public void setDirectionDisplay(int elevator, int direction) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); elevators[elevator].direction = direction; if (elevators[elevator].doorsOpen) { postRiderEvent(RiderEvent.eventDirectionChanged, elevators[elevator].floor, elevator); } if (gui != null) { if (elevators[elevator].doorsOpen) { if (direction == dirUp) gui.clearUpButton(elevators[elevator].floor); else if (direction == dirDown) gui.clearDownButton(elevators[elevator].floor); } gui.setDirectionDisplay(elevator, direction); } } public void finish() { finished = true; Lib.assertTrue(KThread.currentThread() == thread); done.V(); KThread.finish(); } public ElevatorEvent getNextEvent() { if (events.isEmpty()) return null; else return (ElevatorEvent) events.removeFirst(); } void schedule(int when) { privilege.interrupt.schedule(when, "elevator", interrupt); } void postEvent(int event, int floor, int elevator, boolean schedule) { events.add(new ElevatorEvent(event, floor, elevator)); if (schedule) schedule(1); } void interrupt() { for (int i=0; i<numElevators; i++) { if (elevators[i].atNextFloor()) { if (gui != null) gui.elevatorMoved(elevators[i].floor, i); if (elevators[i].atDestination()) { postEvent(ElevatorEvent.eventElevatorArrived, elevators[i].destination, i, false); } else { elevators[i].nextETA += Stats.ElevatorTicks; privilege.interrupt.schedule(Stats.ElevatorTicks, "elevator", interrupt); } } } if (!finished && !events.isEmpty() && handler != null) handler.run(); } void initialize() { controller.initialize(this); } void run() { thread = new KThread(controller); thread.setName("elevator controller"); thread.fork(); } void join() { postEvent(ElevatorEvent.eventRidersDone, -1, -1, true); done.P(); } ElevatorControllerInterface controller; Runnable interrupt; KThread thread; Runnable handler = null; LinkedList<ElevatorEvent> events = new LinkedList<ElevatorEvent>(); Semaphore done = new Semaphore(0); boolean finished = false; } private class ElevatorState { ElevatorState(int floor) { this.floor = floor; destination = floor; } int openDoors() { Lib.assertTrue(!doorsOpen && !moving); doorsOpen = true; return floor; } int closeDoors() { Lib.assertTrue(doorsOpen); doorsOpen = false; return floor; } boolean moveTo(int newDestination) { Lib.assertTrue(!doorsOpen); if (!moving) { // can't move to current floor if (floor == newDestination) return false; destination = newDestination; nextETA = Machine.timer().getTime() + Stats.ElevatorTicks; moving = true; return true; } else { // moving, shouldn't be at destination Lib.assertTrue(floor != destination); // make sure it's ok to stop if ((destination > floor && newDestination <= floor) || (destination < floor && newDestination >= floor)) return false; destination = newDestination; return true; } } boolean enter(RiderState rider, int onFloor) { Lib.assertTrue(!riders.contains(rider)); if (!doorsOpen || moving || onFloor != floor || riders.size() == maxRiders) return false; riders.addElement(rider); return true; } boolean exit(RiderState rider, int onFloor) { Lib.assertTrue(riders.contains(rider)); if (!doorsOpen || moving || onFloor != floor) return false; riders.removeElement(rider); return true; } boolean atNextFloor() { if (!moving || Machine.timer().getTime() < nextETA) return false; Lib.assertTrue(destination != floor); if (destination > floor) floor++; else floor--; for (Iterator i=riders.iterator(); i.hasNext(); ) { RiderState rider = (RiderState) i.next(); rider.floor = floor; } return true; } boolean atDestination() { if (!moving || destination != floor) return false; moving = false; return true; } static final int maxRiders = 4; int floor, destination; long nextETA; boolean doorsOpen = false, moving = false; int direction = dirNeither; public Vector<RiderState> riders = new Vector<RiderState>(); } private class RiderState implements RiderControls { RiderState(RiderInterface rider, int floor, int[] stops) { this.rider = rider; this.floor = floor; this.stops = stops; interrupt = new Runnable() { public void run() { interrupt(); } }; } public int getNumFloors() { return numFloors; } public int getNumElevators() { return numElevators; } public void setInterruptHandler(Runnable handler) { this.handler = handler; } public int getFloor() { return floor; } public int[] getFloors() { int[] array = new int[floors.size()]; for (int i=0; i<array.length; i++) array[i] = ((Integer) floors.elementAt(i)).intValue(); return array; } public int getDirectionDisplay(int elevator) { Lib.assertTrue(elevator >= 0 && elevator < numElevators); return elevators[elevator].direction; } public RiderEvent getNextEvent() { if (events.isEmpty()) return null; else return (RiderEvent) events.removeFirst(); } public boolean pressDirectionButton(boolean up) { if (up) return pressUpButton(); else return pressDownButton(); } public boolean pressUpButton() { Lib.assertTrue(!inElevator && floor < numFloors-1); for (int elevator=0; elevator<numElevators; elevator++) { if (elevators[elevator].doorsOpen && elevators[elevator].direction == ElevatorBank.dirUp && elevators[elevator].floor == floor) return false; } manager.postEvent(ElevatorEvent.eventUpButtonPressed, floor, -1, true); if (gui != null) gui.pressUpButton(floor); return true; } public boolean pressDownButton() { Lib.assertTrue(!inElevator && floor > 0); for (int elevator=0; elevator<numElevators; elevator++) { if (elevators[elevator].doorsOpen && elevators[elevator].direction == ElevatorBank.dirDown && elevators[elevator].floor == floor) return false; } manager.postEvent(ElevatorEvent.eventDownButtonPressed, floor, -1, true); if (gui != null) gui.pressDownButton(floor); return true; } public boolean enterElevator(int elevator) { Lib.assertTrue(!inElevator && elevator >= 0 && elevator < numElevators); if (!elevators[elevator].enter(this, floor)) return false; if (gui != null) gui.enterElevator(floor, elevator); inElevator = true; this.elevator = elevator; return true; } public boolean pressFloorButton(int floor) { Lib.assertTrue(inElevator && floor >= 0 && floor < numFloors); if (elevators[elevator].doorsOpen && elevators[elevator].floor == floor) return false; manager.postEvent(ElevatorEvent.eventFloorButtonPressed, floor, elevator, true); if (gui != null) gui.pressFloorButton(floor, elevator); return true; } public boolean exitElevator(int floor) { Lib.assertTrue(inElevator && floor >= 0 && floor < numFloors); if (!elevators[elevator].exit(this, floor)) return false; inElevator = false; floors.add(new Integer(floor)); if (gui != null) gui.exitElevator(floor, elevator); return true; } public void finish() { finished = true; int[] floors = getFloors(); Lib.assertTrue(floors.length == stops.length); for (int i=0; i<floors.length; i++) Lib.assertTrue(floors[i] == stops[i]); Lib.assertTrue(KThread.currentThread() == thread); done.V(); KThread.finish(); } void schedule(int when) { privilege.interrupt.schedule(when, "rider", interrupt); } void interrupt() { if (!finished && !events.isEmpty() && handler != null) handler.run(); } void initialize() { rider.initialize(this, stops); } void run() { thread = new KThread(rider); thread.setName("rider"); thread.fork(); } void join() { done.P(); } RiderInterface rider; boolean inElevator = false, finished = false; int floor, elevator; int[] stops; Runnable interrupt, handler = null; LinkedList<RiderEvent> events = new LinkedList<RiderEvent>(); Vector<Integer> floors = new Vector<Integer>(); Semaphore done = new Semaphore(0); KThread thread; } private int numFloors, numElevators; private ElevatorManager manager; private ElevatorState[] elevators; private int numRiders; private Vector<RiderControls> ridersVector; private RiderState[] riders; private boolean simulationStarted, enableGui; private Privilege privilege; private ElevatorGui gui; }