From 2fa19599467263dcf582bb12906e03328e03b4a4 Mon Sep 17 00:00:00 2001 From: zhang <zc857179121@qq.com> Date: 星期三, 02 七月 2025 13:12:26 +0800 Subject: [PATCH] 初版提交 --- algorithm_system/algorithms/task_allocation.py | 687 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 687 insertions(+), 0 deletions(-) diff --git a/algorithm_system/algorithms/task_allocation.py b/algorithm_system/algorithms/task_allocation.py new file mode 100644 index 0000000..883ed50 --- /dev/null +++ b/algorithm_system/algorithms/task_allocation.py @@ -0,0 +1,687 @@ +""" +浠诲姟鍒嗛厤绠楁硶 +""" +import time +import random +import logging +from typing import Dict, List, Tuple, Optional, Set, Any +from collections import defaultdict +from abc import ABC, abstractmethod + +from common.data_models import TaskData, AGVStatus, TaskAssignment, BackpackData +from algorithm_system.models.agv_model import AGVModel, AGVModelManager +from common.utils import get_coordinate_from_path_id, calculate_distance, calculate_manhattan_distance +from dataclasses import dataclass + + +class TaskAllocation(ABC): + """浠诲姟鍒嗛厤绠楁硶鍩虹被""" + + def __init__(self, agv_manager: AGVModelManager): + """ + 鍒濆鍖栦换鍔″垎閰嶇畻娉� + + Args: + agv_manager: AGV妯″瀷绠$悊鍣� + """ + self.agv_manager = agv_manager + self.logger = logging.getLogger(__name__) + + @abstractmethod + def allocate_tasks(self, tasks: List[TaskData]) -> List[TaskAssignment]: + """ + 鍒嗛厤浠诲姟缁橝GV + + Args: + tasks: 寰呭垎閰嶇殑浠诲姟鍒楄〃 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉鍒楄〃 + """ + pass + + def find_available_backpack_slot(self, agv_status: AGVStatus) -> Optional[int]: + """ + 鏌ユ壘AGV鐨勫彲鐢ㄨ儗绡撲綅缃� + + Args: + agv_status: AGV鐘舵�佷俊鎭� + + Returns: + Optional[int]: 鍙敤鐨勮儗绡撲綅缃紪鍙凤紝濡傛灉娌℃湁鍙敤浣嶇疆鍒欒繑鍥濶one + """ + if not agv_status.backpack: + # 濡傛灉娌℃湁鑳岀瘬淇℃伅锛屽亣璁句粠绗竴涓綅缃紑濮� + self.logger.warning(f"AGV {agv_status.agvId} 娌℃湁鑳岀瘬淇℃伅锛屽垎閰嶅埌浣嶇疆0") + return 0 + + # 鏌ユ壘绌洪棽涓旀湭鎵ц浠诲姟鐨勮儗绡撲綅缃� + for backpack_item in agv_status.backpack: + if not backpack_item.loaded and not backpack_item.execute and not backpack_item.taskId: + self.logger.debug(f"AGV {agv_status.agvId} 鎵惧埌鍙敤鑳岀瘬浣嶇疆: {backpack_item.index}") + return backpack_item.index + + # 濡傛灉鎵�鏈変綅缃兘琚崰鐢紝杩斿洖None + self.logger.debug(f"AGV {agv_status.agvId} 娌℃湁鍙敤鐨勮儗绡撲綅缃�") + return None + + def get_agv_available_capacity(self, agv_status: AGVStatus) -> int: + """ + 鑾峰彇AGV鐨勫彲鐢ㄨ儗绡撳閲� + + Args: + agv_status: AGV鐘舵�佷俊鎭� + + Returns: + int: 鍙敤鑳岀瘬鏁伴噺 + """ + if not agv_status.backpack: + return 1 # 鍋囪鑷冲皯鏈変竴涓儗绡撲綅缃� + + available_count = 0 + for backpack_item in agv_status.backpack: + if not backpack_item.loaded and not backpack_item.execute and not backpack_item.taskId: + available_count += 1 + + return available_count + + def assign_task_with_backpack(self, agv_model, task: TaskData, lev_id: int) -> bool: + """ + 灏嗕换鍔″垎閰嶇粰AGV鐨勬寚瀹氳儗绡撲綅缃� + + Args: + agv_model: AGV妯″瀷 + task: 浠诲姟鏁版嵁 + lev_id: 鑳岀瘬浣嶇疆缂栧彿 + + Returns: + bool: 鍒嗛厤鏄惁鎴愬姛 + """ + try: + # 浣跨敤AGV妯″瀷鐨刟ssign_task鏂规硶 + success = agv_model.assign_task( + task_id=task.taskId, + priority=task.priority, + start_code=task.start, + end_code=task.end + ) + + if success: + self.logger.info(f"浠诲姟 {task.taskId} 鎴愬姛鍒嗛厤缁橝GV {agv_model.agvId} 鐨勮儗绡撲綅缃� {lev_id}") + return True + else: + self.logger.warning(f"浠诲姟 {task.taskId} 鍒嗛厤缁橝GV {agv_model.agvId} 澶辫触") + return False + + except Exception as e: + self.logger.error(f"鍒嗛厤浠诲姟鏃跺彂鐢熷紓甯�: {e}") + return False + + +class NearestFirstAllocation(TaskAllocation): + """鏈�杩戜紭鍏堝垎閰嶇畻娉�""" + + def allocate_tasks(self, tasks: List[TaskData]) -> List[TaskAssignment]: + """ + 浣跨敤鏈�杩戜紭鍏堢瓥鐣ュ垎閰嶄换鍔� + + Args: + tasks: 寰呭垎閰嶇殑浠诲姟鍒楄〃 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉鍒楄〃 + """ + if not tasks: + return [] + + # 鑾峰彇鍙敤鐨凙GV + available_agvs = self.agv_manager.get_available_agvs() + + if not available_agvs: + self.logger.warning("娌℃湁鍙敤鐨凙GV杩涜浠诲姟鍒嗛厤") + return [] + + # 1. 棣栧厛妫�鏌ヤ换鍔℃槸鍚﹀凡缁忓垎閰嶏紝閬垮厤閲嶅鍒嗛厤 + already_assigned_tasks = set() + all_agvs = self.agv_manager.get_all_agvs() + for agv in all_agvs: + if agv.backpack: + for backpack_item in agv.backpack: + if backpack_item.taskId: + already_assigned_tasks.add(backpack_item.taskId) + self.logger.info(f"浠诲姟 {backpack_item.taskId} 宸插垎閰嶇粰 AGV {agv.agvId}锛岃烦杩囬噸澶嶅垎閰�") + + # 2. 杩囨护鎺夊凡鍒嗛厤鐨勪换鍔� + unassigned_tasks = [task for task in tasks if task.taskId not in already_assigned_tasks] + + if not unassigned_tasks: + self.logger.info("鎵�鏈変换鍔¢兘宸插垎閰嶏紝鏃犻渶閲嶆柊鍒嗛厤") + return [] + + self.logger.info(f"鎬讳换鍔℃暟: {len(tasks)}, 宸插垎閰�: {len(already_assigned_tasks)}, 寰呭垎閰�: {len(unassigned_tasks)}") + + assignments = [] + path_mapping = self.agv_manager.path_mapping + + # 瀵规瘡涓换鍔℃壘鍒版渶杩戠殑AGV + for task in unassigned_tasks: + if not available_agvs: + break + + # 鑾峰彇浠诲姟璧风偣鍧愭爣 + task_start_coord = get_coordinate_from_path_id(task.start, path_mapping) + if not task_start_coord: + self.logger.warning(f"鏃犳硶鑾峰彇浠诲姟 {task.taskId} 璧风偣 {task.start} 鐨勫潗鏍�") + continue + + # 鎵惧埌璺濈鏈�杩戠殑AGV + nearest_agv = None + min_distance = float('inf') + + for agv in available_agvs: + if agv.coordinates: + distance = calculate_manhattan_distance(agv.coordinates, task_start_coord) + + # 濡傛灉AGV宸叉湁浠诲姟锛岃绠楀畬鎴愬綋鍓嶄换鍔″悗鍒版柊浠诲姟璧风偣鐨勮窛绂� + if agv.current_task_count > 0: + # 绠�鍖栵細鍋囪AGV闇�瑕侀澶栨椂闂村畬鎴愬綋鍓嶄换鍔� + distance += agv.current_task_count * 10 + + if distance < min_distance: + min_distance = distance + nearest_agv = agv + + if nearest_agv and nearest_agv.can_accept_task(task.priority): + # 鑾峰彇AGV鐨勫師濮嬬姸鎬佹暟鎹潵鏌ユ壘鍙敤鑳岀瘬浣嶇疆 + agv_status = None + for agv_state in self.agv_manager.get_all_agv_status(): + if agv_state.agvId == nearest_agv.agvId: + agv_status = agv_state + break + + if agv_status: + # 鏌ユ壘鍙敤鐨勮儗绡撲綅缃� + available_lev_id = self.find_available_backpack_slot(agv_status) + + if available_lev_id is not None: + # 鍒嗛厤浠诲姟鍒版寚瀹氳儗绡撲綅缃� + success = self.assign_task_with_backpack(nearest_agv, task, available_lev_id) + if success: + assignments.append(TaskAssignment( + taskId=task.taskId, + agvId=nearest_agv.agvId, + lev_id=available_lev_id + )) + + self.logger.info(f"浠诲姟 {task.taskId} 鍒嗛厤缁欐渶杩戠殑AGV {nearest_agv.agvId}锛岃儗绡撲綅缃�: {available_lev_id}锛岃窛绂�: {min_distance}") + + # 妫�鏌GV鏄惁杩樻湁鍙敤鑳岀瘬浣嶇疆 + remaining_capacity = self.get_agv_available_capacity(agv_status) - 1 + if remaining_capacity <= 0: + available_agvs.remove(nearest_agv) + else: + self.logger.warning(f"浠诲姟 {task.taskId} 鍒嗛厤缁橝GV {nearest_agv.agvId} 澶辫触") + else: + self.logger.warning(f"AGV {nearest_agv.agvId} 娌℃湁鍙敤鐨勮儗绡撲綅缃�") + available_agvs.remove(nearest_agv) + else: + self.logger.warning(f"鏃犳硶鑾峰彇AGV {nearest_agv.agvId} 鐨勭姸鎬佷俊鎭�") + + return assignments + + +class LoadBalancedAllocation(TaskAllocation): + """璐熻浇鍧囪 鍒嗛厤绠楁硶""" + + def allocate_tasks(self, tasks: List[TaskData]) -> List[TaskAssignment]: + """ + 浣跨敤璐熻浇鍧囪 绛栫暐鍒嗛厤浠诲姟 + + Args: + tasks: 寰呭垎閰嶇殑浠诲姟鍒楄〃 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉鍒楄〃 + """ + if not tasks: + return [] + + # 鑾峰彇鎵�鏈堿GV + all_agvs = self.agv_manager.get_all_agvs() + + if not all_agvs: + self.logger.warning("娌℃湁AGV杩涜浠诲姟鍒嗛厤") + return [] + + # 1. 棣栧厛妫�鏌ヤ换鍔℃槸鍚﹀凡缁忓垎閰嶏紝閬垮厤閲嶅鍒嗛厤 + already_assigned_tasks = set() + for agv in all_agvs: + if agv.backpack: + for backpack_item in agv.backpack: + if backpack_item.taskId: + already_assigned_tasks.add(backpack_item.taskId) + self.logger.info(f"浠诲姟 {backpack_item.taskId} 宸插垎閰嶇粰 AGV {agv.agvId}锛岃烦杩囬噸澶嶅垎閰�") + + # 2. 杩囨护鎺夊凡鍒嗛厤鐨勪换鍔� + unassigned_tasks = [task for task in tasks if task.taskId not in already_assigned_tasks] + + if not unassigned_tasks: + self.logger.info("鎵�鏈変换鍔¢兘宸插垎閰嶏紝鏃犻渶閲嶆柊鍒嗛厤") + return [] + + self.logger.info(f"鎬讳换鍔℃暟: {len(tasks)}, 宸插垎閰�: {len(already_assigned_tasks)}, 寰呭垎閰�: {len(unassigned_tasks)}") + + assignments = [] + path_mapping = self.agv_manager.path_mapping + + # 鎸変紭鍏堢骇鎺掑簭浠诲姟 + sorted_tasks = sorted(unassigned_tasks, key=lambda t: t.priority, reverse=True) + + # 瀵规瘡涓换鍔″垎閰嶇粰璐熻浇鏈�浣庣殑AGV + for task in sorted_tasks: + # 鑾峰彇浠诲姟璧风偣鍧愭爣 + task_start_coord = get_coordinate_from_path_id(task.start, path_mapping) + if not task_start_coord: + self.logger.warning(f"鏃犳硶鑾峰彇浠诲姟 {task.taskId} 璧风偣 {task.start} 鐨勫潗鏍�") + continue + + # 鎸夎礋杞藉拰璺濈鎺掑簭AGV + agv_scores = [] + for agv in all_agvs: + if not agv.can_accept_task(task.priority): + continue + + # 璁$畻璐熻浇寰楀垎锛堣礋杞借秺浣庡緱鍒嗚秺楂橈級 + load_score = 1.0 - agv.get_workload_ratio() + + # 璁$畻璺濈寰楀垎锛堣窛绂昏秺杩戝緱鍒嗚秺楂橈級 + distance_score = 0.0 + if agv.coordinates and task_start_coord: + distance = calculate_manhattan_distance(agv.coordinates, task_start_coord) + distance_score = 1.0 / (1.0 + distance / 100.0) # 褰掍竴鍖栬窛绂诲緱鍒� + + # 璁$畻鏁堢巼寰楀垎 + efficiency_score = agv.calculate_efficiency_score(path_mapping) + + # 缁煎悎寰楀垎 + total_score = 0.4 * load_score + 0.3 * distance_score + 0.3 * efficiency_score + agv_scores.append((agv, total_score)) + + if not agv_scores: + # 璇︾粏鍒嗘瀽涓轰粈涔堟病鏈堿GV鍙互鎺ュ彈浠诲姟 + total_agvs = len(all_agvs) + busy_count = 0 + low_battery_count = 0 + overloaded_count = 0 + status_invalid_count = 0 + + for agv in all_agvs: + if agv.is_overloaded(): + overloaded_count += 1 + elif str(agv.status) not in ["0", "1", "2"]: + status_invalid_count += 1 + elif agv.need_charging(): + low_battery_count += 1 + else: + busy_count += 1 + + self.logger.warning(f"娌℃湁AGV鍙互鎺ュ彈浠诲姟 {task.taskId} - 璇︾粏鍒嗘瀽:") + self.logger.warning(f" 鎬籄GV鏁�: {total_agvs}") + self.logger.warning(f" 浠诲姟婊¤浇: {overloaded_count}") + self.logger.warning(f" 鐘舵�佸紓甯�: {status_invalid_count}") + self.logger.warning(f" 鐢甸噺杩囦綆: {low_battery_count}") + self.logger.warning(f" 鍏朵粬鍘熷洜: {busy_count}") + self.logger.warning(f" 浠诲姟浼樺厛绾�: {task.priority}") + continue + + # 閫夋嫨寰楀垎鏈�楂樼殑AGV + agv_scores.sort(key=lambda x: x[1], reverse=True) + best_agv = agv_scores[0][0] + + # 鑾峰彇AGV鐨勫師濮嬬姸鎬佹暟鎹潵鏌ユ壘鍙敤鑳岀瘬浣嶇疆 + agv_status = self.agv_manager.get_agv_status(best_agv.agvId) + + if agv_status: + # 鏌ユ壘鍙敤鐨勮儗绡撲綅缃� + available_lev_id = self.find_available_backpack_slot(agv_status) + + if available_lev_id is not None: + # 鍒嗛厤浠诲姟鍒版寚瀹氳儗绡撲綅缃� + success = self.assign_task_with_backpack(best_agv, task, available_lev_id) + if success: + assignments.append(TaskAssignment( + taskId=task.taskId, + agvId=best_agv.agvId, + lev_id=available_lev_id + )) + + self.logger.info(f"浠诲姟 {task.taskId} 鍒嗛厤缁欒礋杞藉潎琛$殑AGV {best_agv.agvId}锛岃儗绡撲綅缃�: {available_lev_id}锛屽緱鍒�: {agv_scores[0][1]:.3f}") + else: + self.logger.warning(f"浠诲姟 {task.taskId} 鍒嗛厤缁橝GV {best_agv.agvId} 澶辫触") + else: + self.logger.warning(f"AGV {best_agv.agvId} 娌℃湁鍙敤鐨勮儗绡撲綅缃�") + else: + self.logger.warning(f"鏃犳硶鑾峰彇AGV {best_agv.agvId} 鐨勭姸鎬佷俊鎭�") + + return assignments + + +class PriorityFirstAllocation(TaskAllocation): + """浼樺厛绾т紭鍏堝垎閰嶇畻娉�""" + + def allocate_tasks(self, tasks: List[TaskData]) -> List[TaskAssignment]: + """ + 浣跨敤浼樺厛绾т紭鍏堢瓥鐣ュ垎閰嶄换鍔� + + Args: + tasks: 寰呭垎閰嶇殑浠诲姟鍒楄〃 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉鍒楄〃 + """ + if not tasks: + return [] + + # 鑾峰彇鍙敤鐨凙GV + available_agvs = self.agv_manager.get_available_agvs() + + if not available_agvs: + self.logger.warning("娌℃湁鍙敤鐨凙GV杩涜浠诲姟鍒嗛厤") + return [] + + # 1. 棣栧厛妫�鏌ヤ换鍔℃槸鍚﹀凡缁忓垎閰嶏紝閬垮厤閲嶅鍒嗛厤 + already_assigned_tasks = set() + all_agvs = self.agv_manager.get_all_agvs() + for agv in all_agvs: + if agv.backpack: + for backpack_item in agv.backpack: + if backpack_item.taskId: + already_assigned_tasks.add(backpack_item.taskId) + self.logger.info(f"浠诲姟 {backpack_item.taskId} 宸插垎閰嶇粰 AGV {agv.agvId}锛岃烦杩囬噸澶嶅垎閰�") + + # 2. 杩囨护鎺夊凡鍒嗛厤鐨勪换鍔� + unassigned_tasks = [task for task in tasks if task.taskId not in already_assigned_tasks] + + if not unassigned_tasks: + self.logger.info("鎵�鏈変换鍔¢兘宸插垎閰嶏紝鏃犻渶閲嶆柊鍒嗛厤") + return [] + + self.logger.info(f"鎬讳换鍔℃暟: {len(tasks)}, 宸插垎閰�: {len(already_assigned_tasks)}, 寰呭垎閰�: {len(unassigned_tasks)}") + + # 鎸変紭鍏堢骇鎺掑簭浠诲姟锛堥珮浼樺厛绾у湪鍓嶏級 + sorted_tasks = sorted(unassigned_tasks, key=lambda t: t.priority, reverse=True) + + assignments = [] + path_mapping = self.agv_manager.path_mapping + + # 浼樺厛鍒嗛厤楂樹紭鍏堢骇浠诲姟 + for task in sorted_tasks: + if not available_agvs: + break + + # 鑾峰彇浠诲姟璧风偣鍧愭爣 + task_start_coord = get_coordinate_from_path_id(task.start, path_mapping) + if not task_start_coord: + self.logger.warning(f"鏃犳硶鑾峰彇浠诲姟 {task.taskId} 璧风偣 {task.start} 鐨勫潗鏍�") + continue + + # 涓洪珮浼樺厛绾т换鍔¢�夋嫨鏈�浣矨GV + best_agv = None + best_score = -1 + + for agv in available_agvs: + if not agv.can_accept_task(task.priority): + continue + + # 璁$畻缁煎悎寰楀垎 + distance_score = 0.0 + if agv.coordinates and task_start_coord: + distance = calculate_manhattan_distance(agv.coordinates, task_start_coord) + distance_score = 1.0 / (1.0 + distance / 50.0) + + efficiency_score = agv.calculate_efficiency_score(path_mapping) + capacity_score = agv.get_task_capacity() / agv.max_capacity + + # 楂樹紭鍏堢骇浠诲姟鏇存敞閲嶆晥鐜囧拰璺濈 + total_score = 0.5 * distance_score + 0.3 * efficiency_score + 0.2 * capacity_score + + if total_score > best_score: + best_score = total_score + best_agv = agv + + if best_agv: + # 鑾峰彇AGV鐨勫師濮嬬姸鎬佹暟鎹潵鏌ユ壘鍙敤鑳岀瘬浣嶇疆 + agv_status = self.agv_manager.get_agv_status(best_agv.agvId) + + if agv_status: + # 鏌ユ壘鍙敤鐨勮儗绡撲綅缃� + available_lev_id = self.find_available_backpack_slot(agv_status) + + if available_lev_id is not None: + # 鍒嗛厤浠诲姟鍒版寚瀹氳儗绡撲綅缃� + success = self.assign_task_with_backpack(best_agv, task, available_lev_id) + if success: + assignments.append(TaskAssignment( + taskId=task.taskId, + agvId=best_agv.agvId, + lev_id=available_lev_id + )) + + self.logger.info(f"楂樹紭鍏堢骇浠诲姟 {task.taskId} (浼樺厛绾�: {task.priority}) 鍒嗛厤缁橝GV {best_agv.agvId}锛岃儗绡撲綅缃�: {available_lev_id}") + + # 妫�鏌GV鏄惁杩樻湁鍙敤鑳岀瘬浣嶇疆 + remaining_capacity = self.get_agv_available_capacity(agv_status) - 1 + if remaining_capacity <= 0: + available_agvs.remove(best_agv) + else: + self.logger.warning(f"浠诲姟 {task.taskId} 鍒嗛厤缁橝GV {best_agv.agvId} 澶辫触") + else: + self.logger.warning(f"AGV {best_agv.agvId} 娌℃湁鍙敤鐨勮儗绡撲綅缃�") + available_agvs.remove(best_agv) + else: + self.logger.warning(f"鏃犳硶鑾峰彇AGV {best_agv.agvId} 鐨勭姸鎬佷俊鎭�") + + return assignments + + +class MultiObjectiveAllocation(TaskAllocation): + """澶氱洰鏍囦紭鍖栧垎閰嶇畻娉�""" + + def __init__(self, agv_manager: AGVModelManager, + distance_weight: float = 0.4, + load_weight: float = 0.3, + efficiency_weight: float = 0.3): + """ + 鍒濆鍖栧鐩爣浼樺寲鍒嗛厤绠楁硶 + + Args: + agv_manager: AGV妯″瀷绠$悊鍣� + distance_weight: 璺濈鏉冮噸 + load_weight: 璐熻浇鏉冮噸 + efficiency_weight: 鏁堢巼鏉冮噸 + """ + super().__init__(agv_manager) + self.distance_weight = distance_weight + self.load_weight = load_weight + self.efficiency_weight = efficiency_weight + + def allocate_tasks(self, tasks: List[TaskData]) -> List[TaskAssignment]: + """ + 浣跨敤澶氱洰鏍囦紭鍖栫瓥鐣ュ垎閰嶄换鍔� + + Args: + tasks: 寰呭垎閰嶇殑浠诲姟鍒楄〃 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉鍒楄〃 + """ + if not tasks: + return [] + + # 鑾峰彇鎵�鏈堿GV + all_agvs = self.agv_manager.get_all_agvs() + + if not all_agvs: + self.logger.warning("娌℃湁AGV杩涜浠诲姟鍒嗛厤") + return [] + + # 1. 棣栧厛妫�鏌ヤ换鍔℃槸鍚﹀凡缁忓垎閰嶏紝閬垮厤閲嶅鍒嗛厤 + already_assigned_tasks = set() + for agv in all_agvs: + if agv.backpack: + for backpack_item in agv.backpack: + if backpack_item.taskId: + already_assigned_tasks.add(backpack_item.taskId) + self.logger.info(f"浠诲姟 {backpack_item.taskId} 宸插垎閰嶇粰 AGV {agv.agvId}锛岃烦杩囬噸澶嶅垎閰�") + + # 2. 杩囨护鎺夊凡鍒嗛厤鐨勪换鍔� + unassigned_tasks = [task for task in tasks if task.taskId not in already_assigned_tasks] + + if not unassigned_tasks: + self.logger.info("鎵�鏈変换鍔¢兘宸插垎閰嶏紝鏃犻渶閲嶆柊鍒嗛厤") + return [] + + self.logger.info(f"鎬讳换鍔℃暟: {len(tasks)}, 宸插垎閰�: {len(already_assigned_tasks)}, 寰呭垎閰�: {len(unassigned_tasks)}") + + assignments = [] + path_mapping = self.agv_manager.path_mapping + + # 瀵规瘡涓换鍔�-AGV瀵硅绠楀緱鍒� + task_agv_scores = {} + + for task in unassigned_tasks: + task_start_coord = get_coordinate_from_path_id(task.start, path_mapping) + if not task_start_coord: + continue + + for agv in all_agvs: + if not agv.can_accept_task(task.priority): + continue + + # 璺濈寰楀垎 + distance_score = 0.0 + if agv.coordinates: + distance = calculate_manhattan_distance(agv.coordinates, task_start_coord) + distance_score = 1.0 / (1.0 + distance / 100.0) + + # 璐熻浇寰楀垎 + load_score = 1.0 - agv.get_workload_ratio() + + # 鏁堢巼寰楀垎 + efficiency_score = agv.calculate_efficiency_score(path_mapping) + + # 璁$畻缁煎悎寰楀垎 + total_score = ( + self.distance_weight * distance_score + + self.load_weight * load_score + + self.efficiency_weight * efficiency_score + ) + + task_agv_scores[(task.taskId, agv.agvId)] = total_score + + # 浣跨敤璐績绠楁硶杩涜鍖归厤 + assignments = self._greedy_matching(unassigned_tasks, all_agvs, task_agv_scores) + + return assignments + + def _greedy_matching(self, tasks: List[TaskData], agvs: List[AGVModel], + scores: Dict[Tuple[str, str], float]) -> List[TaskAssignment]: + """ + 浣跨敤璐績绠楁硶杩涜浠诲姟-AGV鍖归厤 + + Args: + tasks: 浠诲姟鍒楄〃 + agvs: AGV鍒楄〃 + scores: 浠诲姟-AGV瀵圭殑寰楀垎 + + Returns: + List[TaskAssignment]: 鍒嗛厤缁撴灉 + """ + assignments = [] + remaining_tasks = [task.taskId for task in tasks] + + # 閲嶅鍒嗛厤鐩村埌娌℃湁浠诲姟鎴栨病鏈夊彲鐢ˋGV + while remaining_tasks: + # 鎵惧埌寰楀垎鏈�楂樼殑浠诲姟-AGV瀵� + best_score = -1 + best_task_id = None + best_agv = None + + for task_id in remaining_tasks: + for agv in agvs: + if agv.is_overloaded(): + continue + + score = scores.get((task_id, agv.agvId), 0.0) + if score > best_score: + best_score = score + best_task_id = task_id + best_agv = agv + + if best_task_id and best_agv: + # 鑾峰彇AGV鐨勫師濮嬬姸鎬佹暟鎹潵鏌ユ壘鍙敤鑳岀瘬浣嶇疆 + agv_status = self.agv_manager.get_agv_status(best_agv.agvId) + + if agv_status: + # 鏌ユ壘鍙敤鐨勮儗绡撲綅缃� + available_lev_id = self.find_available_backpack_slot(agv_status) + + if available_lev_id is not None: + # 鎵惧埌瀵瑰簲鐨勪换鍔″璞� + task = next((t for t in tasks if t.taskId == best_task_id), None) + if task: + # 鍒嗛厤浠诲姟鍒版寚瀹氳儗绡撲綅缃� + success = self.assign_task_with_backpack(best_agv, task, available_lev_id) + if success: + assignments.append(TaskAssignment( + taskId=best_task_id, + agvId=best_agv.agvId, + lev_id=available_lev_id + )) + + remaining_tasks.remove(best_task_id) + self.logger.info(f"澶氱洰鏍囦紭鍖栵細浠诲姟 {best_task_id} 鍒嗛厤缁橝GV {best_agv.agvId}锛岃儗绡撲綅缃�: {available_lev_id}锛屽緱鍒�: {best_score:.3f}") + else: + self.logger.warning(f"浠诲姟 {best_task_id} 鍒嗛厤缁橝GV {best_agv.agvId} 澶辫触") + break + else: + self.logger.error(f"鎵句笉鍒颁换鍔� {best_task_id} 鐨勮缁嗕俊鎭�") + break + else: + self.logger.debug(f"AGV {best_agv.agvId} 娌℃湁鍙敤鐨勮儗绡撲綅缃紝璺宠繃") + break + else: + self.logger.warning(f"鏃犳硶鑾峰彇AGV {best_agv.agvId} 鐨勭姸鎬佷俊鎭�") + break + else: + break + + return assignments + + +class TaskAllocationFactory: + """浠诲姟鍒嗛厤绠楁硶宸ュ巶绫�""" + + @staticmethod + def create_allocator(algorithm_type: str, agv_manager: AGVModelManager) -> TaskAllocation: + """ + 鍒涘缓浠诲姟鍒嗛厤绠楁硶 + + Args: + algorithm_type: 绠楁硶绫诲瀷 + agv_manager: AGV妯″瀷绠$悊鍣� + + Returns: + TaskAllocation: 浠诲姟鍒嗛厤绠楁硶瀵硅薄 + """ + if algorithm_type == "NEAREST_FIRST": + return NearestFirstAllocation(agv_manager) + elif algorithm_type == "LOAD_BALANCED": + return LoadBalancedAllocation(agv_manager) + elif algorithm_type == "PRIORITY_FIRST": + return PriorityFirstAllocation(agv_manager) + elif algorithm_type == "MULTI_OBJECTIVE": + return MultiObjectiveAllocation(agv_manager) + else: + # 榛樿浣跨敤璐熻浇鍧囪 绠楁硶 + return LoadBalancedAllocation(agv_manager) \ No newline at end of file -- Gitblit v1.9.1