System managing multiple elevators in a building that use the same control panel.
- Install modules with
npm i
. - Web GUI can be viewed from dev server launched with
npm run start
. - Jest unit tests can be launched with
npm run test
.
Alternatively, website build is also deployed on GitHub Pages: https://ignis05.github.io/elevator-system
- Elevator algorithm - generic interface in
src/services/ElevatorSystem.ts
with implementation class insrc/services/ElevatorManager.ts
- React component with GUI -
src/components/ElevatorDemoComponent/ElevatorDemoComponent.tsx
- minimalistic web app for manually interacting with the algorithm. - Unit tests -
src/services/ElevatorManager.test.ts
andsrc/services/ElevatorSystem.test.ts
- few generic tests for the interface and much more thorough tests for the implementation-specific behaviour. - GitHub Action workflow -
.github/workflows/gh-pages_deploy.yml
- automatically builds, runs tests, and deploys the app on GitHub Pages whenever commits are pushed to the master branch.
- The system operates on two types of elevator jobs:
pickup tasks
anddropoff destinations
. - The system holds a shared list of
pickup tasks
, where each task consists of a floor and a direction. - Each elevator holds its own list of
dropoff destinations
, which are just numbers of floors selected on its internal panel. - Each elevator can have a single assigned
pickup task
, which is removed from the shared pool and stored in the elevator itself. This prevents elevators from completing tasks assigned to other elevators, for example sending 3 elevators from floor 0 to floors 1,2,3, without having the one going to floor 3 also "steal" pickups from floors 1 and 2. - Each elevator can be in one of 3 states:
idle
,moving
orstopped
.idle
means that the elevator has nothing to do,moving
that it's currently moving between floors andstopped
that it is stopped on a floor, but still has tasks to do. - In each algorithm
step
, each elevator can move one floor up or down. When an elevator arrives at a floor where it has apickup
or adropoff
, it will becomestopped
for a singlestep
. After that step, it either becomesmoving
oridle
, depending on whether it has more floors that it must visit or not. - The system assigns new
pickups
toidle
elevator only. This ensures each elevator completes alldropoffs
before being sent to pick up more people. - When simulating people entering the elevator and choosing floor, the selection must be made while the elevator is
stopped
at that floor. Otherwise, the system assumes, that no-one actually entered, or no selection was made in time, so the elevator becomes idle in the nextstep
and might get assigned to anotherpickup
. - The system assigns
pickup tasks
, based on "first-come first-serve" and elevator distance. When a new task appears, it gets assigned to the closestidle
elevator, or it waits in the shared pool until an elevator becomesidle
. Once atask
is assigned, it will not get reassigned to another elevator. This avoids "starvation issue", where for example no elevator reaches floor 20 for a while, because they are all near the bottom and keep getting reassigned to closer tasks that keep appearing. - Despite assignment working on a simple FCFS, elevators will stop to complete other
pickup tasks
from the shared pool at any floor they pass, however certain conditions must be met:- The
task
can't be assigned to another elevator, as that removes it from the publictask
pool. - The
task
's declared direction must match the elevator's current move direction. Ex: elevator going from 1 to 5 won't pick up a taskfloor 3 - down
, at least not until it finishes its jobs or comes back around on its way down. - If the elevator is going to a
pickup task
, thetask
it passes must also match the elevator'stask
's' direction. Ex: Elevator going from floor 0 to taskfloor 8 - down
won't pick up taskfloor 5 - up
, because there's no guarantee that floor 5 doesn't want to go higher than 8. - An exception to the above rule can be made with an optional constructor parameter, or by using a
setFloorLimits
method, which limits the range of floors (by default, elevators can be called or sent to any floor within JavaScript'snumber
type limits). This allows an elevator to pick up afloor 5 - up
task, while going tofloor 8 - down
when it knows that floor 8 is the top floor and there won't be a conflict between passenger from 5 and passenger on 8. It works in the same way for bottom floorpickups
.
- The
- Each elevator prefers to travel in the direction matching the declared direction of
pickup
it completed. If an elevator receives bothdestinations
1 and when stopped completing pickupfloor 3 - up
, it will go to floor 10, despite floor 3 being closer, because that matches the direction declared bypickup task
. This incentivizes people to wait for an elevator going in their declared direction, instead of entering the first one that arrives. - After an elevator has completed all
dropoffs
in its direction, its direction will be flipped if there are any in the opposite one and will keep it as newdropoffs
are declared. This ensures the elevator switches its movement direction as rarely as possible, so it is more likely to pass through and be able to complete morepickup tasks
, than if it was jiggling up and down between a couple floors. - If both
up
anddown
are selected for a single floor, the system is likely to complete that using two separate elevators (unless all except one are being really busy for a long time). This assumes people will know they aren't all supposed to enter the first elevator that arrives and wait for the elevator in their direction instead. - Alternatively, a method
setSoleElevatorMode
can be used to change this behaviour and allow each elevator to complete allpickups
from all floors it passes. This might be a preferable solution in case the system controls a building with one sole elevator, where it might be weird to expect people to not enter the elevator and wait for it to come back around. This method is not present on the interface, as it's assumed it will be set between creating the class instance and passing it to the interface, as it's very unlikely this will need to be changed during system's work. - The system does not try to predict when an elevator will become idle. For example: there's pickup task
floor 10 - down
and system has two elevators: elevator1idle
at floor 0 and elevator2 at floor 13 moving to its one remainingdestination
at floor 11. The system could optimistically predict that elevator2 will be able to finish itsdropoff
at 11, and still get to thepickup
at 10 faster than elevator1 going all the way from floor 0. But if someone from elevator2 presses another button, or someone new enters on floor 11, it's likely that thepickup
will have to be completed by elevator1 anyway, but after a considerable delay in expectation of elevator2 being able to complete it. - Implementation specifics:
- The generic interface is named
ElevatorSystem
, while its implementation class is namedElevatorManager
. There's also a couple other simple helper interfaces and declared types, as well as anElevator
class responsible for a fair share of the algorithm. Elevator
class is responsible for individual elevator's position, direction, choosing next destination, handling state change and completingdropoffs
and assignedpickup
by just callingElevator#moveFloor()
for each elevator once everystep
. Assigning newpickups
to idle elevators and clearingpickups
from shared pool as elevators pass them is done in loops insideElevatorManager#step()
.- Interface methods return clones of objects instead of direct references, to avoid accidentally interfering with the system by accidentally modifying objects passed by reference.
- The generic interface is named