This is a attempt to make a more modern and efficient design of elevator system compare to my old https://github.com/quangIO/Elevator. The older one uses multi-threaded, inefficient, "eager" approach. An event loop keep tracking the request queue to see if there exists some requests need to be sent to elevators. (Lock based, CPU hungry, and waste of electricity)
The idea is based on Actor model. There are ElevatorActors and a ElevatorControllerActor. The messages are passed from Elevators to the centralized ElevatorController (MainVerticle
) by using VertX's event bus (just for convenience, you can just use Rx's PublishBehavior or built-in Kotlin's Coroutine features like channel
to implement it).
id
self-explanatorytimeToReach
time to move from floori
toi+1
or other way aroundpickUpTime
time to pickupvertx
hold the vertx instance for event bus and some other utilities
Each elevator accepts the following commands from MainVerticle
: moveUp
, moveDown
, and pickUp
. There is a timer that simulates the process. The isMoving
variable is atomic, helping to check the current state of an elevator. It will be true
if the elevator is picking up something or moving between 2 floors.
When an elevator reached a floor, it notifies its current state to the controller.
Tl;dr: Elevators basically do not anything. It just follows the order dispatched from MainVerticle
and updates its state.
This holds all the logic of the system, including outsideRequests
(all requests from outside of the elevator), insideRequests
(similarly), elevatorStatuses
(current states of elevators), elevators
(references to each elevator), and requestQueue
(for fairness purpose - TODO: not optimized yet).
All those data should be thread-safe. For the simplicity, I use built-in concurrent data structures with naive filters. However, in real production, an in-memory should be used (it is simpler to code actually).
- When it received an outside request: if it is moving up, it will try to reach the highest floor, and the same goes for moving down. The cost to select the elevator is calculated as
abs(toFloor - request.atFloor)
wheretoFloor
is where the elevator is heading andrequest.atFloor
is where the request comes from. In case we cannot find an elevator to process a request, we add it torequestQueue
- When it received an inside request: similar to outside request but with specific elevator ID, so it is a lot simpler.
- When elevators' states changed
- If should pick up:
pickup
- If already doing something,
return
- If
toFloor > atFloor
: move up to reachtoFloor
- else if
toFloor < atFloor
: move down to reachtoFloor
- else (it is idling): find something to do (try to empty
requestQueue
,in|outsideRequests
)
- If should pick up:
./gradlew shadowJar
java -jar build/libs/*.jar
or
Open import with IntelliJ Idea and run the main function
The server should start and we can use this dashboard to try the simulation. To change the number of elevators, change val n = [number of elevators here]
in the MainVerticle.kt
(101 floors)
Likely. I would be very appreciated if you point out my errors.