How to design a multithreaded elevator system and handle concurrency? - python

I want to write a multithreaded program for an elevator system.
The elevator system can only move if the state is running or wait state.
In the system, the instruction (which floor to go to) can be added in a multithreaded way. For managing the queue of instruction and getting the next stop for the lift, I have written a QueueManager class.
I am not able to figure out how to implement the QueueManager. The problem is I can't get my head around writing the QueueManager. Especially in figuring out how to get the next stop for the Lift from the queue.
Here's my implementation for the system in python3:
"""Implementation of a lift System"""
from enum import Enum
from typing import TypeVar
from threading import Thread
import queue
LiftType = TypeVar('LiftType', bound='Lift')
QueueManagerType = TypeVar('QueueManagerType', bound='QueueManager')
SensorNameType = TypeVar('SensorNameType', bound='SensorName')
DirectionType = TypeVar('DirectionType', bound='DirectionType')
class SensorName(Enum):
weight = 'weight'
smoke = 'smoke'
class Direction(Enum):
up = 1
down = -1
class State(Enum):
running = 'running'
wait = 'waiting'
stop = 'stop'
class Lift:
def __init__(self, num_floors: int, queue_manager: QueueManagerType):
"""
Contains all the properties of lift
"""
self.num_floors = num_floors
self.curr_direction = Direction.up
self.state = State.running
self.curr_floor = 0
self.queue_manager = queue_manager
def move(self):
"""
Moves the lift according to the instruction
"""
if self.state in [State.running, State.wait]:
if self.queue_manager.has_instruction():
self.state = State.running
stop = self.queue_manager.next_stop(self.curr_direction, self.curr_floor)
if stop:
print(stop)
else:
if self.curr_direction == Direction.up:
self.curr_direction = Direction.down
stop = self.queue_manager.next_stop(self.curr_direction,
self.curr_floor)
print(stop)
else:
self.curr_direction = Direction.up
else:
self.state = State.wait
def add_instruction(self, floor, direction=None):
"""Adds the instruction to the queue"""
if direction is None:
if self.curr_floor > floor:
direction = Direction.down
else:
direction = Direction.up
Thread(target=self.queue_manager.add_instruction, args=(floor, direction)).start()
self.move()
class QueueManager:
def __init__(self):
self.instruction_queue = queue.Queue()
def add_instruction(self, floor: int, direction: DirectionType):
"""Add an instruction to the queue. Direction is used """
self.instruction_queue.put([floor, direction])
def next_stop(self, direction: int, curr_floor: int):
"""Tells which is the next stop based on the direction provided."""
pass
def has_instruction(self):
"""If there are any instructions for the lift"""
pass
if __name__ == '__main__':
# weight_sensor = WeightSensor(SensorName.weight)
instruction_queue = QueueManager()
lift_1 = Lift(21, instruction_queue)
lift_1.add_instruction(floor=0, direction=Direction.up)
lift_1.add_instruction(floor=2)
Extension to the above program is writing a Sensor class which runs in background and sets lift to stop state if certain sensor is triggered. Which is also something I am unable to figure out.

Based on the feedback I got in the comments. Here's a multithreaded solution for an elevator system.
"""Implementation of a lift System"""
from enum import Enum
from typing import TypeVar
from threading import Lock, Thread
from heapq import heappush, heappop
import time
import random
LiftType = TypeVar('LiftType', bound='Lift')
QueueManagerType = TypeVar('QueueManagerType', bound='QueueManager')
SensorNameType = TypeVar('SensorNameType', bound='SensorName')
DirectionType = TypeVar('DirectionType', bound='DirectionType')
class SensorName(Enum):
weight = 'weight'
smoke = 'smoke'
class Direction(Enum):
up = 1
down = -1
class State(Enum):
running = 'running'
wait = 'waiting'
stop = 'stop'
class Lift:
def __init__(self, num_floors: int):
"""
Contains all the properties of lift
:param sensors: A list of sensor objects
"""
self.num_floors = num_floors
self.curr_direction = Direction.up
self.state = State.running
self.curr_floor = 0
self.instruction_queue = {Direction.up: [], Direction.down: []}
self.queue_lock = Lock()
def move(self):
"""
Moves the lift according to the instruction
"""
if self.state in [State.running, State.wait]:
if len(self.instruction_queue[Direction.up]) or len(self.instruction_queue[Direction.down]):
self.state = State.running
try:
stop = heappop(self.instruction_queue[Direction.up])
except IndexError:
stop = -1
if stop > -1:
self.curr_floor = stop
print(stop, Direction.up)
else:
if self.curr_direction == Direction.up:
self.curr_direction = Direction.down
if len(self.instruction_queue[Direction.down]):
stop = heappop(self.instruction_queue[Direction.down])
self.curr_floor = stop
print(stop, Direction.down)
else:
self.state = State.wait
else:
self.curr_direction = Direction.up
self.move()
else:
self.state = State.wait
def add_instruction(self, floor, direction=None):
if direction == Direction.up:
time.sleep(random.uniform(0,1))
else:
time.sleep(random.uniform(0,4))
if direction is None:
if self.curr_floor > floor:
direction = Direction.down
else:
direction = Direction.up
with self.queue_lock:
heappush(self.instruction_queue[direction], floor)
self.move()
if __name__ == '__main__':
lift_1 = Lift(21)
lift_1.add_instruction(0, Direction.up)
Thread(target=lift_1.add_instruction, args=(2,)).start()
Thread(target=lift_1.add_instruction, args=(5,)).start()
Thread(target=lift_1.add_instruction, args=(2,)).start()
Thread(target=lift_1.add_instruction, args=(18,)).start()
Thread(target=lift_1.add_instruction, args=(12,)).start()
Thread(target=lift_1.add_instruction, args=(21,)).start()

