621 lines
15 KiB
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;
|
|
}
|