Ejemplo n.º 1
0
class FleetManager:
    """
            A class containing the intelligence of the Fleetmanager agent inheriting a simple task allocation,
            allocating the task to the closest available robot. Robots have no local task list.
    """
    def __init__(self, env, kb, agv_fm_comm, agv_to_fm_comm, print_):

        # Simulation environment
        self.env = env

        # Printing
        self.print = print_

        # Communication attributes
        self.ip = '172.21.0.0'
        self.kb = kb
        self.agv_fm_comm = agv_fm_comm
        self.agv_to_fm_comm = agv_to_fm_comm
        self.comm = Comm(self.ip)

        # Process
        self.main = self.env.process(self.main())

        # Initialize
        self.my_print("Fleet manager:           Started")

    def main(self):

        self.my_print("\n")
        while True:

            # Timeout
            yield self.env.timeout(1)  # Sample time

            # Define current status
            idle_robots = self.get_idle_robots()
            tasks_to_assign = self.get_tasks_to_assign()

            # If there are idle robots and tasks to execute, assign closest task
            if not len(tasks_to_assign) == 0 and not len(idle_robots) == 0:
                # Pick first task
                task = tasks_to_assign[0]

                # Get closest robot
                robot = self.get_closest_robot(task, idle_robots)

                # Assign task to agv task lists
                task.message = 'push'
                self.comm.tcp_write(self.agv_fm_comm[robot.ID], task)

                # Remove task from global task list
                self.comm.sql_remove_task(self.kb['global_task_list'],
                                          task.order_number)

    def get_idle_robots(self):
        robots = np.copy(self.comm.sql_read(self.kb['global_robot_list']))
        robots = list(filter(lambda robot: robot.status == 'IDLE', robots))
        robots = sorted(robots, key=lambda robot_: robot_.ID)
        return robots

    def get_tasks_to_assign(self):
        tasks = np.copy(self.comm.sql_read(self.kb['global_task_list']))
        return tasks

    def get_closest_robot(self, task, idle_robots):
        return min(idle_robots,
                   key=lambda robot: calculate_astar_distance(
                       self.kb['graph'], robot.robot_node, task.pos_A))

    def my_print(self, msg):
        if self.print:
            print(msg)