Related

Why my enemy doesn't move when moving it? (Ursina)

I have this code. [enemy.py].
# Enemy behaviour.
# Entities.
from ursina import Entity
# Colors.
from ursina.color import red
# 3D Vectors.
from ursina import Vec3
# Capsule model.
from ursina import Capsule
# Random.
from random import randint, choice
# Sequences and tools for it.
from ursina.sequence import Sequence, Wait, Func
# Some enemy constants.
ENEMY_MODEL = Capsule() # NOTE (TODO): Maybe later can be replaced on new enemy 3D model.
ENEMY_SCALE = (2, 2, 2)
ENEMY_COLLIDER = 'mesh'
ENEMY_COLLISION = True
ENEMY_COLOR = red
ENEMY_SPAWN_POS = (16.328, 1.500, 16.773) # XXX: Generated by console.
# Enemy class.
class Enemy(Entity):
def __init__(self) -> None:
"""Enemy class."""
super().__init__(
model=ENEMY_MODEL,
scale=Vec3(ENEMY_SCALE),
collider=ENEMY_COLLIDER,
collision=ENEMY_COLLISION,
color=ENEMY_COLOR,
position=ENEMY_SPAWN_POS
)
self.ai_enabled = False
self.friendly = False
self.move_every_secs = 5
self.sequences = [
Sequence(
Func(self.simulate_moving),
Wait(self.move_every_secs),
loop=True
)
]
self.sequences_started = False
# Enemy update function.
def update(self):
if self.ai_enabled:
if not self.sequences_started:
for sequence in self.sequences:
sequence.start()
self.sequences_started = True
elif not self.ai_enabled:
if self.sequences_started:
for sequence in self.sequences:
sequence.pause()
self.sequences_started = False
def simulate_moving(self):
"""Simulate enemy moving."""
move_by = ['x', 'z']
move_by_random = choice(move_by)
move_on = randint(5, 10)
if move_by_random == 'x':
for _ in range(int(self.position.x + move_on)):
self.position.x += 1
elif move_by_random == 'z':
for _ in range(int(self.position.z + move_on)):
self.position.z += 1
self.ai_log('Moved.')
def enable_ai(self) -> None:
"""Enable enemy AI."""
self.ai_enabled = True
def disable_ai(self) -> None:
"""Disable enemy AI."""
self.ai_enabled = False
def set_friendly(self) -> None:
"""Make enemy friendly."""
self.friendly = True
def set_not_friendly(self) -> None:
"""Make enemy not friendly."""
self.friendly = False
def update_moving_per_secs(self, new_val: int) -> None:
"""Update moving activity per seconds."""
self.move_every_secs = new_val
def ai_log(self, message) -> None:
"""Create AI log into console."""
print(f'AI (Core) : {message}')
[game.py] (Not full code, but anyway that's what we need to know).
from enemy import Enemy
enemy = Enemy()
enemy.enable_ai()
And every 5 seconds it must move, but it's doesn't move at all.
Note, that function get called.
What to do?
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
Oh, i solved it.
We need to use self.x, not self.position.x.

Writing a test in python

Im currently taking a python class and im new in programming. I have written the code below and want to write a code that tests if the ResilientPlayer actually does what it is supposed to. The code is from a chutes and ladders board game where the ResilientPlayer is a "special" type of player that gets a "superpower" in its next move afther falling down a chute. The next round afther he has fallen down a chute, he will add a given or a default number to the die_roll, and I want to test if my code actually does this! Hope someone can help me with this problem :)
class Player:
def __init__(self, board):
self.board = board
self.position = 0
self.n_steps = 0
def move(self):
die_roll = random.randint(1, 6)
self.position = self.get_position() + die_roll
self.board.position_adjustment(self.position)
self.n_steps += 1
def get_position(self):
return self.position
def get_steps(self):
return self.n_steps
class ResilientPlayer(Player):
default_extra_steps = 1
def __init__(self, board, extra_steps=None):
super().__init__(board)
self.extra_steps = extra_steps
if self.extra_steps is None:
self.extra_steps = self.default_extra_steps
def move(self):
if self.get_position() in self.board.chutes.values():
die_roll = random.randint(1, 6)
self.position = self.get_position() + die_roll + self.extra_steps
self.board.position_adjustment(self.position)
self.n_steps += 1
else:
super().move()
def get_position(self):
return self.position
def get_steps(self):
return self.n_steps
The best way to do this is using the unittest class, I do this as following:
import unittest
from .... import ResilientPlayer
class TestResilientPlayer(unittest.TestCase):
def setUp(self):
self.resilient_player = ResilientPlayer(....)
def test_move(self):
# Do stuff
self.assertEqual(1, 1)
if __name__ == '__main__':
unittest.main()
Here, unittest.main() will run all the tests in the file. setUp is run before each test (so you can have multiple tests with the same starting conditions).
This is an incredible useful module and I strongly suggest reading more on it, check the documentation

