diff --git a/ForkliftAgent.py b/ForkliftAgent.py index b2bf5f0..6393746 100644 --- a/ForkliftAgent.py +++ b/ForkliftAgent.py @@ -1,8 +1,10 @@ +from copy import deepcopy from typing import Tuple, List from AgentBase import AgentBase from PatchAgent import PatchAgent from PatchType import PatchType +from data.GameConstants import GameConstants from data.Item import Item from data.Order import Order from data.enum.Direction import Direction @@ -16,7 +18,7 @@ from util.PathDefinitions import GridLocation, GridWithWeights class ForkliftAgent(AgentBase): - def __init__(self, model, game_constants, client_delivery: PatchAgent, drop_off: PatchAgent, + def __init__(self, model, game_constants: GameConstants, client_delivery: PatchAgent, drop_off: PatchAgent, graph: GridWithWeights): super().__init__(model) self.action_queue: List[Action] = [] @@ -28,11 +30,20 @@ class ForkliftAgent(AgentBase): self.drop_off: PatchAgent = drop_off self.graph = graph self.game_constants = game_constants - self.current_order: Order = None - self.current_item = None self.item_station_completed = False - self.provided_items: List[Item] = [] + self.current_order_delivered_items: List[Item] = [] self.ready_for_execution = False + self.last_delviered_item = None + + self.current_item: Item = None + self.current_order = None + self.base: GridLocation = None + self.goal: GridLocation = None + + def set_base(self, drop_off: PatchAgent): + self.drop_off = drop_off + self.base = self.drop_off.location + self.goal = self.base def queue_movement_actions(self, movement_actions: List[Action]): self.action_queue.extend(movement_actions) @@ -43,120 +54,94 @@ class ForkliftAgent(AgentBase): action_type = action.action_type if action_type == ActionType.ROTATE_UP: - print("rotate {} --> {}".format(self.current_rotation, action_type)) + # print("rotate {} --> {}".format(self.current_rotation, action_type)) self.current_rotation = Direction.top elif action_type == ActionType.ROTATE_RIGHT: - print("rotate {} --> {}".format(self.current_rotation, action_type)) + # print("rotate {} --> {}".format(self.current_rotation, action_type)) self.current_rotation = Direction.right elif action_type == ActionType.ROTATE_DOWN: - print("rotate {} --> {}".format(self.current_rotation, action_type)) + # print("rotate {} --> {}".format(self.current_rotation, action_type)) self.current_rotation = Direction.down elif action_type == ActionType.ROTATE_LEFT: - print("rotate {} --> {}".format(self.current_rotation, action_type)) + # print("rotate {} --> {}".format(self.current_rotation, action_type)) self.current_rotation = Direction.left elif action_type == ActionType.MOVE: if self.current_rotation == Direction.top: - print("move {} --> {}".format(self.current_position, action_type)) + # print("move {} --> {}".format(self.current_position, action_type)) self.current_position = (self.current_position[0], self.current_position[1] + 1) elif self.current_rotation == Direction.down: - print("move {} --> {}".format(self.current_position, action_type)) + # print("move {} --> {}".format(self.current_position, action_type)) self.current_position = (self.current_position[0], self.current_position[1] - 1) elif self.current_rotation == Direction.right: - print("move {} --> {}".format(self.current_position, action_type)) + # print("move {} --> {}".format(self.current_position, action_type)) self.current_position = (self.current_position[0] + 1, self.current_position[1]) elif self.current_rotation == Direction.left: - print("move {} --> {}".format(self.current_position, action_type)) + # print("move {} --> {}".format(self.current_position, action_type)) self.current_position = (self.current_position[0] - 1, self.current_position[1]) - def plan_actions(self): - if len(self.current_order.items) > 0: - i = self.current_order.items.pop(0) - if self.current_item is None: - self.provided_items.clear() - self.current_item = i - print("PLAN MOVEMENT") - - # get item + def step(self) -> None: + if len(self.action_queue) > 0: + self.move() + elif self.ready_for_execution: + if self.current_position != self.goal: pathFinder = PathFinderOnStates( self.game_constants, - self.drop_off.location, - PathFinderState(self.current_position, - self.current_rotation, - 0, - Action(ActionType.NONE), - []) - ) - actions = pathFinder.get_action_list() - self.queue_movement_actions(actions) - - elif not self.item_station_completed: - # go through station - - packing_station: GridLocation = None - stations = dict(self.graph.packingStations) - if i.real_type == ItemType.SHELF: - packing_station = stations[PatchType.packingShelf] - elif i.real_type == ItemType.REFRIGERATOR: - packing_station = stations[PatchType.packingRefrigerator] - elif i.real_type == ItemType.DOOR: - packing_station = stations[PatchType.packingC] - - pathFinder = PathFinderOnStates( - self.game_constants, - packing_station, + self.goal, PathFinderState(self.current_position, self.current_rotation, 0, Action( - desired_item=i, + desired_item=None, action_type=ActionType.PICK_ITEM ), []) ) actions = pathFinder.get_action_list() self.queue_movement_actions(actions) - self.item_station_completed = True - else: - # go to client delivery area - pathFinder = PathFinderOnStates( - self.game_constants, - self.client_delivery.location, - PathFinderState(self.current_position, - self.current_rotation, - 0, - Action(ActionType.NONE), - []) - ) - actions = pathFinder.get_action_list() - self.queue_movement_actions(actions) - self.queue_movement_actions( - [Action(ActionType.DROP_ITEM)] - ) + if self.current_order is not None and self.goal == self.base: + self.current_item = self.current_order.items.pop(0) + packing_station: GridLocation = None + stations = dict(self.graph.packingStations) - self.current_item = None - self.provided_items.append(self.current_item) - self.item_station_completed = False + if self.current_item.real_type == ItemType.SHELF: + packing_station = stations[PatchType.packingShelf] + elif self.current_item.real_type == ItemType.REFRIGERATOR: + packing_station = stations[PatchType.packingRefrigerator] + elif self.current_item.real_type == ItemType.DOOR: + packing_station = stations[PatchType.packingDoor] - def step(self) -> None: - if len(self.action_queue) > 0: - self.move() - elif self.ready_for_execution and len(self.orderList) > 0: - if (self.current_order is not None and len(self.current_order.items)) == 0: - self.fulfilled_orders.append(self.current_order) - self.current_order = None + self.goal = packing_station - if self.current_order is None: - self.current_order = self.orderList.pop(0) + elif self.goal in [i[1] for i in self.graph.packingStations]: + self.goal = self.client_delivery.location - self.plan_actions() + elif self.goal == self.client_delivery.location: + if self.current_order is not None and len(self.current_order.items) == 0: + + self.current_order_delivered_items.append(self.current_item) + self.current_order.items = deepcopy(self.current_order_delivered_items) + self.fulfilled_orders.append(self.current_order) + + self.current_item = None + self.current_order = None + + self.goal = self.base + else: + self.current_order_delivered_items.append(self.current_item) + self.goal = self.base + self.current_item = None + + elif self.goal == self.base and self.current_order is None: + self.current_order_delivered_items.clear() + self.current_order = self.orderList.pop(0) def creation_log(self): print("Created Forklift Agent [id: {}]".format(self.unique_id)) diff --git a/GameModel.py b/GameModel.py index 78630a7..bde776b 100644 --- a/GameModel.py +++ b/GameModel.py @@ -9,6 +9,7 @@ from mesa.time import RandomActivation from AgentBase import AgentBase from ForkliftAgent import ForkliftAgent from InitialStateFactory import InitialStateFactory +from ItemDisplayAgent import ItemDisplayAgent from PatchAgent import PatchAgent from PatchType import PatchType from PictureVisualizationAgent import PictureVisualizationAgent @@ -36,7 +37,9 @@ class Phase(Enum): class GameModel(Model): - def __init__(self, width, height, graph: GridWithWeights, items: int, orders: int, classificator): + def __init__(self, width, height, graph: GridWithWeights, items: int, orders: int, classificator, + item_display_pos: List[GridLocation]): + # self.num_agents = 5 self.first = True self.item_recognised = False @@ -44,10 +47,12 @@ class GameModel(Model): self.grid = MultiGrid(height, width, True) self.schedule = RandomActivation(self) self.current_item_recognition = None + self.current_item = None self.client_delivery: PatchAgent = None self.drop_off: PatchAgent = None self.graph = graph + self.cut_orders : List[Order] = [] self.game_constants = GameConstants( width, @@ -68,15 +73,16 @@ class GameModel(Model): self.schedule.add(self.forklift_agent) self.agents.append(self.forklift_agent) + self.item_display_agents: List[ItemDisplayAgent] = [] # INITIALIZATION # print("############## INITIALIZATION ##############") self.phase = Phase.INIT - self.initialize_grid(graph) + self.initialize_grid(graph, item_display_pos) self.orderList: List[Order] = InitialStateFactory.generate_order_list(orders) self.fulfilled_orders: List[Order] = [] - self.forklift_agent.orderList = self.orderList self.forklift_agent.fulfilled_orders = self.fulfilled_orders + self.forklift_agent.set_base(self.drop_off) self.classificator = classificator print("############## RECOGNISE ITEMS ##############") @@ -85,6 +91,8 @@ class GameModel(Model): self.items_for_recognization = copy.deepcopy(self.provided_items) self.recognised_items: List[Item] = [] + self.current_order_delivered_items = self.forklift_agent.current_order_delivered_items + print("Relocate forklift agent to loading area for item recognition") pathFinder = PathFinderOnStates( @@ -98,8 +106,9 @@ class GameModel(Model): print("PATHFINDING") print(actions) self.forklift_agent.queue_movement_actions(actions) + self.current_order = self.forklift_agent.current_order - def initialize_grid(self, graph: GridWithWeights): + def initialize_grid(self, graph: GridWithWeights, item_display_pos): print("INITIALIZING GRID") # Add the agent to a random grid cell x = 5 @@ -120,6 +129,7 @@ class GameModel(Model): self.place_walls_agents(graph.walls) self.place_puddles(graph.puddles) self.place_packing_stations(graph.packingStations) + self.place_order_items_display(item_display_pos) def place_dividers(self): for i in range(0, 10): @@ -160,10 +170,24 @@ class GameModel(Model): self.agents.append(agent) self.grid.place_agent(agent, p[1]) + def place_order_items_display(self, item_positions: List[GridLocation]): + for p in item_positions: + agent = ItemDisplayAgent(self, p) + self.item_display_agents.append(agent) + self.grid.place_agent(agent, p) + + def update_item_display(self): + self.current_item = self.forklift_agent.current_item + for i in range(4): + self.item_display_agents[i].image = None + if len(self.forklift_agent.current_order_delivered_items) > i: + self.item_display_agents[i].image = self.forklift_agent.current_order_delivered_items[i].image + def step(self): self.schedule.step() self.grid.remove_agent(self.forklift_agent) self.grid.place_agent(self.forklift_agent, self.forklift_agent.current_position) + self.update_item_display() if self.phase == Phase.ITEM_RECOGNITION: if not self.item_recognised and self.forklift_agent.current_position == self.drop_off.location: @@ -200,15 +224,70 @@ class GameModel(Model): # print("ORDER {}, PRIO: {}".format(new_orders[i].id, new_orders[i].priority)) self.orderList = new_orders + self.count_recognised_items() + + self.sort_orders() + + self.forklift_agent.orderList = self.orderList print("FINISHED CLIENT ORDER SORTING") self.phase = Phase.EXECUTION if self.phase == Phase.EXECUTION: - print("Execution") + self.current_order = self.forklift_agent.current_order + pass + # print("Execution") + + def sort_orders(self): + orders_to_fill: [Order] = [] + cut_orders: [Order] = [] + + for i in range(len(self.orderList)): + o: Order = self.orderList[i] + refrige = self.count_item_type(o, ItemType.REFRIGERATOR) + shelf = self.count_item_type(o, ItemType.SHELF) + door = self.count_item_type(o, ItemType.DOOR) + + if self.count_shelf - shelf >= 0 and self.count_refrige - refrige >= 0 and self.count_door - door >= 0: + self.count_shelf -= shelf + self.count_door -= door + self.count_refrige -= refrige + orders_to_fill.append(o) + else: + cut_orders.append(o) + + self.cut_orders = cut_orders + self.orderList = orders_to_fill + self.forklift_agent.orderList = orders_to_fill + + def count_item_type(self, o: Order, itemType: ItemType) -> int: + res = 0 + for i in range(len(o.items)): + it: Item = o.items[i] + if it.guessed_type == itemType: + res += 1 + + return res + + def count_recognised_items(self): + count_refrige: int = 0 + count_door: int = 0 + count_shelf: int = 0 + + for i in range(len(self.recognised_items)): + item: Item = self.recognised_items[i] + if item.guessed_type == ItemType.DOOR: + count_door += 1 + elif item.guessed_type == ItemType.SHELF: + count_shelf += 1 + else: + count_refrige += 1 + + self.count_door = count_door + self.count_shelf = count_shelf + self.count_refrige = count_refrige def recognise_item(self, item: Item): - # TODO IMAGE PROCESSING val = image_classification(self.picture_visualization.img, self.classificator) print("VAL: {}".format(val)) diff --git a/InitialStateFactory.py b/InitialStateFactory.py index 1289f0c..1b0b823 100644 --- a/InitialStateFactory.py +++ b/InitialStateFactory.py @@ -5,13 +5,14 @@ from data.Order import Order from data.enum.ItemType import ItemType from data.enum.Priority import Priority from util.ClientParamsFactory import ClientParamsFactory +from util.PathByEnum import PathByEnum class InitialStateFactory: @staticmethod def generate_item_list(output_list_size: int): - item_list : [Item] = [] + item_list: [Item] = [] for i in range(output_list_size): item_list.append(InitialStateFactory.__generate_item()) @@ -74,7 +75,7 @@ class InitialStateFactory: @staticmethod def generate_input_sequence(self, input_sequence_size): - sequence : [Item] = [] + sequence: [Item] = [] for i in range(0, input_sequence_size): sequence.append(self.__generate_item()) @@ -83,4 +84,6 @@ class InitialStateFactory: @staticmethod def __generate_item() -> Item: randomly_picked_type = random.choice(list(ItemType)) - return Item(randomly_picked_type) + item = Item(randomly_picked_type, PathByEnum.get_random_path(randomly_picked_type)) + item.guessed_type = item.real_type + return item diff --git a/ItemDisplayAgent.py b/ItemDisplayAgent.py new file mode 100644 index 0000000..73a9ca4 --- /dev/null +++ b/ItemDisplayAgent.py @@ -0,0 +1,10 @@ +from PatchAgent import PatchAgent +from PatchType import PatchType +from util.PathDefinitions import GridLocation + + +class ItemDisplayAgent(PatchAgent): + + def __init__(self, model, location: GridLocation): + self.image = None + super().__init__(model, location, patch_type=PatchType.itemDisplay) diff --git a/PatchType.py b/PatchType.py index 96a4c05..57fcc10 100644 --- a/PatchType.py +++ b/PatchType.py @@ -11,3 +11,4 @@ class PatchType(enum.Enum): packingRefrigerator = 7 packingDoor = 8 divider = 9 + itemDisplay = 10 diff --git a/data/Item.py b/data/Item.py index bf2f67c..4d50d18 100644 --- a/data/Item.py +++ b/data/Item.py @@ -6,9 +6,10 @@ from data.enum.ItemType import ItemType class Item: id_counter = count(start=0) - def __init__(self, item_type: ItemType): + def __init__(self, item_type: ItemType, image): self.id = next(self.id_counter) self.real_type = item_type + self.image = image self.guessed_type = None def __repr__(self) -> str: diff --git a/main.py b/main.py index ffa4084..8e97fb4 100644 --- a/main.py +++ b/main.py @@ -1,24 +1,20 @@ -import math import random from mesa.visualization.ModularVisualization import ModularServer from mesa.visualization.modules import CanvasGrid +from tensorflow import keras from ForkliftAgent import ForkliftAgent from GameModel import GameModel -from InitialStateFactory import InitialStateFactory +from ItemDisplayAgent import ItemDisplayAgent from PatchAgent import PatchAgent from PatchType import PatchType from PictureVisualizationAgent import PictureVisualizationAgent -from data.Order import Order from data.enum.Direction import Direction -from tensorflow import keras - -from data.enum.Priority import Priority -from genetic_order.GeneticOrder import GeneticOrder from util.PathDefinitions import GridWithWeights from visualization.DisplayAttributeElement import DisplayAttributeElement from visualization.DisplayItemListAttribute import DisplayItemListAttributeElement +from visualization.DisplayOrder import DisplayOrder from visualization.DisplayOrderList import DisplayOrderList colors = [ @@ -39,7 +35,7 @@ def agent_portrayal(agent): elif agent.current_rotation == Direction.left: shape = "img/image_left.png" - portrayal = {"Shape": shape, "scale": 1.0, "Layer": 0} + portrayal = {"Shape": shape, "scale": 1.0, "Layer": 2} if isinstance(agent, PatchAgent): color = colors[0] @@ -52,11 +48,11 @@ def agent_portrayal(agent): elif agent.patch_type == PatchType.diffTerrain: portrayal = {"Shape": "img/puddle.png", "scale": 1.0, "Layer": 0} elif agent.patch_type == PatchType.packingShelf: - portrayal = {"Shape": "img/shelf_s.jpg", "scale": 1.0, "Layer": 0} + portrayal = {"Shape": "img/shelf_s.jpg", "scale": 1.0, "Layer": 1} elif agent.patch_type == PatchType.packingRefrigerator: - portrayal = {"Shape": "img/fridge_f.jpg", "scale": 1.0, "Layer": 0} + portrayal = {"Shape": "img/fridge_f.jpg", "scale": 1.0, "Layer": 1} elif agent.patch_type == PatchType.packingDoor: - portrayal = {"Shape": "img/door_d.jpg", "scale": 1.0, "Layer": 0} + portrayal = {"Shape": "img/door_d.jpg", "scale": 1.0, "Layer": 1} elif agent.patch_type == PatchType.divider: portrayal = \ @@ -78,6 +74,18 @@ def agent_portrayal(agent): if isinstance(agent, PictureVisualizationAgent): portrayal = {"Shape": f"{agent.img}", "scale": 3.0, "Layer": 0} + if isinstance(agent, ItemDisplayAgent): + if agent is not None and agent.image is not None: + portrayal = {"Shape": f"{agent.image}", "scale": 1.0, "Layer": 0} + else: + portrayal = \ + {"Shape": "rect", + "Filled": "true", + "Layer": 0, + "Color": "black", + "w": 1, + "h": 1} + return portrayal @@ -88,88 +96,32 @@ if __name__ == '__main__': scale = base / gridWidth diagram = GridWithWeights(gridWidth, gridHeight) - diagram.walls = [(6, 5), (6, 6), (6, 7), (6, 8), (2, 3), (2, 4), (3, 4), (4, 4), (6, 4)] - diagram.puddles = [(2, 2), (2, 5), (2, 6), (5, 4)] - diagram.packingStations = [(PatchType.packingShelf, (4, 8)), (PatchType.packingRefrigerator, (4, 6)), (PatchType.packingDoor, (4, 2))] + diagram.walls = [(6, 5), (6, 6), (6, 7), (6, 8), (2, 3), (2, 4), (2, 6), (4, 7), (3, 4), (4, 4), (6, 4)] + diagram.puddles = [(2, 2), (2, 5), (5, 4), (4, 8), (4, 6), (4, 2)] + diagram.packingStations = [(PatchType.packingShelf, (4, 8)), (PatchType.packingRefrigerator, (4, 6)), + (PatchType.packingDoor, (4, 2))] grid = CanvasGrid(agent_portrayal, gridWidth, gridHeight, scale * gridWidth, scale * gridHeight) + display_items = [(6, 11), (7, 11), (8, 11), (9, 11)] + readyText = DisplayAttributeElement("phase") - # current_item = DisplayPictureElement("current_item_recognition") provided_itesm = DisplayItemListAttributeElement("provided_items") recognised_items = DisplayItemListAttributeElement("recognised_items") + current_order = DisplayOrder("current_order") + current_item = DisplayAttributeElement("current_item") ordersText = DisplayOrderList("orderList") fulfilled_orders = DisplayOrderList("fulfilled_orders") + cut_orders = DisplayOrderList("cut_orders") # MTR! model = keras.models.load_model("imageClasification/my_model") server = ModularServer(GameModel, - [grid, readyText, provided_itesm, recognised_items, ordersText, - fulfilled_orders], + [grid, readyText, current_item, current_order, fulfilled_orders, ordersText, provided_itesm, + recognised_items, cut_orders], "Automatyczny Wózek Widłowy", - dict(width=gridHeight, height=gridWidth, graph=diagram, items=50, orders=20, classificator=model)) - - # def order_rule(order: Order): - # return order.id - # - # - # punish_low = 500 - # punish_med = 300 - # def sum_wrong(member: [Order]) -> int: - # last_high = 0 - # last_med = 0 - # - # sum_high = 0 - # sum_med = 0 - # sum_low = 0 - # - # counter = 0 - # - # for i in range(len(member)): - # o: Order = member[i] - # if o.priority == Priority.HIGH : - # sum_high += 1 - # last_high = i - # elif o.priority == Priority.MEDIUM: - # sum_med += 1 - # last_med = i - # else: - # sum_low += 1 - # - # for i in range(last_high): - # o: Order = member[i] - # if o.priority == Priority.MEDIUM: - # counter += punish_med - # elif o.priority == Priority.LOW: - # counter += punish_low - # - # for i in range(last_med): - # o: Order = member[i] - # if o.priority == Priority.LOW: - # counter += punish_low - # - # print("sum: high:", sum_high, "med:", sum_med, "low:", sum_low) - # print("last_high:", last_high, "last_med:", last_med, "sum:", counter) - # return counter - # - # orders = InitialStateFactory.generate_order_list_XD(20) - # test: GeneticOrder = GeneticOrder(orders) - # punish_low = test.punish_low - # punish_med = test.punish_med - # - # print("SIEMA before: ") - # sum_wrong(orders) - # for i in orders: - # print("id:", i.id, "priority:", i.priority, "sum/time:", i.sum/i.time) - # # print("id:", i.id, "priority:", i.priority) - # - # newOrders = test.get_orders_sorted(orders) - # - # print("NAURA after:") - # sum_wrong(newOrders) - # for i in newOrders: - # print("id:", i.id, "priority:", i.priority, "sum/time:", i.sum/i.time) - # # print("id:", i.id, "priority:", i.priority) + dict(width=gridHeight, height=gridWidth, graph=diagram, items=60, orders=20, + classificator=model, item_display_pos=display_items)) server.port = 8888 server.launch() diff --git a/pathfinding/PathfinderOnStates.py b/pathfinding/PathfinderOnStates.py index 65b306d..1cc88fc 100644 --- a/pathfinding/PathfinderOnStates.py +++ b/pathfinding/PathfinderOnStates.py @@ -57,7 +57,7 @@ class PathFinderOnStates: if action == action.action_type.MOVE: if curr_state.agent_position in self.game_constants.diffTerrain: - cost = curr_state.cost + 20 + cost = curr_state.cost + 45 # tutaj koszt kaluzy else: cost = curr_state.cost + 1 diff --git a/visualization/DisplayOrder.py b/visualization/DisplayOrder.py new file mode 100644 index 0000000..eb7d43b --- /dev/null +++ b/visualization/DisplayOrder.py @@ -0,0 +1,46 @@ +from collections import Counter +from typing import List + +from mesa.visualization.modules import TextElement + +from data.Order import Order + + +class DisplayOrder(TextElement): + def __init__(self, attr_name): + ''' + Create a new text attribute element. + + Args: + attr_name: The name of the attribute to extract from the model. + + Example return: "happy: 10" + ''' + self.attr_name = attr_name + + def render(self, model): + val = getattr(model, self.attr_name) + res = self.attr_name + if val is not None: + o = val + itemList = map(lambda x: x.real_type, o.items) + itemCounter = Counter(itemList) + + item_str = "" + + res += f"
  • items: {item_str} priority: {o.priority}
    Client: {vars(o.client_params)}
  • " + + return res diff --git a/visualization/DisplayOrderList.py b/visualization/DisplayOrderList.py index f7b7564..46bc5f5 100644 --- a/visualization/DisplayOrderList.py +++ b/visualization/DisplayOrderList.py @@ -4,7 +4,6 @@ from typing import List from mesa.visualization.modules import TextElement from data.Order import Order -from data.enum.ItemType import ItemType class DisplayOrderList(TextElement): @@ -21,35 +20,36 @@ class DisplayOrderList(TextElement): def render(self, model): val = getattr(model, self.attr_name) + res = "" + if val is not None: + orderList: List[Order] = val - orderList: List[Order] = val + res = self.attr_name + ":
      " - res = self.attr_name + ":
        " + for o in orderList: + if o is None: + continue - for o in orderList: - if o is None: - continue + itemList = map(lambda x: x.real_type, o.items) + itemCounter = Counter(itemList) - itemList = map(lambda x: x.real_type, o.items) - itemCounter = Counter(itemList) + item_str = "" + res += f"
      1. items: {item_str} priority: {o.priority}
        Client: {vars(o.client_params)}
      2. " - res += f"
      3. items: {item_str} priority: {o.priority}
        Client: {vars(o.client_params)}
      4. " - - res += "
      " + res += "
    " return res