package com.algo.service;
|
|
import com.algo.config.EnvDataConfig;
|
import com.algo.model.AGVStatus;
|
import com.algo.model.BackpackData;
|
import com.algo.model.TaskAssignment;
|
import com.algo.model.TaskData;
|
import com.algo.util.JsonUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.util.*;
|
import java.util.stream.Collectors;
|
|
/**
|
* 任务分配服务类
|
*/
|
@Service
|
public class TaskAllocationService {
|
|
@Autowired
|
private EnvDataConfig envDataConfig;
|
|
|
|
|
/**
|
* 执行任务分配
|
* 根据AGV状态和任务列表进行智能分配
|
*
|
* @param agvStatusList AGV状态列表
|
* @param taskList 任务列表
|
* @return 任务分配结果列表
|
*/
|
public List<TaskAssignment> allocateTasks(List<AGVStatus> agvStatusList, List<TaskData> taskList) {
|
System.out.println("开始任务分配,AGV数量: " + agvStatusList.size() + ", 任务数量: " + taskList.size());
|
|
// 输出环境信息
|
System.out.println("环境信息:宽度=" + envDataConfig.getEnvironmentConfig().get("width") +
|
", 高度=" + envDataConfig.getEnvironmentConfig().get("height") +
|
", 工作站数量=" + envDataConfig.getEnvironmentConfig().get("stationCount"));
|
|
List<TaskAssignment> assignments = new ArrayList<>();
|
|
// 1. 过滤可用的AGV
|
List<AGVStatus> availableAgvs = agvStatusList.stream()
|
.filter(agv -> agv.isAvailable() && agv.getAvailableBackpackCount() > 0)
|
.collect(Collectors.toList());
|
|
System.out.println("可用AGV数量: " + availableAgvs.size());
|
|
if (availableAgvs.isEmpty()) {
|
System.out.println("没有可用的AGV,任务分配结束");
|
return assignments;
|
}
|
|
// 2. 过滤需要分配的任务(排除已指定AGV的任务)
|
List<TaskData> unassignedTasks = taskList.stream()
|
.filter(task -> !task.isAssignedToAgv() && task.isTransportTask())
|
.collect(Collectors.toList());
|
|
System.out.println("需要分配的运输任务数量: " + unassignedTasks.size());
|
|
// 3. 按优先级排序任务
|
unassignedTasks.sort((t1, t2) -> Integer.compare(t2.getPriority(), t1.getPriority()));
|
|
// 4. 使用智能分配策略(考虑工作站信息)
|
assignments.addAll(performIntelligentAllocation(availableAgvs, unassignedTasks));
|
|
System.out.println("任务分配完成,生成分配结果数量: " + assignments.size());
|
|
return assignments;
|
}
|
|
/**
|
* 执行智能分配策略
|
* 考虑AGV位置、任务位置、背篓容量、工作站容量等因素
|
*
|
* @param availableAgvs 可用AGV列表
|
* @param tasks 待分配任务列表
|
* @return 分配结果列表
|
*/
|
private List<TaskAssignment> performIntelligentAllocation(List<AGVStatus> availableAgvs, List<TaskData> tasks) {
|
List<TaskAssignment> assignments = new ArrayList<>();
|
|
// 为每个AGV维护一个工作负载记录
|
Map<String, Integer> agvWorkLoad = new HashMap<>();
|
Map<String, Integer> agvUsedBackpacks = new HashMap<>();
|
|
for (AGVStatus agv : availableAgvs) {
|
agvWorkLoad.put(agv.getAgvId(), 0);
|
agvUsedBackpacks.put(agv.getAgvId(), 0);
|
}
|
|
// 按地理位置分组任务(考虑工作站信息)
|
Map<String, List<TaskData>> tasksByStartLocation = groupTasksByStartLocation(tasks);
|
|
// 为每个位置分组分配AGV
|
for (Map.Entry<String, List<TaskData>> entry : tasksByStartLocation.entrySet()) {
|
String startLocation = entry.getKey();
|
List<TaskData> locationTasks = entry.getValue();
|
|
System.out.println("处理起点 " + startLocation + " 的任务,数量: " + locationTasks.size());
|
|
// 检查是否为工作站
|
if (JsonUtils.isStation(startLocation, envDataConfig.getEnvironmentConfig())) {
|
Map<String, Object> stationInfo = JsonUtils.getStationInfo(startLocation, envDataConfig.getEnvironmentConfig());
|
if (stationInfo != null) {
|
Integer capacity = (Integer) stationInfo.get("capacity");
|
System.out.println("工作站 " + startLocation + " 容量: " + capacity);
|
|
// 限制同时分配给该工作站的任务数量(不超过容量)
|
locationTasks = locationTasks.subList(0, Math.min(locationTasks.size(), capacity));
|
}
|
}
|
|
// 找到距离该位置最近的AGV
|
AGVStatus bestAgv = findNearestAvailableAgv(availableAgvs, startLocation, agvUsedBackpacks);
|
|
if (bestAgv != null) {
|
// 为该AGV分配尽可能多的任务(考虑背篓容量)
|
int availableBackpacks = bestAgv.getAvailableBackpackCount() - agvUsedBackpacks.get(bestAgv.getAgvId());
|
int tasksToAssign = Math.min(locationTasks.size(), availableBackpacks);
|
|
for (int i = 0; i < tasksToAssign; i++) {
|
TaskData task = locationTasks.get(i);
|
|
// 查找可用的背篓位置
|
int backpackIndex = findAvailableBackpackIndex(bestAgv, agvUsedBackpacks.get(bestAgv.getAgvId()));
|
|
if (backpackIndex != -1) {
|
// 创建取货任务分配
|
TaskAssignment takeAssignment = new TaskAssignment(
|
task.getTaskId(),
|
bestAgv.getAgvId(),
|
TaskAssignment.generateSegId(task.getTaskId(), bestAgv.getAgvId(), 1),
|
String.valueOf(backpackIndex),
|
TaskAssignment.TYPE_TAKE
|
);
|
assignments.add(takeAssignment);
|
|
// 创建放货任务分配
|
TaskAssignment putAssignment = new TaskAssignment(
|
task.getTaskId(),
|
bestAgv.getAgvId(),
|
TaskAssignment.generateSegId(task.getTaskId(), bestAgv.getAgvId(), 2),
|
String.valueOf(backpackIndex),
|
TaskAssignment.TYPE_PUT
|
);
|
assignments.add(putAssignment);
|
|
// 更新AGV工作负载
|
agvWorkLoad.put(bestAgv.getAgvId(), agvWorkLoad.get(bestAgv.getAgvId()) + 1);
|
agvUsedBackpacks.put(bestAgv.getAgvId(), agvUsedBackpacks.get(bestAgv.getAgvId()) + 1);
|
|
System.out.println("分配任务 " + task.getTaskId() + " 给AGV " + bestAgv.getAgvId() +
|
" 背篓 " + backpackIndex + " (起点: " + startLocation + ")");
|
} else {
|
System.out.println("AGV " + bestAgv.getAgvId() + " 没有可用的背篓位置");
|
break;
|
}
|
}
|
} else {
|
System.out.println("未找到可用的AGV处理起点 " + startLocation + " 的任务");
|
}
|
}
|
|
return assignments;
|
}
|
|
/**
|
* 按起点位置分组任务
|
*
|
* @param tasks 任务列表
|
* @return 按起点分组的任务Map
|
*/
|
private Map<String, List<TaskData>> groupTasksByStartLocation(List<TaskData> tasks) {
|
Map<String, List<TaskData>> grouped = new HashMap<>();
|
|
for (TaskData task : tasks) {
|
String startLocation = task.getStart();
|
grouped.computeIfAbsent(startLocation, k -> new ArrayList<>()).add(task);
|
}
|
|
return grouped;
|
}
|
|
/**
|
* 查找距离指定位置最近的可用AGV
|
*
|
* @param availableAgvs 可用AGV列表
|
* @param targetLocation 目标位置
|
* @param usedBackpacks 已使用背篓数量统计
|
* @return 最近的可用AGV
|
*/
|
private AGVStatus findNearestAvailableAgv(List<AGVStatus> availableAgvs, String targetLocation, Map<String, Integer> usedBackpacks) {
|
AGVStatus bestAgv = null;
|
double minDistance = Double.MAX_VALUE;
|
|
int[] targetCoord = JsonUtils.getCoordinate(targetLocation, envDataConfig.getPathMapping());
|
if (targetCoord == null) {
|
System.out.println("无法获取目标位置 " + targetLocation + " 的坐标");
|
return availableAgvs.isEmpty() ? null : availableAgvs.get(0);
|
}
|
|
for (AGVStatus agv : availableAgvs) {
|
// 检查AGV是否还有可用背篓
|
int availableBackpacks = agv.getAvailableBackpackCount() - usedBackpacks.get(agv.getAgvId());
|
if (availableBackpacks <= 0) {
|
continue;
|
}
|
|
int[] agvCoord = JsonUtils.getCoordinate(agv.getPosition(), envDataConfig.getPathMapping());
|
if (agvCoord != null) {
|
double distance = JsonUtils.calculateManhattanDistance(agvCoord, targetCoord);
|
|
// 考虑AGV的工作负载(距离越近且负载越轻越好)
|
double adjustedDistance = distance + usedBackpacks.get(agv.getAgvId()) * 2.0;
|
|
if (adjustedDistance < minDistance) {
|
minDistance = adjustedDistance;
|
bestAgv = agv;
|
}
|
}
|
}
|
|
return bestAgv;
|
}
|
|
/**
|
* 查找AGV的可用背篓位置
|
*
|
* @param agv AGV状态
|
* @param usedCount 已使用的背篓数量
|
* @return 可用背篓索引,如果没有可用则返回-1
|
*/
|
private int findAvailableBackpackIndex(AGVStatus agv, int usedCount) {
|
if (agv.getBackpack() == null || agv.getBackpack().isEmpty()) {
|
return usedCount == 0 ? 0 : -1;
|
}
|
|
int availableCount = 0;
|
for (BackpackData backpack : agv.getBackpack()) {
|
if (backpack.isAvailable()) {
|
if (availableCount >= usedCount) {
|
return backpack.getIndex();
|
}
|
availableCount++;
|
}
|
}
|
|
return -1;
|
}
|
|
/**
|
* 验证任务分配结果
|
*
|
* @param assignments 分配结果列表
|
* @return 验证是否通过
|
*/
|
public boolean validateAssignments(List<TaskAssignment> assignments) {
|
Map<String, Set<String>> agvBackpackUsage = new HashMap<>();
|
|
for (TaskAssignment assignment : assignments) {
|
if (!assignment.isValid()) {
|
System.out.println("发现无效的任务分配: " + assignment);
|
return false;
|
}
|
|
String agvId = assignment.getAgvId();
|
String backpackId = assignment.getLev();
|
|
agvBackpackUsage.computeIfAbsent(agvId, k -> new HashSet<>()).add(backpackId);
|
}
|
|
// 检查背篓使用冲突
|
for (Map.Entry<String, Set<String>> entry : agvBackpackUsage.entrySet()) {
|
String agvId = entry.getKey();
|
Set<String> usedBackpacks = entry.getValue();
|
|
System.out.println("AGV " + agvId + " 使用背篓: " + usedBackpacks);
|
}
|
|
return true;
|
}
|
}
|