p1/machine/ElevatorBank.java

621 lines
15 KiB
Java

// 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;
}