class AGV:
    """
        A class containing the intelligence of the AGV agent
    """
    def __init__(self, env, agv_params, kb, fm_to_agv_comm, agv_to_fm_comm,
                 print_):

        # Simulation environment
        self.env = env

        # Printing
        self.print = print_

        # Communication attributes
        self.ip = '172.21.0.0'
        self.kb = kb  # Knwoledge base (SQL database)
        self.fm_to_agv_comm = fm_to_agv_comm
        self.agv_to_fm_comm = agv_to_fm_comm
        self.comm = Comm(self.ip)

        # agv attributes
        self.ID = agv_params['ID']  # Each agv has an unique ID number
        self.robot_speed = agv_params['robot_speed']  # Constant robot speed
        self.task_execution_time = agv_params['task_execution_time']
        self.depot_locations = agv_params['depot_locations']
        self.battery_threshold = agv_params[
            'battery_threshold']  # Battery threshold when the agv needs to charge
        self.collision_threshold = agv_params[
            'collision_threshold']  # Collision threshold
        self.max_charging_time = agv_params[
            'max_charging_time']  # One hour to charge fully
        self.epsilon = agv_params['epsilon']  # Objective parameter
        self.max_tasks_in_task_list = agv_params['max_tasks_in_task_list']
        self.initial_resources = agv_params['initial_resources']

        # Local task list
        self.kb['local_task_list_R' + str(self.ID)] = simpy.FilterStore(
            self.env)

        # Updated attributes
        self.robot_location = self.kb['graph'].nodes[
            agv_params['start_location']].pos
        self.robot_node = agv_params['start_location']
        self.status = 'IDLE'
        self.battery_status = self.initial_resources
        self.travelled_time = 0
        self.charged_time = 0
        self.heading_direction = 0
        self.task_executing = None
        self.path = []
        self.slots = []
        self.congestions = 0
        self.total_path = []

        # AGV tasks
        self.task_allocation = TaskAllocation(self)
        self.resource_management = ResourceManagement(self)
        self.action = Action(self)

        # Processes
        self.main = self.env.process(self.main())

        # Initialize
        self.my_print("agv " + str(self.ID) + ":                   Started")
        self.robot = Robot(self.ID, self.robot_location, self.robot_node,
                           self.heading_direction, self.path, self.status,
                           self.battery_status, self.travelled_time,
                           self.charged_time, self.congestions,
                           self.task_executing)
        self.comm.sql_write(self.kb['global_robot_list'], self.robot)

    def main(self):

        while True:

            # Wait for an assigned task
            self.my_print("AGV " + str(self.ID) +
                          ":      Waiting for tasks..." + " at " +
                          str(self.env.now))
            self.task_executing = yield self.kb['local_task_list_R' +
                                                str(self.ID)].get()

            # Start task
            self.my_print("AGV " + str(self.ID) +
                          ":      Start executing task " +
                          str(self.task_executing.to_string()) + " at " +
                          str(self.env.now))
            if self.status is not 'EMPTY':
                self.status = 'BUSY'
            self.update_global_robot_list()

            # Remove task from global task list and add to executing list
            self.comm.sql_remove_task(self.kb['global_task_list'],
                                      self.task_executing.order_number)
            self.task_executing.robot = self.ID
            self.comm.sql_write(self.kb['tasks_executing'],
                                self.task_executing)

            # Go to task A
            yield self.env.process(self.execute_task(self.task_executing))

            # Perform task A
            yield self.env.process(self.action.pick())
            self.task_executing.picked = True
            self.my_print("AGV " + str(self.ID) +
                          ":      Picked item of task " +
                          str(self.task_executing.order_number) + " at " +
                          str(self.env.now))

            # Task executed
            self.comm.sql_remove_task(self.kb['tasks_executing'],
                                      self.task_executing.order_number)
            self.task_executing = None

            # Set status to IDLE when task is done or when done charging
            if self.status is not 'EMPTY':
                self.status = 'IDLE'
            self.update_global_robot_list()

    # Calculates shortest path with Astar and executes
    def execute_task(self, task):

        # Compute astar path
        self.path, _ = find_shortest_path(self.kb['graph'], self.robot_node,
                                          task.pos_A)

        # Update state
        self.update_global_robot_list()

        # Move from node to node
        while len(self.path) > 0:
            yield self.env.process(self.action.move_to_node(self.path[0]))

        # Check if task is charging task
        if task.order_number == '000':

            # Compute charging time
            charging_time = (100 - self.battery_status
                             ) / self.resource_management.charging_factor

            # Update status
            self.my_print("agv " + str(self.ID) + ":      Is charging for " +
                          str(charging_time) + " seconds at " +
                          str(self.env.now))
            self.status = 'CHARGING'
            self.update_global_robot_list()

            # Charging
            yield self.env.timeout(charging_time)

            # Update robot status
            self.battery_status = self.battery_status + charging_time * self.resource_management.charging_factor
            self.status = 'IDLE'
            self.charged_time += int(
                charging_time) + calculate_path_traveltime(
                    self.kb['graph'], self.path, self.robot_speed)
            self.update_global_robot_list()

    def update_global_robot_list(self):
        self.comm.sql_remove_robot(self.kb['global_robot_list'], self.ID)
        self.robot = Robot(self.ID, self.robot_location, self.robot_node,
                           self.heading_direction, self.path, self.status,
                           self.battery_status, self.travelled_time,
                           self.charged_time, self.congestions,
                           self.task_executing, self.total_path)
        self.comm.sql_write(self.kb['global_robot_list'], self.robot)

    def my_print(self, msg):
        if self.print:
            print(msg)