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