How can I inherit QTableWidget and my class MainWindow?

First off I'm relatively new to python and very new to PyQt. I'm taking my first steps into object oriented programming and I have been looking at online tutorials, but I'm stuck on a multiple inheritance issue.
I have class called CreateTable() whose purpose is to make a QTableWidget in my app window and when a row is clicked it opens a context menu and you can then choose the option to graph the selected row. Where the graph option is connected to another class Plotter().
My problem is that CreateTable needs to inherit both the QTableWidget from PyQt and my class MainWindow because I need to reference the layout variable in order to embed my graph into my application window.
My code for attempted inheritance is as follows and heavily borrows from here:How does Python's super() work with multiple inheritance?
class QTable(QTableWidget):
def __init__(self):
super(QTable, self).__init__()
class PassMain(MainWindow):
def __init__(self):
super(PassMain, self).__init__()
class PassMainTable(PassMain, QTable):
def __init__(self):
super(PassMainTable, self).__init__()
The main problem is when I try to place the graph inside my MainWindow layout.
self.vboxRight.addWidget(self.Graph)
Here is my code for creating the table and calling plotter
class CreateTable(PassMainTable): #QTableWidget
def __init__(self, Data, row, col, colHeaders, rowHeaders): #Index, ColumnHeaders
super(CreateTable, self).__init__()
self.setSelectionBehavior(self.SelectRows)
print("Start initialization")
self.ColHeader = colHeaders
self.setRowCount(row)
self.setColumnCount(col)
self.data = Data
self.setHorizontalHeaderLabels(colHeaders)
print("Right before for loop")
n = len(Data)
m = len(colHeaders)
for i in range(n):
DataValues = self.data.iloc[i,:]
print("values are {}".format(DataValues))
#m = len(values)
ConvertedVals = pd.to_numeric(DataValues)
ValList = DataValues.values.tolist()
print(ValList)
for j in range(0,m):
self.item = QTableWidgetItem(str(round(ValList[j],5)))
#print("{}, {}".format(i, j))
self.setItem(i,j, self.item)
def contextMenuEvent(self, event):
menu = QMenu(self)
graphAction = menu.addAction("Graph")
compareAction = menu.addAction("Compare")
scatterAction = menu.addAction("Plot types")
aboutAction = menu.addAction("about")
quitAction = menu.addAction("quit")
printAction = menu.addAction("Print Row")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == quitAction:
qApp.quit()
elif action == printAction:
self.selected = self.selectedItems()
n = len(self.selected)
print("n is {}".format(n))
for i in range(n):
self.selected[i] = str(self.selected[i].text())
for i in range(n):
self.selected[i] = float(self.selected[i])
print(self.selected)
elif action == graphAction:
self.selected = self.selectedItems()
n = len(self.selected)
for i in range(n):
self.selected[i] = str(self.selected[i].text())
for i in range(n):
self.selected[i] = float(self.selected[i])
print("right before plotter called")
self.Graph = Plotter(self.selected, self.ColHeader)
self.vboxRight.addWidget(self.Graph)
else:
print("u clicked something other than quit")
To make matters worse PyQt is catching all my errors as exceptions so all I get for errors is "Process finished with exit code 1"
if you need further reference to my full code I have provided a link here: https://github.com/Silvuurleaf/Data-Analysis-and-Visualization-GUI/blob/master/Plotter3.1.py
Thank you I appreciate any help y'all can give me.
In order to share data it is not necessary to inherit from the widget, just use signals, this is the natural way in PyQt to share data asynchronously. In your case for example we will create a signal called dataSignal, according to what is observed of your code you want to use the variables self.selected, self.ColHeader, the first one is of type list, and the second numpy.ndarray so with that We will build the signal:
class CreateTable(QTableWidget): #QTableWidget
dataSignal = pyqtSignal(list, np.ndarray)
def __init__(self, Data, row, col, colHeaders, rowHeaders): #Index, ColumnHeaders
super(CreateTable, self).__init__()
self.setSelectionBehavior(self.SelectRows)
print("Start initialization")
self.ColHeader = colHeaders
self.setRowCount(row)
self.setColumnCount(col)
self.data = Data
self.setHorizontalHeaderLabels(colHeaders)
print("Right before for loop")
n = len(Data)
m = len(colHeaders)
for i in range(n):
DataValues = self.data.iloc[i,:]
print("values are {}".format(DataValues))
#m = len(values)
ConvertedVals = pd.to_numeric(DataValues)
ValList = DataValues.values.tolist()
print(ValList)
for j in range(0,m):
self.item = QTableWidgetItem(str(round(ValList[j],5)))
#print("{}, {}".format(i, j))
self.setItem(i,j, self.item)
def contextMenuEvent(self, event):
menu = QMenu(self)
graphAction = menu.addAction("Graph")
compareAction = menu.addAction("Compare")
scatterAction = menu.addAction("Plot types")
aboutAction = menu.addAction("about")
quitAction = menu.addAction("quit")
printAction = menu.addAction("Print Row")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action == quitAction:
qApp.quit()
elif action == printAction:
self.selected = self.selectedItems()
n = len(self.selected)
print("n is {}".format(n))
for i in range(n):
self.selected[i] = str(self.selected[i].text())
for i in range(n):
self.selected[i] = float(self.selected[i])
print(self.selected)
elif action == graphAction:
self.selected = self.selectedItems()
n = len(self.selected)
for i in range(n):
self.selected[i] = str(self.selected[i].text())
for i in range(n):
self.selected[i] = float(self.selected[i])
print("right before plotter called")
print(type(self.selected), type(self.ColHeader))
self.dataSignal.emit(self.selected, self.ColHeader)
else:
print("u clicked something other than quit")
Then in the MainWindow class we create a slot, in this create the Plotter object and add it to the layout.
def dataPlotter(self, x_data,y_data):
self.Graph = Plotter(x_data, y_data)
self.vboxRightBottom.addWidget(self.Graph)
To do this we connect the signal when we create the CreateTable object:
self.Table = CreateTable(self.BaseStats, row, col, colHeaders, rowHeaders)
self.Table.dataSignal.connect(self.dataPlotter)
self.vboxRightBottom.addWidget(self.Table)
The complete code is here.

