diff --git a/ForkliftAgent.py b/ForkliftAgent.py index 5201ca2..8dee11e 100644 --- a/ForkliftAgent.py +++ b/ForkliftAgent.py @@ -1,18 +1,37 @@ from typing import Tuple, List from AgentBase import AgentBase +from PatchAgent import PatchAgent +from PatchType import PatchType +from data.Item import Item +from data.Order import Order from data.enum.Direction import Direction +from data.enum.ItemType import ItemType from decision.Action import Action from decision.ActionType import ActionType +from pathfinding.PathFinderState import PathFinderState +from pathfinding.PathfinderOnStates import PathFinderOnStates +from util.PathDefinitions import GridLocation, GridWithWeights class ForkliftAgent(AgentBase): - def __init__(self, model): + def __init__(self, model, game_constants, client_delivery: PatchAgent, drop_off: PatchAgent, + graph: GridWithWeights): super().__init__(model) self.action_queue: List[Action] = [] self.current_position = Tuple[int, int] self.current_rotation = Direction.right + self.orderList = [] + self.fulfilled_orders: List[Order] = [] + self.client_delivery: PatchAgent = client_delivery + 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] = [] def queue_movement_actions(self, movement_actions: List[Action]): self.action_queue.extend(movement_actions) @@ -55,8 +74,88 @@ class ForkliftAgent(AgentBase): 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 + 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.packingA] + elif i.real_type == ItemType.EGG: + packing_station = stations[PatchType.packingB] + elif i.real_type == ItemType.DOOR: + packing_station = stations[PatchType.packingC] + + pathFinder = PathFinderOnStates( + self.game_constants, + packing_station, + PathFinderState(self.current_position, + self.current_rotation, + 0, + Action( + desired_item=i, + 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)] + ) + + self.current_item = None + self.provided_items.append(self.current_item) + self.item_station_completed = False + def step(self) -> None: - self.move() + if len(self.action_queue) > 0: + self.move() + elif 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 + + if self.current_order is None: + self.current_order = self.orderList.pop(0) + + self.plan_actions() def creation_log(self): print("Created Forklift Agent [id: {}]".format(self.unique_id)) diff --git a/GameModel.py b/GameModel.py index e8db162..45999f5 100644 --- a/GameModel.py +++ b/GameModel.py @@ -10,11 +10,10 @@ from ForkliftAgent import ForkliftAgent from InitialStateFactory import InitialStateFactory from PatchAgent import PatchAgent from PatchType import PatchType +from PictureVisualizationAgent import PictureVisualizationAgent from data.GameConstants import GameConstants from data.Item import Item from data.Order import Order -from data.enum.Direction import Direction -from data.enum.ItemType import ItemType from decision.Action import Action from decision.ActionType import ActionType from pathfinding.PathfinderOnStates import PathFinderOnStates, PathFinderState @@ -38,14 +37,10 @@ class GameModel(Model): self.running = True self.grid = MultiGrid(height, width, True) self.schedule = RandomActivation(self) + self.current_item_recognition = None self.client_delivery: PatchAgent = None self.drop_off: PatchAgent = None - - self.agents = [AgentBase] - self.forklift_agent = ForkliftAgent(self) - self.schedule.add(self.forklift_agent) - self.agents.append(self.forklift_agent) self.graph = graph self.game_constants = GameConstants( @@ -55,11 +50,27 @@ class GameModel(Model): graph.puddles ) + self.agents = [AgentBase] + + self.forklift_agent = ForkliftAgent( + self, + self.game_constants, + self.client_delivery, + self.drop_off, + self.graph + ) + + self.schedule.add(self.forklift_agent) + self.agents.append(self.forklift_agent) + # INITIALIZATION # print("############## INITIALIZATION ##############") self.phase = Phase.INIT self.initialize_grid(graph) self.orderList: List[Order] = InitialStateFactory.generate_order_list(3) + self.fulfilled_orders: List[Order] = [] + self.forklift_agent.orderList = self.orderList + self.forklift_agent.fulfilled_orders = self.fulfilled_orders print("############## RECOGNISE ITEMS ##############") self.phase = Phase.ITEM_RECOGNITION @@ -88,22 +99,40 @@ class GameModel(Model): self.grid.place_agent(self.forklift_agent, (x, y)) self.forklift_agent.current_position = (x, y) + self.picture_visualization = PictureVisualizationAgent( + self, + (1, 11), + ) + self.schedule.add(self.picture_visualization) + self.grid.place_agent(self.picture_visualization, self.picture_visualization.location) + self.agents.append(self.picture_visualization) + self.place_logistics() + self.place_dividers() self.place_walls_agents(graph.walls) self.place_puddles(graph.puddles) self.place_packing_stations(graph.packingStations) + def place_dividers(self): + for i in range(0, 10): + for j in range(10,13): + agent = PatchAgent(self, (i, j), PatchType.divider) + self.agents.append(agent) + self.grid.place_agent(agent, (i, j)) + def place_logistics(self): agent = PatchAgent(self, (self.grid.width - 1, int(self.grid.height / 2)), PatchType.pickUp) self.schedule.add(agent) self.grid.place_agent(agent, agent.location) self.agents.append(agent) self.client_delivery = agent + self.forklift_agent.client_delivery = self.client_delivery agent = PatchAgent(self, (0, int(self.grid.height / 2)), PatchType.dropOff) self.grid.place_agent(agent, agent.location) self.agents.append(agent) self.drop_off = agent + self.forklift_agent.drop_off = self.drop_off def place_walls_agents(self, walls: List[GridLocation]): for w in walls: @@ -138,6 +167,7 @@ class GameModel(Model): else: print("BEGIN ITEM RECOGNITION, left: {}".format(len(self.provided_items))) item_to_recognise = self.provided_items.pop() + self.picture_visualization.img = "item_images/door/drzwi1.jpg" recognised = self.recognise_item(item_to_recognise) self.recognised_items.append(recognised) @@ -145,93 +175,10 @@ class GameModel(Model): # TODO GENERICS SORTING sorted(self.orderList, key=lambda x: len(x.items)) print("FINISHED CLIENT ORDER SORTING") - self.phase = Phase.PLAN_MOVEMENT - - if self.phase == Phase.PLAN_MOVEMENT: - if len(self.orderList) > 0: - self.plan_actions() - self.phase = Phase.EXECUTION - else: - print("NO MORE ORDERS") + self.phase = Phase.EXECUTION if self.phase == Phase.EXECUTION: print("Execution") - if (len(self.forklift_agent.action_queue) == 0): - self.phase = Phase.PLAN_MOVEMENT - - def plan_actions(self): - order = self.orderList.pop(0) - print("PLAN MOVEMENT") - for i in order.items: - if self.first: - # get item - pathFinder = PathFinderOnStates( - self.game_constants, - self.drop_off.location, - PathFinderState(self.forklift_agent.current_position, - self.forklift_agent.current_rotation, - 0, - Action(ActionType.NONE), - []) - ) - actions = pathFinder.get_action_list() - self.forklift_agent.queue_movement_actions(actions) - self.first = False - else: - # get item - pathFinder = PathFinderOnStates( - self.game_constants, - self.drop_off.location, - PathFinderState(self.client_delivery.location, - Direction.right, - 0, - Action(ActionType.NONE), - []) - ) - actions = pathFinder.get_action_list() - self.forklift_agent.queue_movement_actions(actions) - self.first = False - - # go through station - packing_station: GridLocation = None - stations = dict(self.graph.packingStations) - if i.real_type == ItemType.PASTA: - packing_station = stations[PatchType.packingA] - elif i.real_type == ItemType.EGG: - packing_station = stations[PatchType.packingB] - elif i.real_type == ItemType.PIZZA: - packing_station = stations[PatchType.packingC] - - pathFinder = PathFinderOnStates( - self.game_constants, - packing_station, - PathFinderState(self.drop_off.location, - Direction.left, - 0, - Action( - desired_item=i, - action_type=ActionType.PICK_ITEM - ), - []) - ) - actions = pathFinder.get_action_list() - self.forklift_agent.queue_movement_actions(actions) - - # go to client delivery area - pathFinder = PathFinderOnStates( - self.game_constants, - self.client_delivery.location, - PathFinderState(packing_station, - Direction.right, - 0, - Action(ActionType.NONE), - []) - ) - actions = pathFinder.get_action_list() - self.forklift_agent.queue_movement_actions(actions) - self.forklift_agent.queue_movement_actions( - [Action(ActionType.DROP_ITEM)] - ) def recognise_item(self, item: Item): # TODO IMAGE PROCESSING diff --git a/PatchType.py b/PatchType.py index bae60de..289107d 100644 --- a/PatchType.py +++ b/PatchType.py @@ -10,3 +10,4 @@ class PatchType(enum.Enum): packingA = 6 packingB = 7 packingC = 8 + divider = 9 diff --git a/PictureVisualizationAgent.py b/PictureVisualizationAgent.py new file mode 100644 index 0000000..7e09113 --- /dev/null +++ b/PictureVisualizationAgent.py @@ -0,0 +1,13 @@ +from AgentBase import AgentBase +from util.PathDefinitions import GridLocation + + +class PictureVisualizationAgent(AgentBase): + + def __init__(self, model, location: GridLocation): + self.location = location + self.img = "" + super().__init__(model) + + def creation_log(self): + print("Created Patch Agent [id: {} ,img: {}]".format(self.unique_id, self.img)) diff --git a/data/enum/ItemType.py b/data/enum/ItemType.py index edb4e4b..f15e785 100644 --- a/data/enum/ItemType.py +++ b/data/enum/ItemType.py @@ -2,6 +2,6 @@ from enum import Enum class ItemType(Enum): - PIZZA = 1 - PASTA = 2 + DOOR = 1 + SHELF = 2 EGG = 3 \ No newline at end of file diff --git a/item_images/door/drzwi1.jpg b/item_images/door/drzwi1.jpg new file mode 100644 index 0000000..f239f2a Binary files /dev/null and b/item_images/door/drzwi1.jpg differ diff --git a/item_images/door/drzwi2.png b/item_images/door/drzwi2.png new file mode 100644 index 0000000..f9563e1 Binary files /dev/null and b/item_images/door/drzwi2.png differ diff --git a/main.py b/main.py index 9881bc2..0574917 100644 --- a/main.py +++ b/main.py @@ -7,9 +7,13 @@ from ForkliftAgent import ForkliftAgent from GameModel import GameModel from PatchAgent import PatchAgent from PatchType import PatchType +from PictureVisualizationAgent import PictureVisualizationAgent from data.enum.Direction import Direction from util.PathDefinitions import GridWithWeights from visualization.DisplayAttributeElement import DisplayAttributeElement +from visualization.DisplayItemListAttribute import DisplayItemListAttributeElement +from visualization.DisplayOrderList import DisplayOrderList +from visualization.DisplayPictureElement import DisplayPictureElement colors = [ 'blue', 'cyan', 'orange', 'yellow', 'magenta', 'purple', '#103d3e', '#9fc86c', @@ -41,6 +45,14 @@ def agent_portrayal(agent): portrayal = {"Shape": "img/okB00mer.png", "scale": 1.0, "Layer": 0} elif agent.patch_type == PatchType.diffTerrain: portrayal = {"Shape": "img/puddle.png", "scale": 1.0, "Layer": 0} + elif agent.patch_type == PatchType.divider: + portrayal = \ + {"Shape": "rect", + "Filled": "true", + "Layer": 0, + "Color": "black", + "w": 1, + "h": 1} else: color = colors[random.randrange(13) + 3] portrayal = {"Shape": "rect", @@ -49,13 +61,17 @@ def agent_portrayal(agent): "Color": color, "w": 1, "h": 1} + + if isinstance(agent, PictureVisualizationAgent): + portrayal = {"Shape": f"{agent.img}", "scale": 3.0, "Layer": 0} + return portrayal if __name__ == '__main__': base = 512 gridWidth = 10 - gridHeight = 10 + gridHeight = 13 scale = base / gridWidth diagram = GridWithWeights(gridWidth, gridHeight) @@ -66,12 +82,15 @@ if __name__ == '__main__': grid = CanvasGrid(agent_portrayal, gridWidth, gridHeight, scale * gridWidth, scale * gridHeight) readyText = DisplayAttributeElement("phase") - provided_itesm = DisplayAttributeElement("provided_items") - recognised_items = DisplayAttributeElement("recognised_items") - ordersText = DisplayAttributeElement("orderList") + # current_item = DisplayPictureElement("current_item_recognition") + provided_itesm = DisplayItemListAttributeElement("provided_items") + recognised_items = DisplayItemListAttributeElement("recognised_items") + ordersText = DisplayOrderList("orderList") + fulfilled_orders = DisplayOrderList("fulfilled_orders") server = ModularServer(GameModel, - [grid, readyText, provided_itesm, recognised_items], + [grid, readyText, provided_itesm, recognised_items, ordersText, + fulfilled_orders], "Automatyczny Wózek Widłowy", {"width": gridHeight, "height": gridWidth, "graph": diagram}, ) diff --git a/visualization/DisplayItemListAttribute.py b/visualization/DisplayItemListAttribute.py new file mode 100644 index 0000000..82b2029 --- /dev/null +++ b/visualization/DisplayItemListAttribute.py @@ -0,0 +1,48 @@ +from collections import Counter +from typing import List + +from mesa.visualization.modules import TextElement + +from data.Item import Item +from data.enum.ItemType import ItemType + + +class DisplayItemListAttributeElement(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) + + itemList: List[Item] = val + + itemList = map(lambda x: x.real_type, itemList) + + itemCounter = Counter(itemList) + + # return self.attr_name + ":" + pprint.pformat(itemCounter) + res = self.attr_name + ":" + "" + + return res diff --git a/visualization/DisplayOrderList.py b/visualization/DisplayOrderList.py new file mode 100644 index 0000000..9b06d35 --- /dev/null +++ b/visualization/DisplayOrderList.py @@ -0,0 +1,55 @@ +from collections import Counter +from typing import List + +from mesa.visualization.modules import TextElement + +from data.Order import Order +from data.enum.ItemType import ItemType + + +class DisplayOrderList(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) + + orderList: List[Order] = val + + res = self.attr_name + ":
    " + + for o in orderList: + if o is None: + continue + + 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 += "
" + + return res diff --git a/visualization/DisplayPictureElement.py b/visualization/DisplayPictureElement.py new file mode 100644 index 0000000..6220692 --- /dev/null +++ b/visualization/DisplayPictureElement.py @@ -0,0 +1,21 @@ +from mesa.visualization.modules import TextElement + + +class DisplayPictureElement(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) + if val is not None: + return self.attr_name + ': {}'.format(val, self.attr_name) + else: + return ""