ProjektAI/kelner/src/managers/WaiterManager.py

264 lines
12 KiB
Python

import threading
import time
import sys
from math import sqrt
from kelner.src.components.Table import Status
from kelner.src.algorithms.AStar.Finder import Finder
from kelner.src.algorithms.BFS.BFS import BFS
from kelner.src.components.Table import Status
from kelner.src.algorithms.CNN.ModelPrediction import Predictor
# creates new thread
class WaiterManager(threading.Thread):
def __init__(self, drawableManager, waiters, kitchen_manager, menu_manager, table_manager):
super().__init__()
self.__drawableManager = drawableManager
self.__waiters = waiters
self.__runThread = True
self._kitchen_manager = kitchen_manager
self._menu_manager = menu_manager
self._table_manager = table_manager
def __getDistance(self, waiter, tupleXY):
dx = waiter.getX() - tupleXY[0]
dy = waiter.getY() - tupleXY[1]
return sqrt(dx * dx + dy * dy)
def __sortTargets(self, waiter, targets):
return sorted(targets, key=lambda target: self.__getDistance(waiter, (target[0], target[1])))
def __getTargets(self, waiter, finder):
found = []
tables = self.__drawableManager.getTables(Status.Ready)
if tables:
origin = (waiter.getX(), waiter.getY())
for table in tables:
if table.hasOrder():
targets = finder.getNeighbours((table.getX(), table.getY()), False)
for target in targets:
if target == origin:
return []
else:
found.append(target)
return self.__sortTargets(waiter, found)
def __getNearestTargetPath(self, waiter, targets=None):
distance = sys.maxsize
nearestTargetPath = None
reservedPlaces = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reservedPlaces)
origin = (waiter.getX(), waiter.getY())
if targets is None:
targets = self.__getTargets(waiter, finder)
for target in targets:
if distance > self.__getDistance(waiter, target):
path = finder.getPath(origin, target, True)
if path:
result = len(path)
if result < distance:
distance = result
nearestTargetPath = path
return nearestTargetPath
def get_specified_path(self, waiter, table):
distance = sys.maxsize
nearestTargetPath = None
reserved_places = self.__drawableManager.getReservedPlaces(waiter)
finder = Finder(reserved_places)
origin = (waiter.getX(), waiter.getY())
targets = finder.getNeighbours((table[0], table[1]), False)
for target in targets:
if distance > self.__getDistance(waiter, target):
path = finder.getPath(origin, target, True)
if path:
result = len(path)
if result < distance:
distance = result
nearestTargetPath = path
return nearestTargetPath
def __changeWaiterDirection(self, waiter, x, y):
targetDirection = x - waiter.getX(), y - waiter.getY()
originDirection = waiter.getDirection()
while originDirection is not None:
originDirection = waiter.getNextDirection(originDirection, targetDirection, True)
if originDirection is not None:
time.sleep(0.3)
waiter.setDirection(originDirection[0], originDirection[1])
self.__drawableManager.forceRepaint()
def __moveWaiter(self, waiter, x, y):
time.sleep(0.4)
self.__drawableManager.moveWaiter(waiter, x, y)
self.__drawableManager.forceRepaint()
def __collectOrder(self, waiter):
doCollectOrder = True
while doCollectOrder and waiter.get_state():
tables = self.__drawableManager.getNearestTables(waiter, Status.Ready)
turns = sys.maxsize
lessTurnsTable = None
originDirection = waiter.getDirection()
for table in tables:
targetDirection = table.getX() - waiter.getX(), table.getY() - waiter.getY()
result = Finder.getTurnsCount(originDirection, targetDirection, True)
if result < turns:
turns = result
lessTurnsTable = table
if lessTurnsTable is not None:
tables.remove(lessTurnsTable)
self.__changeWaiterDirection(waiter, lessTurnsTable.getX(), lessTurnsTable.getY())
# order = lessTurnsTable.getOrder()
order = lessTurnsTable.get_order()
if order is not None and waiter.get_state:
waiter.addOrder(lessTurnsTable, order)
time.sleep(2)
lessTurnsTable.setStatus(Status.Waiting)
self.__drawableManager.forceRepaint()
doCollectOrder = len(tables) > 0
def pass_orders_to_kitchen(self, orders):
kitchen = self.__drawableManager.get_kitchen()
self._kitchen_manager.pass_orders(orders, kitchen)
def receive_ready_orders(self, waiter):
kitchen = self.__drawableManager.get_kitchen()
try:
ready_orders = self._kitchen_manager.get_ready_orders(kitchen)
for dish in ready_orders:
print("Orders ready to take", end=" ")
print(dish[1])
except IndexError as e:
print("No orders")
waiter.make_ready()
waiter.clear_accepted_orders()
print("Ready to go")
return ready_orders
def predict_order(self, order, kitchen):
food_list = self._menu_manager.get_menu()
image_predictor = Predictor(food_list)
paths = self._kitchen_manager.draw_single_order(order, kitchen)
predicted_dishes = []
self._kitchen_manager.run()
self._table_manager.pause()
# print("Printed images paths: {}".format(paths))
if paths is not None:
for img_path in paths:
predicted_dish = image_predictor.predict_class(img_path)
predicted_dishes.append(predicted_dish)
time.sleep(2)
self.__drawableManager.forceRepaint()
return predicted_dishes
def match_predicted_orders(self, predicted_orders):
all_tables = self.__drawableManager.getTables(Status.Waiting)
located_orders = dict()
for p_order in predicted_orders:
p_order.sort()
for table in all_tables:
table_dishes = table.get_order()
table_dishes.sort()
if p_order == table_dishes:
located_orders[table] = p_order
return located_orders
def get_order_targets(self, orders):
targets = dict()
for table, dishes in orders.items():
targets[table.get_posistion()] = dishes
return targets
def set_next_waiter_targets(self, waiter, matched_targets):
for table in matched_targets.keys():
path = self.get_specified_path(waiter, table)
waiter.add_remaining_target(path)
def set_next_waiter_positions(self, waiter, matched_positions):
for position in matched_positions.keys():
waiter.add_remaining_position(position)
# changes the status of a random table from NotReady to Ready
def run(self):
while self.__runThread:
if self.__waiters:
for waiter in self.__waiters:
if len(waiter.getAcceptedOrders()) > 1:
waiter.set_target('kitchen')
path = self.__getNearestTargetPath(waiter, [(14, 1), (13, 0)])
waiter.make_busy()
else:
if waiter.get_target() == 'return_order' and waiter.isPathEmpty():
print("Order returned")
table = self._table_manager.get_table(waiter.get_remaining_positions()[0])
table.setStatus(Status.Served)
self.__changeWaiterDirection(waiter, table.getX(), table.getY())
waiter.get_remaining_positions().pop(0)
time.sleep(2)
if not waiter.get_remaining_positions() == []:
waiter.set_target('return_order')
path = self.get_specified_path(waiter, waiter.get_remaining_positions()[0])
else:
waiter.set_target('table')
path = self.__getNearestTargetPath(waiter)
waiter.setPath([] if path is None else path)
if not waiter.isPathEmpty():
step = waiter.popStepFromPath()
self.__changeWaiterDirection(waiter, step[0], step[1])
self.__moveWaiter(waiter, step[0], step[1])
# check if waiter is near kitchen
if (waiter.getX(), waiter.getY()) == (14, 1) or (waiter.getX(), waiter.getY()) == (13, 0):
if waiter.get_target() == 'kitchen':
self._table_manager.pause()
kitchen = self.__drawableManager.get_kitchen()
self.__changeWaiterDirection(waiter, kitchen.getX(), kitchen.getY())
waiter_orders = waiter.getAcceptedOrders()
# print("Waiter near kitchen. Collected orders: {}".format(waiter_orders))
kitchen.add_orders(waiter_orders)
received_orders = kitchen.get_ready_orders()
waiter.clear_accepted_orders()
if received_orders:
predicted_orders = []
for order in received_orders:
# get predicted dishes
predicted_dishes = self.predict_order(order, kitchen)
predicted_orders.append(predicted_dishes)
# check if all predicted orders were really ordered
matched_targets = self.match_predicted_orders(predicted_orders)
# get positions to matched orders (X,Y): dish
order_positions = self.get_order_targets(matched_targets)
self.set_next_waiter_positions(waiter, order_positions)
# set new ready paths to waiter
# self.set_next_waiter_targets(waiter, matched_targets)
self._kitchen_manager.stop()
self._table_manager.resume()
kitchen.clear_orders()
waiter.make_ready()
waiter.set_target('table')
elif waiter.get_target() not in ['return_order', 'check']:
self.__collectOrder(waiter)
def stop(self):
self.__runThread = False
def resume(self):
self.__runThread = True