My code can't find the file

I have written a program that has to execute a file and draw it in turtle.
But when I try to call a fdl filename it cant find it.
import turtle
#I start out making the basic turtle commands
def lt (turtle, n):
turtle.lt(n)
def fd (turtle, n):
turtle.fd(n)
def bk (turtle, n):
turtle.bk(n)
def rt (turtle, n):
turtle.rt(n)
#Then i create a class that makes it possible to create a set of rules
class Rule(object):
#Here the rule based on a description string is initialized like ("F -> F L F L")
def __init__(self,repr):
#This is where the left and right part of the rule in "F -> F L F L" comes in
self.left, self.right = [a.strip() for a in repr.split("->")]
if self.left is None or self.right is None:
print("Invalid rule description!")
#Now i use the same princip like i did in task6. the Apply function
def apply_rule_on_elements(self,element):
return [self._apply_rule_for_element (element) for element in elements]
#This is a helper function that only works on one element at a time
def _apply_rule_for_element (self,element):
if element == self.left:
return self.right.split()
return element
#Here is a very simple helper function, handy in some cases
#This allows one to perform assignment multiple values og grouping
def split_command(command, *args):
return command, args
#Now i make a command class that wraps the execution of a given command
class Command(object):
def __init__(self,command):
#the name and number of arguments for the given command
self.name,self.args = split_command(*command.split())
def execute(self,turtle,length):
if self.name == "lt":
lt(turtle,int(self.args[0]))
elif self.name == "scale":
length[0] = length[0]*float(self.args[0])
elif self.name == "fd":
fd(turtle,length[0])
elif self.name == "bk":
bk(turtle,length[0])
elif self.name == "rt":
rt(turtle,int(self.args[0]))
elif self.name == "nop":
pass
#Here i write the main Fractal class
class Fractal(object):
def __init__(self):
#Initial and current state
self.state = []
#Rules associated with the current fractal
self.rules = []
#Commands associated with the current fractal
self.commands = {}
#since values are immutable and passed by value, I use an array (passed by reference value)
#to allow the modification of the variable
self.length = [0]
#The current depth
self.depth = 0
#Executes the command associated w/ the current states stored in the fractal
def execute_commands(self,turtle,states):
for state in states:
self.commands[state].execute(turtle,self.length)
#Flattens a list
def _flatten(self,l):
flattened_list = []
for element in l:
flattened_list.extend(element)
return flattened_list
#Here i compute the fractal, which does that actual iteration work
#It returns the state of the fractal after the computation
def compute(self):
current_depth = self.depth
current_state = self.state
while self.depth !=0:
current_state=self.compute_next_state(current_state)
self.depth-=1
return current_state
def _compute_next_state(self,state):
for rule in self.rules:
state = rule.apply_rule_on_elements(state)
return self._flatten(state)
#This parses the fdl file, creates a fractal and set it up with the values
#read in the fdl file
def read_fdl(filename):
import os
f = Fractal()
if os.path.exists(filename):
lines = open(filename).readlines()
for line in lines:
if not len(line.strip())==0:
name,arguments = split_command(*line.strip().split())
if name == "start":
f.state = arguments
elif name == "rule":
f.rules.append(Rule("".join(arguments)))
elif name =="length":
f.length = [int(arguments[0])]
elif name == "depth":
f.depth = int(arguments[0])
elif name == "cmd":
f.commands[arguments[0]] = Command("".join (arguments[1:]))
else:
print("File does not exist")
#no check is made, to see if we have a fractal that was completely initialized
return f
import sys
import turtle
if len(sys.argv)>1:
f=read_fdl(sys.argv[1])
f.execute_commands(turtle,f.compute())
read_fdl("sierpinski")
The message I get is "File does not exist", but it does.
No, it doesn't exist - at least not in the current directory and without an extension.
Try adding the right directory and extension to the name "sierpinski"
Given that this is an fdl (fractal definition language) file, try:
read_fdl("sierpinski.fdl")

