From 09cd85f364f2235ea8bb7340ceba7f2e15f68947 Mon Sep 17 00:00:00 2001 From: Imbus <> Date: Thu, 26 Sep 2024 00:07:55 +0200 Subject: [PATCH] Lab2-lift --- lift/src/LiftMain.java | 38 +++++++ lift/src/OnePersonRidesLift.java | 43 ++++--- lift/src/lift/Door.java | 25 +++++ lift/src/lift/LiftMonitor.java | 175 +++++++++++++++++++++++++++++ lift/src/lift/LiftThread.java | 20 ++++ lift/src/lift/Passenger.java | 27 +++-- lift/src/lift/PassengerThread.java | 34 ++++++ 7 files changed, 327 insertions(+), 35 deletions(-) create mode 100644 lift/src/LiftMain.java create mode 100644 lift/src/lift/Door.java create mode 100644 lift/src/lift/LiftMonitor.java create mode 100644 lift/src/lift/LiftThread.java create mode 100644 lift/src/lift/PassengerThread.java diff --git a/lift/src/LiftMain.java b/lift/src/LiftMain.java new file mode 100644 index 0000000..daded74 --- /dev/null +++ b/lift/src/LiftMain.java @@ -0,0 +1,38 @@ +import lift.LiftMonitor; +import lift.LiftThread; +import lift.LiftView; +import lift.PassengerThread; + +public class LiftMain { + public static void main(String[] args) { + // Could be read from args + final int NBR_FLOORS = 12, MAX_PASSENGERS = 4; + + LiftView view = new LiftView(NBR_FLOORS, MAX_PASSENGERS); + + // LiftMonitor passed to all threads + LiftMonitor monitor = new LiftMonitor(view, NBR_FLOORS); + + // Static array since the number of passengers is fixed + PassengerThread[] pass_threads = new PassengerThread[20]; + + for (PassengerThread p : pass_threads) { + p = new PassengerThread(view, monitor); + p.start(); + } + + LiftThread lift = new LiftThread(view, monitor); + lift.start(); + + // Rejoin all threads, in practice this is redundant + try { + // Passenger threads first + for (PassengerThread p : pass_threads) { + p.join(); + } + lift.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/lift/src/OnePersonRidesLift.java b/lift/src/OnePersonRidesLift.java index 0faec50..2b5c0e2 100644 --- a/lift/src/OnePersonRidesLift.java +++ b/lift/src/OnePersonRidesLift.java @@ -1,30 +1,27 @@ - import lift.LiftView; import lift.Passenger; public class OnePersonRidesLift { + public static void main(String[] args) { + final int NBR_FLOORS = 7, MAX_PASSENGERS = 4; - public static void main(String[] args) { + LiftView view = new LiftView(NBR_FLOORS, MAX_PASSENGERS); + Passenger pass = view.createPassenger(); + int fromFloor = pass.getStartFloor(); + int toFloor = pass.getDestinationFloor(); - final int NBR_FLOORS = 7, MAX_PASSENGERS = 4; - - LiftView view = new LiftView(NBR_FLOORS, MAX_PASSENGERS); - Passenger pass = view.createPassenger(); - int fromFloor = pass.getStartFloor(); - int toFloor = pass.getDestinationFloor(); - - pass.begin(); // walk in (from left) - if (fromFloor != 0) { - view.moveLift(0, fromFloor); - } - view.openDoors(fromFloor); - pass.enterLift(); // step inside - - view.closeDoors(); - view.moveLift(fromFloor, toFloor); // ride lift - view.openDoors(toFloor); - - pass.exitLift(); // leave lift - pass.end(); // walk out (to the right) + pass.begin(); // walk in (from left) + if (fromFloor != 0) { + view.moveLift(0, fromFloor); } -} \ No newline at end of file + view.openDoors(fromFloor); + pass.enterLift(); // step inside + + view.closeDoors(); + view.moveLift(fromFloor, toFloor); // ride lift + view.openDoors(toFloor); + + pass.exitLift(); // leave lift + pass.end(); // walk out (to the right) + } +} diff --git a/lift/src/lift/Door.java b/lift/src/lift/Door.java new file mode 100644 index 0000000..d7058cd --- /dev/null +++ b/lift/src/lift/Door.java @@ -0,0 +1,25 @@ +package lift; + +class Door { + private boolean open; + + /** Create a new closed door */ + public Door() { + open = false; + } + + /** Returns true if door is open */ + public boolean isOpen() { + return open; + } + + /** Open the door */ + public void open() { + open = true; + } + + /** Close the door */ + public void close() { + open = false; + } +} \ No newline at end of file diff --git a/lift/src/lift/LiftMonitor.java b/lift/src/lift/LiftMonitor.java new file mode 100644 index 0000000..2cc02d9 --- /dev/null +++ b/lift/src/lift/LiftMonitor.java @@ -0,0 +1,175 @@ +package lift; + +import java.util.Arrays; + +enum Direction { + UP, + DOWN, +} + +/** Lift monitor. Handles lift motion and various state. */ +public class LiftMonitor { + /** the floor the lift is currently on */ + private int floor; + /** Number of floors */ + private int nfloors; + /** true if the lift is moving, false if standing still with doors open */ + private boolean moving; + /** Current direction of the lift */ + private Direction direction; + /** number of passengers waiting to enter the lift at the various floors */ + private int[] waitEntry; + /** number of passengers (in lift) waiting to leave at the various floors */ + private int[] waitExit; + /** number of passengers currently in the lift */ + private int load; + /** view object passed from main */ + private LiftView view; + /** State (open/closed) of door on current floor */ + private Door door; + /** Counter of number of passengers currently entering the elevator */ + private int passengersEntering; + /** Counter of number of passengers currently exiting the elevator */ + private int passengersExiting; + + /** Create new LiftMonitor */ + public LiftMonitor(LiftView v, int floors) { + floor = 0; + nfloors = floors; + moving = true; + direction = Direction.UP; + door = new Door(); + waitEntry = new int[nfloors]; + waitExit = new int[nfloors]; + load = 0; + view = v; + passengersEntering = 0; + passengersExiting = 0; + } + + /** Increases the number of waiting passengers on given floor */ + public synchronized void increaseWaitEntry(int passengerFloor) { + waitEntry[passengerFloor]++; + notifyAll(); + } + + /** Handles passengers waiting to enter arriving lift */ + public synchronized void enterLift(int passengerFloor, + int passengerDestination) { + while (floor != passengerFloor || load == 4 || moving || + passengersEntering == 4) { + try { + wait(); + } catch (InterruptedException e) { + throw new Error("Monitor.enterLift interrupted " + e); + } + } + waitEntry[floor]--; + waitExit[passengerDestination]++; + load++; + passengersEntering++; + notifyAll(); + } + + /** Called after passengers has completed entry animation */ + public synchronized void enterCompleted() { + passengersEntering--; + notifyAll(); + } + + /** Handles passengers waiting to exit lift */ + public synchronized void exitLift(int passengerDestination) { + while (floor != passengerDestination) { + try { + wait(); + } catch (InterruptedException e) { + throw new Error("Monitor.exitLift interrupted " + e); + } + } + waitExit[passengerDestination]--; + load--; + passengersExiting++; + notifyAll(); + } + + /** Called after passengers has completed exit animation */ + public synchronized void exitCompleted() { + passengersExiting--; + notifyAll(); + } + + /** + * Handles the conditions when the elevator is to wait for passengers + * entering/exiting the lift and waiting when there are no passengers waiting + * on any floor. + */ + public synchronized int[] liftContinue() { + + while (Arrays.stream(waitEntry).sum() == 0 && + Arrays.stream(waitExit).sum() == 0) { + try { + wait(); + } catch (InterruptedException e) { + throw new Error("LiftContinue no passengers interrupted " + e); + } + } + + /** Stops the elevator to allow passengers to enter or exit */ + while (waitEntry[floor] > 0 && load != 4 || waitExit[floor] > 0 || + passengersEntering > 0 || passengersExiting > 0) { + if (!door.isOpen()) { + view.openDoors(floor); + door.open(); + } + moving = false; + notifyAll(); + try { + wait(); + } catch (InterruptedException e) { + throw new Error("Monitor.liftContinue interrupted " + e); + } + } + + if (!moving) + view.closeDoors(); + + if (door.isOpen()) + door.close(); + + moving = true; + + calculateDirection(); + + /** + * Sends the elevators current position and next position to the LiftThread + */ + int[] movingPositions = new int[2]; + movingPositions[0] = floor; + movingPositions[1] = floor + (direction == Direction.UP ? 1 : -1); + return movingPositions; + } + + /** Moves the elevator in the given direction */ + public synchronized void incrementFloor() { + floor = floor + (direction == Direction.UP ? 1 : -1); + } + + /** + * Handles calculation of elevator direction + * Changes the direction of the elevator if there are no passengers that wants + * to enter or exit on floors either above or below the elevator + */ + private void calculateDirection() { + if (Arrays.stream(waitEntry, floor, nfloors).sum() == 0 && + Arrays.stream(waitExit, floor, nfloors).sum() == 0 && floor != 0) { + direction = Direction.DOWN; + } else if (Arrays.stream(waitEntry, 0, floor + 1).sum() == 0 && + Arrays.stream(waitExit, 0, floor + 1).sum() == 0 && floor != 6) { + direction = Direction.UP; + } else if (floor == nfloors - 1) { + direction = Direction.DOWN; + } else if (floor == 0) { + direction = Direction.UP; + } + } +} diff --git a/lift/src/lift/LiftThread.java b/lift/src/lift/LiftThread.java new file mode 100644 index 0000000..1ef0b66 --- /dev/null +++ b/lift/src/lift/LiftThread.java @@ -0,0 +1,20 @@ +package lift; + +/** Thread responsible for lift movement and rendering */ +public class LiftThread extends Thread { + private LiftView view; + private LiftMonitor monitor; + + public LiftThread(LiftView v, LiftMonitor m) { + view = v; + monitor = m; + } + + public void run() { + while (true) { + int[] elevatorPositions = monitor.liftContinue(); + view.moveLift(elevatorPositions[0], elevatorPositions[1]); + monitor.incrementFloor(); + } + } +} diff --git a/lift/src/lift/Passenger.java b/lift/src/lift/Passenger.java index e17b844..ae0b19d 100644 --- a/lift/src/lift/Passenger.java +++ b/lift/src/lift/Passenger.java @@ -1,21 +1,24 @@ package lift; public interface Passenger { - /** @return the floor the passenger starts at */ - int getStartFloor(); + /** @return the floor the passenger starts at */ + int getStartFloor(); - /** @return the floor the passenger is going to */ - int getDestinationFloor(); + /** @return the floor the passenger is going to */ + int getDestinationFloor(); - /** First, delay for 0..45 seconds. Then animate the passenger's walk, on the entry floor, to the lift. */ - void begin(); + /** + * First, delay for 0..45 seconds. Then animate the passenger's walk, on the + * entry floor, to the lift. + */ + void begin(); - /** Animate the passenger's walk from the entry floor into the lift. */ - void enterLift(); + /** Animate the passenger's walk from the entry floor into the lift. */ + void enterLift(); - /** Animate the passenger's walk out of the lift, to the exit floor. */ - void exitLift(); + /** Animate the passenger's walk out of the lift, to the exit floor. */ + void exitLift(); - /** Animate the passenger's walk, on the exit floor, out of view. */ - void end(); + /** Animate the passenger's walk, on the exit floor, out of view. */ + void end(); } diff --git a/lift/src/lift/PassengerThread.java b/lift/src/lift/PassengerThread.java new file mode 100644 index 0000000..543b0c9 --- /dev/null +++ b/lift/src/lift/PassengerThread.java @@ -0,0 +1,34 @@ +package lift; + +/** Responsible for a single passenger. */ +public class PassengerThread extends Thread { + private LiftView view; + private LiftMonitor monitor; + + public PassengerThread(LiftView v, LiftMonitor m) { + view = v; + monitor = m; + } + + /** Drives a passenger through the lift phases */ + public void run() { + while (true) { + Passenger p = view.createPassenger(); + p.begin(); + + // Walk towards the lift + monitor.increaseWaitEntry(p.getStartFloor()); + monitor.enterLift(p.getStartFloor(), p.getDestinationFloor()); + p.enterLift(); + + // Passenger is now inside the lift, now exit + monitor.enterCompleted(); + monitor.exitLift(p.getDestinationFloor()); + p.exitLift(); + + // Despawn + monitor.exitCompleted(); + p.end(); + } + } +}