def get_position_info_of_vehicles(self, id_to_vehicle: dict, to_time: int): for vehicle_id, vehicle in id_to_vehicle.items(): if len(vehicle.cur_factory_id ) == 0 and vehicle.destination is None: logger.error( f"Vehicle {vehicle_id}, the current position {vehicle.position_info.cur_factory_id}, " f"the destination is None") continue node_list = self.get_node_list_of_vehicle(vehicle) cur_factory_id = "" arrive_time_at_current_factory = 0 leave_time_at_current_factory = 0 for node in node_list: if node.arr_time <= to_time <= node.leave_time: cur_factory_id = node.id arrive_time_at_current_factory = node.arr_time leave_time_at_current_factory = node.leave_time if len(cur_factory_id) == 0 and node_list[-1].leave_time < to_time: cur_factory_id = node_list[-1].id arrive_time_at_current_factory = node_list[-1].arr_time leave_time_at_current_factory = max(node_list[-1].leave_time, to_time) self.vehicle_id_to_cur_position_info[vehicle_id] = { "cur_factory_id": cur_factory_id, "arrive_time_at_current_factory": arrive_time_at_current_factory, "leave_time_at_current_factory": leave_time_at_current_factory, "update_time": to_time }
def dispatch(self, input_info): # 1. Prepare the input json of the algorithm convert_input_info_to_json_files(input_info) # 2. Run the algorithm command = get_algorithm_calling_command() time_start_algorithm = time.time() used_seconds, message = subprocess_function(command) # 3. parse the output json of the algorithm if Configs.ALGORITHM_SUCCESS_FLAG in message: if (time_start_algorithm < os.stat( Configs.algorithm_output_destination_path).st_mtime < time.time() and time_start_algorithm < os.stat( Configs.algorithm_output_planned_route_path).st_mtime < time.time()): vehicle_id_to_destination, vehicle_id_to_planned_route = get_output_of_algorithm( self.id_to_order_item) dispatch_result = DispatchResult(vehicle_id_to_destination, vehicle_id_to_planned_route) return used_seconds, dispatch_result else: logger.error('output.json is not the newest') sys.exit(-1) else: logger.error(message) raise ValueError('未寻获算法输出成功标识SUCCESS')
def __meet_order_splitting_constraint(dispatch_result, id_to_vehicle: dict, id_to_order: dict): vehicle_id_to_destination = dispatch_result.vehicle_id_to_destination vehicle_id_to_planned_route = dispatch_result.vehicle_id_to_planned_route vehicle_id_to_item_list = get_item_list_of_vehicles(dispatch_result, id_to_vehicle) capacity = 0 split_order_id_list = Checker.__find_split_orders_from_vehicles(vehicle_id_to_item_list) for vehicle_id, vehicle in id_to_vehicle.items(): logger.debug(f"Find split orders of vehicle {vehicle_id}") capacity = vehicle.board_capacity carrying_items = copy.deepcopy(vehicle.carrying_items) route = [] if vehicle_id in vehicle_id_to_destination and vehicle_id_to_destination.get(vehicle_id) is not None: route.append(vehicle_id_to_destination.get(vehicle_id)) if vehicle_id in vehicle_id_to_planned_route: route.extend(vehicle_id_to_planned_route.get(vehicle_id)) split_order_id_list.extend(Checker.__find_split_orders_in_vehicle_routes(route, carrying_items)) for order_id in split_order_id_list: if order_id in id_to_order: order = id_to_order.get(order_id) if order.demand <= capacity: logger.error(f"order {order.id} demand: {order.demand} <= {capacity}, we can not split this order.") return False return True
def get_position_info_of_vehicles(self, id_to_vehicle: dict, to_time: int): for vehicle_id, vehicle in id_to_vehicle.items(): cur_factory_id = "" arrive_time_at_current_factory = 0 leave_time_at_current_factory = 0 # still in current position if len(vehicle.cur_factory_id) > 0: if vehicle.leave_time_at_current_factory >= to_time or vehicle.destination is None: cur_factory_id = vehicle.cur_factory_id arrive_time_at_current_factory = vehicle.arrive_time_at_current_factory leave_time_at_current_factory = vehicle.leave_time_at_current_factory # in the following node if len(cur_factory_id) == 0: if vehicle.destination is None: logger.error( f"Vehicle {vehicle_id}, the current position {vehicle.position_info.cur_factory_id}, " f"the destination is None") continue if vehicle.destination.arrive_time > to_time: cur_factory_id = "" elif vehicle.destination.leave_time > to_time: cur_factory_id = vehicle.destination.id arrive_time_at_current_factory = vehicle.destination.arrive_time leave_time_at_current_factory = vehicle.destination.leave_time else: if len(vehicle.planned_route) == 0: cur_factory_id = vehicle.destination.id arrive_time_at_current_factory = vehicle.destination.arrive_time leave_time_at_current_factory = vehicle.destination.leave_time else: cur_factory_id = "" for node in vehicle.planned_route: if node.arrive_time <= to_time <= node.leave_time: cur_factory_id = node.id arrive_time_at_current_factory = node.arrive_time leave_time_at_current_factory = node.leave_time break # Considering boundary conditions if len(cur_factory_id) == 0 and vehicle.planned_route[ -1].leave_time < to_time: cur_factory_id = vehicle.planned_route[-1].id arrive_time_at_current_factory = vehicle.planned_route[ -1].arrive_time leave_time_at_current_factory = vehicle.planned_route[ -1].leave_time self.vehicle_id_to_cur_position_info[vehicle_id] = { "cur_factory_id": cur_factory_id, "arrive_time_at_current_factory": arrive_time_at_current_factory, "leave_time_at_current_factory": leave_time_at_current_factory, "update_time": to_time }
def calculate_transport_time_between_factories(self, src_factory_id, dest_factory_id): if src_factory_id == dest_factory_id: return 0 if (src_factory_id, dest_factory_id) in self.__factory_id_pair_to_time: return self.__factory_id_pair_to_time.get( (src_factory_id, dest_factory_id)) else: logger.error( f"({src_factory_id}, {dest_factory_id}) is not in time matrix") return sys.maxsize
def __get_delivery_factory_id(items): if len(items) == 0: logger.error("Length of items is 0") return "" factory_id = items[0].delivery_factory_id for item in items: if item.delivery_factory_id != factory_id: logger.error("The delivery factory of these items is not the same") return "" return factory_id
def __initialize(factory_info_file_name: str, route_info_file_name: str, instance_folder: str): """ 模拟器初始化, Initialize the simulator :param factory_info_file_name: 工厂数据文件名, name of the file containing information of factories :param route_info_file_name: 地图数据文件名, name of the file containing information of route map :param instance_folder: 测试例对应的文件夹, folder name of the instance :return: SimulateEnvironment """ route_info_file_path = os.path.join(Configs.benchmark_folder_path, route_info_file_name) factory_info_file_path = os.path.join(Configs.benchmark_folder_path, factory_info_file_name) instance_folder_path = os.path.join(Configs.benchmark_folder_path, instance_folder) # 车辆数据文件名, name of the file containing information of vehicles vehicle_info_file_path = "" # 订单数据文件名, name of the file containing information of orders data_file_path = "" for file_name in os.listdir(instance_folder_path): if file_name.startswith("vehicle"): vehicle_info_file_path = os.path.join(instance_folder_path, file_name) else: data_file_path = os.path.join(instance_folder_path, file_name) # 初始化时间, initial the start time of simulator now = datetime.datetime.now() initial_datetime = datetime.datetime(now.year, now.month, now.day) initial_time = int(time.mktime(initial_datetime.timetuple())) time_interval = Configs.ALG_RUN_FREQUENCY * 60 logger.info( f"Start time of the simulator: {initial_datetime}, time interval: {time_interval: .2f}" ) try: # 获取初始化数据, get the input id_to_order, id_to_vehicle, route_map, id_to_factory = get_initial_data( data_file_path, vehicle_info_file_path, route_info_file_path, factory_info_file_path, initial_time) # 初始化车辆位置, set the initial position of vehicles __initial_position_of_vehicles(id_to_factory, id_to_vehicle, initial_time) # return the instance of the object SimulateEnvironment return SimulateEnvironment(initial_time, time_interval, id_to_order, id_to_vehicle, id_to_factory, route_map) except Exception as exception: logger.error("Failed to read initial data") logger.error(f"Error: {exception}, {traceback.format_exc()}") return None
def subprocess_function(cmd): # 开启子进程,并连接到它们的输入/输出/错误管道,获取返回值 sub_process = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True) try: start_time = time.time() # 设置超时 sub_process.wait(Configs.MAX_RUNTIME_OF_ALGORITHM) end_time = time.time() # 返回算法运行时间和算法返回值 return end_time - start_time, sub_process.stdout.read().decode() except Exception as e: logger.error(e) sys.exit(-1)
def calculate_total_over_time(item_id_to_status_list: dict): total_over_time = 0 order_id_to_item_id_list = {} item_id_to_complete_time = {} order_id_to_committed_completion_time = {} failure_flag = False for item_id, status_info_list in item_id_to_status_list.items(): selected_status_info_list = [ status_info for status_info in status_info_list if status_info["state"] == Configs.ORDER_STATUS_TO_CODE.get( "COMPLETED") ] if len(selected_status_info_list) == 0: logger.error( f"Item {item_id} has no history of completion status") failure_flag = True continue selected_status_info_list.sort(key=lambda x: x["update_time"]) item_id_to_complete_time[item_id] = selected_status_info_list[ 0].get("update_time") order_id = selected_status_info_list[0].get("order_id") if order_id not in order_id_to_item_id_list: order_id_to_item_id_list[order_id] = [] order_id_to_item_id_list[order_id].append(item_id) if order_id not in order_id_to_committed_completion_time: order_id_to_committed_completion_time[ order_id] = selected_status_info_list[0].get( "committed_completion_time") if failure_flag: return sys.maxsize for order_id, item_id_list in order_id_to_item_id_list.items(): latest_arr_time = -1 for item_id in item_id_list: if item_id in item_id_to_complete_time: completion_time = item_id_to_complete_time.get(item_id) if completion_time > latest_arr_time: latest_arr_time = completion_time committed_completion_time = order_id_to_committed_completion_time.get( order_id) over_time = latest_arr_time - committed_completion_time if over_time > 0: total_over_time += over_time return total_over_time
def __is_returned_destination_valid(returned_destination, vehicle): origin_destination = vehicle.destination if origin_destination is not None: if returned_destination is None: logger.error( f"Vehicle {vehicle.id}, returned destination is None, " f"however the origin destination is not None.") return False else: if origin_destination.id != returned_destination.id: logger.error( f"Vehicle {vehicle.id}, returned destination id is {returned_destination.id}, " f"however the origin destination id is {origin_destination.id}." ) return False if origin_destination.arrive_time != returned_destination.arrive_time: logger.error( f"Vehicle {vehicle.id}, arrive time of returned destination is " f"{returned_destination.arrive_time}, " f"however the arrive time of origin destination is " f"{origin_destination.arrive_time}.") return False elif len(vehicle.cur_factory_id) == 0 and returned_destination is None: logger.error( f"Currently, Vehicle {vehicle.id} is not in the factory(cur_factory_id==''), " f"however, returned destination is also None, we cannot locate the vehicle." ) return False return True
def ignore_allocating_timeout_orders(self, dispatch_result): vehicle_id_to_item_list = get_item_list_of_vehicles(dispatch_result, self.id_to_vehicle) total_item_ids_in_dispatch_result = [] for vehicle_id, item_list in vehicle_id_to_item_list.items(): for item in item_list: if item.id not in total_item_ids_in_dispatch_result: total_item_ids_in_dispatch_result.append(item.id) for item_id, item in self.id_to_generated_order_item.items(): if item_id not in total_item_ids_in_dispatch_result: if item.committed_completion_time < self.cur_time: logger.error(f"{datetime.datetime.fromtimestamp(self.cur_time)}, Item {item_id} has timed out, " f"however it is still ignored in the dispatch result") return True return False
def __do_pickup_and_delivery_items_match_the_node(route: list): for node in route: factory_id = node.id pickup_items = node.pickup_items for item in pickup_items: if item.pickup_factory_id != factory_id: logger.error(f"Pickup factory of item {item.id} is {item.pickup_factory_id}, " f"however you allocate the vehicle to pickup this item in {factory_id}") return False delivery_items = node.delivery_items for item in delivery_items: if item.delivery_factory_id != factory_id: logger.error(f"Delivery factory of item {item.id} is {item.delivery_factory_id}, " f"however you allocate the vehicle to delivery this item in {factory_id}") return False return True
def run(self): used_seconds = 0 # 迭代 while True: logger.info(f"{'*' * 50}") # 确定当前时间, 取算法执行时间和模拟器的切片时间的大值 self.cur_time = self.pre_time + ( used_seconds // self.time_interval + 1) * self.time_interval logger.info( f"cur time: {datetime.datetime.fromtimestamp(self.cur_time)}, " f"pre time: {datetime.datetime.fromtimestamp(self.pre_time)}") # update the status of vehicles and orders in a given interval [self.pre_time, self.cur_time] updated_input_info = self.update_input() # 派单环节, 设计与算法交互 used_seconds, dispatch_result = self.dispatch(updated_input_info) self.time_to_dispatch_result[self.cur_time] = dispatch_result # 校验, 车辆目的地不能改变 if not Checker.check_dispatch_result( dispatch_result, self.id_to_vehicle, self.id_to_order): logger.error("Dispatch result is infeasible") return # 根据派单指令更新车辆 self.deliver_control_command_to_vehicles(dispatch_result) # 判断是否完成所有订单的派发 if self.complete_the_dispatch_of_all_orders(): break self.pre_time = self.cur_time # 若订单已经超时, 但是算法依旧未分配, 模拟终止 if self.ignore_allocating_timeout_orders(dispatch_result): logger.error('Simulator terminated') sys.exit(-1) # 模拟完成车辆剩下的订单 self.simulate_the_left_ongoing_orders_of_vehicles(self.id_to_vehicle) # 根据self.history 计算指标 self.total_score = Evaluator.calculate_total_score( self.history, self.route_map, len(self.id_to_vehicle))
def deliver_control_command_to_vehicles(self, dispatch_result): vehicle_id_to_destination = dispatch_result.vehicle_id_to_destination vehicle_id_to_planned_route = dispatch_result.vehicle_id_to_planned_route for vehicle_id, vehicle in self.id_to_vehicle.items(): if vehicle_id not in vehicle_id_to_destination: logger.error(f"algorithm does not output the destination of vehicle {vehicle_id}") continue if vehicle_id not in vehicle_id_to_planned_route: logger.error(f"algorithm does not output the planned route of vehicle {vehicle_id}") continue vehicle.destination = vehicle_id_to_destination.get(vehicle_id) if vehicle.destination is not None: vehicle.destination.update_service_time() vehicle.planned_route = vehicle_id_to_planned_route.get(vehicle_id) for node in vehicle.planned_route: if node is not None: node.update_service_time()
def __contain_duplicate_items(route, carrying_items): item_id_list = [] while not carrying_items.is_empty(): item = carrying_items.pop() if item.id not in item_id_list: item_id_list.append(item.id) else: logger.error(f"Item {item.id}: duplicate item id") return True for node in route: pickup_items = node.pickup_items for item in pickup_items: if item.id not in item_id_list: item_id_list.append(item.id) else: logger.error(f"Item {item.id}: duplicate item id") return True return False
def get_algorithm_calling_command(): files = os.listdir(Configs.root_folder_path) for file in files: # 调度算法的入口文件必须以main_algorithm开头 if file.startswith(Configs.ALGORITHM_ENTRY_FILE_NAME): end_name = file.split('.')[-1] algorithm_language = Configs.ALGORITHM_LANGUAGE_MAP.get(end_name) if algorithm_language == 'python': return 'python {}'.format(file) elif algorithm_language == 'java': return 'java {}'.format(file.split('.')[0]) # c和c++调用方式一样,但系统不同调用方式有异 elif algorithm_language == 'c': system = platform.system() if system == 'Windows': return file elif system == 'Linux': os.system(f'chmod 777 {file}') return './{}'.format(file) logger.error('Can not find main_algorithm file.') sys.exit(-1)
def update_status_of_vehicles(self, vehicle_id_to_cur_position_info, vehicle_id_to_destination, vehicle_id_to_carry_items): for vehicle_id, vehicle in self.id_to_vehicle.items(): if vehicle_id in vehicle_id_to_cur_position_info: cur_position_info = vehicle_id_to_cur_position_info.get( vehicle_id) vehicle.set_cur_position_info( cur_position_info.get("cur_factory_id"), cur_position_info.get("update_time"), cur_position_info.get("arrive_time_at_current_factory"), cur_position_info.get("leave_time_at_current_factory")) else: logger.error( f"Vehicle {vehicle_id} does not have updated position information" ) if vehicle_id in vehicle_id_to_destination: vehicle.destination = vehicle_id_to_destination.get(vehicle_id) else: logger.error( f"Vehicle {vehicle_id} does not have the destination information" ) if vehicle_id in vehicle_id_to_carry_items: vehicle.carrying_items = vehicle_id_to_carry_items.get( vehicle_id) else: logger.error( f"Vehicle {vehicle_id} does not have the information of carrying items" ) vehicle.planned_route = []
def __meet_capacity_constraint(route: list, carrying_items, capacity): left_capacity = capacity # Stack while not carrying_items.is_empty(): item = carrying_items.pop() left_capacity -= item.demand if left_capacity < 0: logger.error(f"left capacity {left_capacity} < 0") return False for node in route: delivery_items = node.delivery_items pickup_items = node.pickup_items for item in delivery_items: left_capacity += item.demand if left_capacity > capacity: logger.error( f"left capacity {left_capacity} > capacity {capacity}") return False for item in pickup_items: left_capacity -= item.demand if left_capacity < 0: logger.error(f"left capacity {left_capacity} < 0") return False return True
def check_dispatch_result(dispatch_result, id_to_vehicle: dict, id_to_order: dict): vehicle_id_to_destination = dispatch_result.vehicle_id_to_destination vehicle_id_to_planned_route = dispatch_result.vehicle_id_to_planned_route # 检查是否所有的车辆都有返回值 if len(vehicle_id_to_destination) != len(id_to_vehicle): logger.error(f"Num of returned destination {len(vehicle_id_to_destination)} " f"is not equal to vehicle number {len(id_to_vehicle)}") return False if len(vehicle_id_to_planned_route) != len(id_to_vehicle): logger.error(f"Num of returned planned route {len(vehicle_id_to_planned_route)} " f"is not equal to vehicle number {len(id_to_vehicle)}") return False # 逐个检查各车辆路径 for vehicle_id, vehicle in id_to_vehicle.items(): if vehicle_id not in vehicle_id_to_destination: logger.error(f"Destination information of Vehicle {vehicle_id} is not in the returned result") return False # check destination destination_in_result = vehicle_id_to_destination.get(vehicle_id) origin_destination = vehicle.destination if origin_destination is not None: if destination_in_result is None: logger.error(f"Vehicle {vehicle_id}, returned destination is None, " f"however the origin destination is not None.") return False else: if origin_destination.id != destination_in_result.id: logger.error(f"Vehicle {vehicle_id}, returned destination id is {destination_in_result.id}, " f"however the origin destination id is {origin_destination.id}.") return False if origin_destination.arrive_time != destination_in_result.arrive_time: logger.error(f"Vehicle {vehicle_id}, arrive time of returned destination is " f"{destination_in_result.arrive_time}, " f"however the arrive time of origin destination is " f"{origin_destination.arrive_time}.") return False # check routes if vehicle_id not in vehicle_id_to_planned_route: logger.error(f"Planned route of Vehicle {vehicle_id} is not in the returned result") return False route = [] if destination_in_result is not None: route.append(destination_in_result) route.extend(vehicle_id_to_planned_route.get(vehicle_id)) if len(route) > 0: # check capacity if not Checker.__meet_capacity_constraint(route, copy.deepcopy(vehicle.carrying_items), vehicle.board_capacity): logger.error(f"Vehicle {vehicle_id} violates the capacity constraint") return False # check LIFO if not Checker.__meet_loading_and_unloading_constraint(route, copy.deepcopy(vehicle.carrying_items)): logger.error(f"Vehicle {vehicle_id} violates the LIFO constraint") return False # # check duplicated node id # if Checker.contain_duplicate_nodes(complete_route): # return False # check duplicated item id if Checker.__contain_duplicate_items(route, copy.deepcopy(vehicle.carrying_items)): return False # check order splitting if not Checker.__meet_order_splitting_constraint(dispatch_result, id_to_vehicle, id_to_order): return False return True
def work(self, vehicle): cur_factory_id = vehicle.cur_factory_id # 当前在工厂: 1. 还处于装卸过程(占用货口资源); 2. 停车状态(不占用货口资源) if len(cur_factory_id) > 0: # 1.还处于装卸过程(占用货口资源); if vehicle.leave_time_at_current_factory > self.env.now: resource = self.factory_id_to_dock_resource.get(cur_factory_id) with resource.request() as req: yield req yield self.env.timeout(vehicle.leave_time_at_current_factory - self.env.now) # 2. 停车状态(不占用货口资源) else: vehicle.leave_time_at_current_factory = self.env.now if vehicle.destination is None: if len(cur_factory_id) == 0: logger.error(f"Vehicle {vehicle.id}: both the current factory and the destination are None!!!") return if len(cur_factory_id) > 0: next_factory_id = vehicle.destination.id transport_time = self.route_map.calculate_transport_time_between_factories(cur_factory_id, next_factory_id) yield self.env.timeout(transport_time) else: # driving towards destination arr_time = vehicle.destination.arrive_time if arr_time >= self.env.now: yield self.env.timeout(arr_time - self.env.now) else: logger.error(f"Vehicle {vehicle.id} is driving toward the destination, " f"however current time {datetime.datetime.fromtimestamp(self.env.now)} is greater than " f"the arrival time {datetime.datetime.fromtimestamp(arr_time)} of destination!!!") vehicle.destination.arrive_time = self.env.now service_time = vehicle.destination.service_time cur_factory_id = vehicle.destination.id resource = self.factory_id_to_dock_resource.get(cur_factory_id) with resource.request() as req: yield req yield self.env.timeout(service_time + Configs.DOCK_APPROACHING_TIME) vehicle.destination.leave_time = self.env.now # driving towards the left nodes for node in vehicle.planned_route: next_factory_id = node.id # 计算运输时间 transport_time = self.route_map.calculate_transport_time_between_factories(cur_factory_id, next_factory_id) yield self.env.timeout(transport_time) # 计算服务时间 arr_time = self.env.now service_time = node.service_time resource = self.factory_id_to_dock_resource.get(next_factory_id) with resource.request() as req: yield req yield self.env.timeout(service_time + Configs.DOCK_APPROACHING_TIME) leave_time = self.env.now node.arrive_time = arr_time node.leave_time = leave_time cur_factory_id = next_factory_id
test_instances = selected_instances else: test_instances = Configs.all_test_instances score_list = [] for idx in test_instances: # Initial the log log_file_name = f"dpdp_{datetime.datetime.now().strftime('%y%m%d%H%M%S')}.log" ini_logger(log_file_name) instance = "instance_%d" % idx logger.info(f"Start to run {instance}") try: score = simulate(Configs.factory_info_file, Configs.route_info_file, instance) score_list.append(score) except Exception as e: logger.error("Failed to run simulator") logger.error(f"Error: {e}, {traceback.format_exc()}") # 删除日志句柄 remove_file_handler_of_logging(log_file_name) avg_score = np.mean(score_list) # with report(True) as logs: # logs.log_metrics('score', [avg_score]) print(score_list) print(avg_score) print("Happy Ending")
def check_dispatch_result(dispatch_result, id_to_vehicle: dict, id_to_order: dict): vehicle_id_to_destination = dispatch_result.vehicle_id_to_destination vehicle_id_to_planned_route = dispatch_result.vehicle_id_to_planned_route # 检查是否所有的车辆都有返回值 if len(vehicle_id_to_destination) != len(id_to_vehicle): logger.error( f"Num of returned destination {len(vehicle_id_to_destination)} " f"is not equal to vehicle number {len(id_to_vehicle)}") return False if len(vehicle_id_to_planned_route) != len(id_to_vehicle): logger.error( f"Num of returned planned route {len(vehicle_id_to_planned_route)} " f"is not equal to vehicle number {len(id_to_vehicle)}") return False # 逐个检查各车辆路径 for vehicle_id, vehicle in id_to_vehicle.items(): if vehicle_id not in vehicle_id_to_destination: logger.error( f"Destination information of Vehicle {vehicle_id} is not in the returned result" ) return False # check destination destination_in_result = vehicle_id_to_destination.get(vehicle_id) if not Checker.__is_returned_destination_valid( destination_in_result, vehicle): logger.error( f"Returned destination of Vehicle {vehicle_id} is not valid" ) return False # check routes if vehicle_id not in vehicle_id_to_planned_route: logger.error( f"Planned route of Vehicle {vehicle_id} is not in the returned result" ) return False route = [] if destination_in_result is not None: route.append(destination_in_result) route.extend(vehicle_id_to_planned_route.get(vehicle_id)) if len(route) > 0: # check capacity if not Checker.__meet_capacity_constraint( route, copy.deepcopy(vehicle.carrying_items), vehicle.board_capacity): logger.error( f"Vehicle {vehicle_id} violates the capacity constraint" ) return False # check LIFO if not Checker.__meet_loading_and_unloading_constraint( route, copy.deepcopy(vehicle.carrying_items)): logger.error( f"Vehicle {vehicle_id} violates the LIFO constraint") return False # check adjacent-duplicated nodes Checker.__contain_duplicated_nodes(vehicle_id, route) # check duplicated item id if Checker.__contain_duplicate_items( route, copy.deepcopy(vehicle.carrying_items)): return False if not Checker.__do_pickup_and_delivery_items_match_the_node( route): return False # check order splitting if not Checker.__meet_order_splitting_constraint( dispatch_result, id_to_vehicle, id_to_order): return False return True
def dispatch_orders_to_vehicles(id_to_unallocated_order_item: dict, id_to_vehicle: dict, id_to_factory: dict): """ :param id_to_unallocated_order_item: item_id ——> OrderItem object(state: "GENERATED") :param id_to_vehicle: vehicle_id ——> Vehicle object :param id_to_factory: factory_id ——> factory object """ vehicle_id_to_destination = {} vehicle_id_to_planned_route = {} # dealing with the carrying items of vehicles (处理车辆身上已经装载的货物) for vehicle_id, vehicle in id_to_vehicle.items(): unloading_sequence_of_items = vehicle.get_unloading_sequence() vehicle_id_to_planned_route[vehicle_id] = [] if len(unloading_sequence_of_items) > 0: delivery_item_list = [] factory_id = unloading_sequence_of_items[0].delivery_factory_id for item in unloading_sequence_of_items: if item.delivery_factory_id == factory_id: delivery_item_list.append(item) else: factory = id_to_factory.get(factory_id) node = Node(factory_id, factory.lng, factory.lat, [], copy.copy(delivery_item_list)) vehicle_id_to_planned_route[vehicle_id].append(node) delivery_item_list = [item] factory_id = item.delivery_factory_id if len(delivery_item_list) > 0: factory = id_to_factory.get(factory_id) node = Node(factory_id, factory.lng, factory.lat, [], copy.copy(delivery_item_list)) vehicle_id_to_planned_route[vehicle_id].append(node) # for the empty vehicle, it has been allocated to the order, but have not yet arrived at the pickup factory pre_matching_item_ids = [] for vehicle_id, vehicle in id_to_vehicle.items(): if vehicle.carrying_items.is_empty( ) and vehicle.destination is not None: pickup_items = vehicle.destination.pickup_items pickup_node, delivery_node = __create_pickup_and_delivery_nodes_of_items( pickup_items, id_to_factory) vehicle_id_to_planned_route[vehicle_id].append(pickup_node) vehicle_id_to_planned_route[vehicle_id].append(delivery_node) pre_matching_item_ids.extend([item.id for item in pickup_items]) # dispatch unallocated orders to vehicles capacity = __get_capacity_of_vehicle(id_to_vehicle) order_id_to_items = {} for item_id, item in id_to_unallocated_order_item.items(): if item_id in pre_matching_item_ids: continue order_id = item.order_id if order_id not in order_id_to_items: order_id_to_items[order_id] = [] order_id_to_items[order_id].append(item) vehicle_index = 0 vehicles = [vehicle for vehicle in id_to_vehicle.values()] for order_id, items in order_id_to_items.items(): demand = __calculate_demand(items) if demand > capacity: cur_demand = 0 tmp_items = [] for item in items: if cur_demand + item.demand > capacity: pickup_node, delivery_node = __create_pickup_and_delivery_nodes_of_items( tmp_items, id_to_factory) if pickup_node is None or delivery_node is None: continue vehicle = vehicles[vehicle_index] vehicle_id_to_planned_route[vehicle.id].append(pickup_node) vehicle_id_to_planned_route[vehicle.id].append( delivery_node) vehicle_index = (vehicle_index + 1) % len(vehicles) tmp_items = [] cur_demand = 0 tmp_items.append(item) cur_demand += item.demand if len(tmp_items) > 0: pickup_node, delivery_node = __create_pickup_and_delivery_nodes_of_items( tmp_items, id_to_factory) if pickup_node is None or delivery_node is None: continue vehicle = vehicles[vehicle_index] vehicle_id_to_planned_route[vehicle.id].append(pickup_node) vehicle_id_to_planned_route[vehicle.id].append(delivery_node) else: pickup_node, delivery_node = __create_pickup_and_delivery_nodes_of_items( items, id_to_factory) if pickup_node is None or delivery_node is None: continue vehicle = vehicles[vehicle_index] vehicle_id_to_planned_route[vehicle.id].append(pickup_node) vehicle_id_to_planned_route[vehicle.id].append(delivery_node) vehicle_index = (vehicle_index + 1) % len(vehicles) # create the output of the algorithm for vehicle_id, vehicle in id_to_vehicle.items(): origin_planned_route = vehicle_id_to_planned_route.get(vehicle_id) destination = None planned_route = [] # determine the destination if vehicle.destination is not None: if len(origin_planned_route) == 0: logger.error(f"Planned route of vehicle {vehicle_id} is wrong") else: destination = origin_planned_route[0] destination.arrive_time = vehicle.destination.arrive_time planned_route = [ origin_planned_route[i] for i in range(1, len(origin_planned_route)) ] elif len(origin_planned_route) > 0: destination = origin_planned_route[0] planned_route = [ origin_planned_route[i] for i in range(1, len(origin_planned_route)) ] vehicle_id_to_destination[vehicle_id] = destination vehicle_id_to_planned_route[vehicle_id] = planned_route return vehicle_id_to_destination, vehicle_id_to_planned_route
# of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE import traceback from src.utils.logging_engine import logger from algorithm.algorithm_demo import scheduling if __name__ == '__main__': try: scheduling() print("SUCCESS") except Exception as e: logger.error("Failed to run algorithm") logger.error(f"Error: {e}, {traceback.format_exc()}") print("FAIL")