Simulator of elevators written in Python using PyQt5 as GUI. The structure of elevators is defined in JSON file. This simulator is designed for practising of programming automata.
- Clone or download this repository:
git clone https://github.com/bsaid/ElevatorSimulator.git
- Install Python3 if you do not have it.
- Install the required Python packages:
pip3 install PyQt5
- Run the example.py:
python3 ./example.py
- A new window with the simulation should appear. After clicking the
Start
button you should see something like this:
The structure of elevators is defined in JSON format. The JSON file defines the number of elevators, the number of floors, where each elevator stops and what buttons are used to control the elevators. Here is a simple example for one elevator:
{
"buttons": [],
"elevators": [
{
"id": "Alfa",
"floors": [-1,0,1,2],
"maxSpeed": 1.0,
"speedStep": 0.05
}
]
}
The result:
We can define more complex structure in the JSON file. Please notice that the elevator does not have to be able to stop in every floor:
{
"buttons": [],
"elevators": [
{ "id": "A", "floors": [-1,0,1,2], "maxSpeed": 1.0, "speedStep": 0.05 },
{ "id": "B", "floors": [0,1,2], "maxSpeed": 1.0, "speedStep": 0.05 },
{ "id": "C", "floors": [-1,1,2], "maxSpeed": 1.0, "speedStep": 0.05 },
{ "id": "D", "floors": [-1,0,1], "maxSpeed": 1.0, "speedStep": 0.05 },
{ "id": "E", "floors": [0,2], "maxSpeed": 1.0, "speedStep": 0.05 },
{ "id": "F", "floors": [-1,2], "maxSpeed": 1.0, "speedStep": 0.05 }
]
}
The result:
The JSON file also contains a list of all buttons. Currently, we will assign only id
for each button, but we can also set their color, caption, and where they are displayed in the simulation scene, we can also group the buttons for better organisation in the simulator (the part od the id before the ':' symbol is the group) events are passed to the code without the group, so you can have one button in multiple groups (pressing the button with id
"group:move" will pass the event "move", so will the id
"group2:move"):
{
"buttons": [
{"id" : "group1:Up"},
{"id" : "group1:Down"},
{"id" : "Stop"}
],
"elevators": [
{ "id": "A", "floors": [0,1], "maxSpeed": 1.0, "speedStep": 0.05 }
]
}
The result:
The user code is a Python3 file that needs to import elevators
, and then it has to call elevators.runSimulation(configFileName, elevatorSimulationStep)
where configFileName
is the name of the elevators.json
file and def elevatorSimulationStep(e)
is a procedure that needs to be implemented by user and that is called inside the simulation loop. The parameter e
is a class with the current information about all the elevators and it also contains API to interact with the elevators during the simulation.
import elevators
def elevatorSimulationStep(e):
if (e.getTime()+10) % 40 < 20:
e.speedUp('Alfa')
else:
e.speedDown('Alfa')
elevators.runSimulation('example4.json', elevatorSimulationStep)
JSON configuration:
{
"buttons": [],
"elevators": [
{"id": "Alfa", "floors": [0,1], "maxSpeed": 1.0, "speedStep": 0.1}
]
}
import elevators
class GlobalData:
alfaDirection = 1
deltaDirection = 0
doorsDirection = 1
def processEvents(e):
while e.numEvents() > 0:
event = e.getNextEvent()
if event == 'DeltaUp':
GlobalData.deltaDirection = 1
elif event == 'DeltaDown':
GlobalData.deltaDirection = -1
elif event == 'DeltaStop':
GlobalData.deltaDirection = 0
else:
print('Unknown event.')
def processAlfa(e):
if e.getSpeed('Alfa') > 1.9:
GlobalData.alfaDirection = -1
if e.getSpeed('Alfa') < -1.9:
GlobalData.alfaDirection = 1
if GlobalData.alfaDirection == 1:
e.speedUp('Alfa')
else:
e.speedDown('Alfa')
def processDelta(e):
if GlobalData.deltaDirection == 1:
e.speedUp('Delta')
elif GlobalData.deltaDirection == -1:
e.speedDown('Delta')
else:
speed = e.getSpeed('Delta')
if speed > 0:
e.speedDown('Delta')
elif speed < 0:
e.speedUp('Delta')
def processDoors(e):
if e.getDoorsPosition('Delta', 0) > 0.9:
GlobalData.doorsDirection = -1
elif e.getDoorsPosition('Delta', 0) < 0.1:
GlobalData.doorsDirection = 1
if GlobalData.doorsDirection == 1:
e.openDoors('Delta', 0)
else:
e.closeDoors('Delta', 0)
def printTelemetry(e):
print(e.getSpeed('Alfa'), e.getDoorsPosition('Delta', 0))
def elevatorSimulationStep(e):
processEvents(e)
processAlfa(e)
processDelta(e)
processDoors(e)
printTelemetry(e)
configFileName = 'elevators.json'
elevators.runSimulation(configFileName, elevatorSimulationStep)
JSON configuration:
{
"buttons": [
{"id" : "DeltaUp"},
{"id" : "DeltaDown"},
{"id" : "DeltaStop"}
],
"elevators": [
{"id": "Alfa", "floors": [0,1,2], "maxSpeed": 2.0, "speedStep": 0.2},
{"id": "Delta", "floors": [0,2], "maxSpeed": 0.5, "speedStep": 0.05}
]
}
The result:
The elevator simulator implements a passenger system, which allows you to test your elevator code with passengers. Passengers interact with your code through events. For the passengers to know which event to send when, you must create a getPassengerEvent(data)
function, which you pass as another argument to elevators.runSimulation
.
The getPassengerEvent(data)
must take one argument, data
and return the event that should be passed to the elevator code. The data
argument contains data about where the passenger is, where the passenger wants to go, and, if the passenger is in an elevator, which elevator the passenger is in. The data
argument is a dictionary in this form:
{
'isInElevator': True/False,
'elevator': ID,
'currentFloor': CURRENT_FLOOR,
'targetFloor': TARGET_FLOOR
}
Here is an example of a getPassengerEvent
function:
def getPassengerEvent(data):
if data['isInElevator']:
return "call elevator " + data['elevator'] + " to floor " + data['targetFloor']
else: # passenger is waiting for an elevator in a floor
return "call to floor " + data['currentFloor']
elevators.runSimulation(configFileName, elevatorSimulationStep, getPassengerEvent)
Once this function is passed to the elevators.runSimulation
function, passengers will start appearing (and, if your elevator code works, using the elevators) and you will be able to use the UI to set how many passengers will be arriving, aswell as see the average transport time.
You can check wether a passenger is in the doors of an elevator with the e.doorSensor(id)
function.
This question is the goal for the users. The goal of this application is to provide a simulated environment for experiments and practice of programming automata.
-
e.getConfig()
- Return the whole configuration as Python dictionary. -
e.addEvent(eventText)
- Creates a new event witheventText
string. -
e.getAllElevators()
- Returns a list of string IDs of all elevators. -
e.getDescription(id)
- Returns a dictionary of all parametrs for given elevator ID. -
e.getPosition(id)
- Returns position of the given elevator as float. Integers represent that the elevator is exactly in given floor. Decimal numbers represent positions between floors. -
e.getSpeed(id)
- Returns the current speed of the given elevator. Positive number represents climbing direction. Number 1.0 represents a speed of 1.0 floors per second. -
e.speedUp(id)
- Increases speed of the given elevator. If the elevator is descending (has negative speed), this negative speed is decreased. -
e.speedDown(id)
- Opposite function toe.speedUp(id)
. -
e.numEvents()
- Returns the number of events in the queue. -
e.getNextEvent()
- Returns the string of the first event in the queue and removes this event from the queue. -
e.getDoors(id)
- Returns a list of float numbers representing all door positions for the given elevator. Zero means closed doors, one means fully opened doors. -
e.openDoors(id, floor)
- Start or keep opening of the doors for given elevator at given floor. This function must be called repeatedly until the doors are fully opened. -
e.closeDoors(id, floor)
- Oposite function toe.openDoors(id, floor)
. -
e.getDoorsPosition(id, floor)
- Returns position of the doors for given elevator at given floor. Zero means closed doors, one means fully opened doors. -
e.doorSensor(id)
- ReturnTrue
if a passenger is in the doors of an elevator, use this to avoid closing elevator doors when a passenger is entering/leaving an elevator. -
e.getTime()
- Returns integer that says how many times the functionelevatorSimulationStep(e)
(defined by user) was called.