Python Unbound Method Error

Can someone please correct me on the mistake I have been having trouble with! The problem is that it is not generating a random integer in the Goalie nor Player class. This issue isn't allowing me to actually get both "players" to move.
Error:
import random
def main():
name = raw_input("Name: ")
kick = raw_input("\nWelcome " +name+ " to soccer shootout! Pick a corner to fire at! Type: TR, BR, TL, or BL! T = TOP B = BOTTOM L = LEFT R = RIGHT: ")
if Player.Shot == Goalie.Block:
print("\nThe goalie dives and blocks the shot!")
if Player.Shot != Goalie.Block:
print("\nThe ball spirals into the net!")
print(Player.Shot)
print(Goalie.Block)
class Player():
def __init__(self):
self.Shot()
def Shot(self):
shot = random.randint(0,3)
self.Shot = shot
class Goalie():
def __init__(self):
self.Block()
def Block(self):
block = random.randint(0,3)
self.Block = block
main()
you need to instantiate the class first::
try:
import random
def main():
name = raw_input("Name: ")
kick = raw_input("\nWelcome " +name+ " to soccer shootout! Pick a corner to fire at! Type: TR, BR, TL, or BL! T = TOP B = BOTTOM L = LEFT R = RIGHT: ")
player=Player() #error fixed:<-- called the class
goalie=Goalie() #error fixed:<-- called the class
if player.shot == goalie.block:#error fixed:<-- used that initiated class
print("\nThe goalie dives and blocks the shot!")
if player.shot != goalie.block:
print("\nThe ball spirals into the net!")
print(player.shot)
print(goalie.block)
class Player:
def __init__(self):
self.Shot()
def Shot(self):
shot = random.randint(0,3)
self.shot = shot #error fixed:<-- attribute and method are same
class Goalie:
def __init__(self):
self.Block()
def Block(self):
block = random.randint(0,3)
self.block = block #error fixed:<-- attribute and method are same
main()

Categories