[Home] [Puzzles & Projects] [Delphi Techniques] [Math topics] [Library] [Utilities]
This is version 1 of an elevator
simulator. It implements manual control of up to 6 elevators traveling
across 2 to 8 floors with up/down Call buttons outside of the elevators on each
Floor Destination buttons inside each car.
Background & Techniques
I recently help an elevator company convert some real elevator control routines written in Basic to Delphi. That wasn't very exciting in itself, but it did lead me to finally tackle one of the programs on my "life objectives" list - an elevator simulation.
The logic required to control elevators are more complex than it seems on the surface, particularly the problem of responding to calls when there are multiple elevators to choose from. Should we closest the closest one? Probably not if it is moving in the opposite direction from the call button direction. What if an idle elevator is one floor away, but there is a moving elevator two floors away that will pass this floor anyway? For this version I've deferred addressing these issues and created a scheduler that assigns the first (lowest numbered) idle elevator to floor calls.
An earlier posting, Threadstest1, introduced simple "elevator-like" objects that used Delphi's threading capabilities to allow their independent operation. This program expands that start by adding call and elevator buttons and a scheduler to assign an elevator to a call button push. Each elevator had to become "smarter" in order to detect and handle floor destination buttons (the buttons inside the elevator) and floor call buttons (the buttons outside the elevators) that the scheduler had assigned to it.
Each elevator is represented by a TElevator descendant of TThread. Each has two arrays with entries for each floor; Stops entries are of type TCallrec, a record indicating which call button direction (up, down, or none), and a pointer to the TCallBtn object itself (so we can turn the button off when we handle the call). FloorBtns is a Boolean array with true entries for each destination button that was pressed from inside the elevator.
In Threadstest1, the Execute method for each elevator suspended it's execution when idle. There is probably no real advantage to this. The current implementation periodically (10 times per second) checks the button arrays to determine if we have any pending requests and never suspends itself.
A timer, Timer1, is used to invoke the Scheduler procedure, also 10 times per second. Scheduler checks for pending elevator call button pushes and assigns them to the Stops array of an idle elevators. This method, rather than giving the call button click the responsibility, solves the problem of what to do if all elevators are busy.
Timer1 also has responsibility for closing open elevator doors after it has been open for DefaultDoorOpenTime (currently set at 5 seconds) . Logically, closing the door should be the elevator's job, and may be in the next version. The problem is that a common panel control, ElPanel, is used to show the internal elevator buttons when the user clicks on an elevator and all of this logic; the elevator click, making the control panel visible, and keeping track of which elevator was clicked is all known to the form but not to the elevators. If manual control disappears in the next version then the need for an external control panel will display will go away, and the door closing job will be moved back to the elevator.
There are other nooks and crannies in the code, but the above outline the main elements. You may have the fun of exploring the rest on your own!
Running/Exploring the Program
Suggestions for further exploration
Copyright © 2000-2018, Gary Darby All rights reserved.