feat(wms): 优化批次分配策略以解决死锁和进程冲突问题
- 新增 checkDeadlock 方法检测任务执行是否会产生死锁 - 改进 generateNonDeadlockBatches 方法,增加对死锁和进程冲突的处理 - 实现增强版 DFS 检测环,并收集环中的节点- 添加进程依赖图构建和冲突进程对查找功能 - 优化批次分组算法,确保所有任务都被合理分配
This commit is contained in:
@@ -5,6 +5,7 @@ import java.util.List;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.klp.domain.vo.BatchGroupVo;
|
import com.klp.domain.vo.BatchGroupVo;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -107,18 +108,24 @@ public class WmsBatchController extends BaseController {
|
|||||||
* 生成不会产生死锁的批次分配方案
|
* 生成不会产生死锁的批次分配方案
|
||||||
* 相同processId的任务会合并到一个批次组中
|
* 相同processId的任务会合并到一个批次组中
|
||||||
* 不同processId的任务会放在不同的批次组中
|
* 不同processId的任务会放在不同的批次组中
|
||||||
|
* 对于存在死锁的情况,会避免将冲突的进程合并到一起
|
||||||
*
|
*
|
||||||
* @param rows 任务执行顺序数组
|
* @param rows 任务执行顺序数组
|
||||||
* @return 批次分配方案
|
* @return 批次分配方案
|
||||||
*/
|
*/
|
||||||
@PostMapping("/generate")
|
@PostMapping("/generate")
|
||||||
public R<List<BatchGroupVo>> generateNonDeadlockBatches(@RequestBody List<List<Map<String, Object>>> rows) {
|
public R<List<BatchGroupVo>> generateNonDeadlockBatches(@RequestBody List<List<Map<String, Object>>> rows) {
|
||||||
// 先获取原始的批次分配方案
|
// 检查是否存在死锁
|
||||||
List<BatchGroupVo> originalBatches = iWmsBatchService.generateNonDeadlockBatches(rows);
|
boolean hasDeadlock = iWmsBatchService.checkDeadlock(rows);
|
||||||
|
|
||||||
|
// 获取批次分配方案
|
||||||
|
List<BatchGroupVo> batches = iWmsBatchService.generateNonDeadlockBatches(rows);
|
||||||
|
|
||||||
|
// 如果不存在死锁,才进行进一步合并
|
||||||
|
if (!hasDeadlock) {
|
||||||
// 使用Java 8 Stream API按processId分组并合并任务
|
// 使用Java 8 Stream API按processId分组并合并任务
|
||||||
Map<String, List<BatchGroupVo>> groupedByProcessId = originalBatches.stream()
|
Map<String, List<BatchGroupVo>> groupedByProcessId = batches.stream()
|
||||||
.collect(java.util.stream.Collectors.groupingBy(BatchGroupVo::getProcessId));
|
.collect(Collectors.groupingBy(BatchGroupVo::getProcessId));
|
||||||
|
|
||||||
// 合并相同processId的批次组
|
// 合并相同processId的批次组
|
||||||
List<BatchGroupVo> mergedBatches = new ArrayList<>();
|
List<BatchGroupVo> mergedBatches = new ArrayList<>();
|
||||||
@@ -133,13 +140,17 @@ public class WmsBatchController extends BaseController {
|
|||||||
// 合并所有taskIds
|
// 合并所有taskIds
|
||||||
List<String> allTaskIds = groups.stream()
|
List<String> allTaskIds = groups.stream()
|
||||||
.flatMap(group -> group.getTaskIds().stream())
|
.flatMap(group -> group.getTaskIds().stream())
|
||||||
.collect(java.util.stream.Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
mergedGroup.setTaskIds(allTaskIds);
|
mergedGroup.setTaskIds(allTaskIds);
|
||||||
|
|
||||||
mergedBatches.add(mergedGroup);
|
mergedBatches.add(mergedGroup);
|
||||||
});
|
});
|
||||||
|
|
||||||
return R.ok(mergedBatches);
|
return R.ok(mergedBatches);
|
||||||
|
} else {
|
||||||
|
// 如果存在死锁,直接返回Service层的结果,不进行额外合并
|
||||||
|
return R.ok(batches);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ public interface IWmsBatchService {
|
|||||||
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测任务执行是否会产生死锁
|
||||||
|
*
|
||||||
|
* @param rows 任务执行顺序数组
|
||||||
|
* @return 是否存在死锁
|
||||||
|
*/
|
||||||
|
boolean checkDeadlock(List<List<Map<String, Object>>> rows);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成不会产生死锁的批次分配方案
|
* 生成不会产生死锁的批次分配方案
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -116,12 +116,37 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
return baseMapper.deleteBatchIds(ids) > 0;
|
return baseMapper.deleteBatchIds(ids) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建任务依赖图
|
||||||
|
*/
|
||||||
|
private Map<String, Set<String>> buildTaskDependencyGraph(List<List<Map<String, Object>>> rows) {
|
||||||
|
Map<String, Set<String>> graph = new HashMap<>();
|
||||||
|
|
||||||
|
// 遍历每一行,记录任务间的依赖关系
|
||||||
|
for (List<Map<String, Object>> row : rows) {
|
||||||
|
// 按sequence排序
|
||||||
|
row.sort(Comparator.comparingInt(task -> Integer.parseInt(task.get("sequence").toString())));
|
||||||
|
|
||||||
|
// 构建依赖关系
|
||||||
|
for (int i = 0; i < row.size() - 1; i++) {
|
||||||
|
String currentTask = row.get(i).get("taskId").toString();
|
||||||
|
String nextTask = row.get(i + 1).get("taskId").toString();
|
||||||
|
|
||||||
|
// 添加依赖:nextTask依赖于currentTask
|
||||||
|
graph.computeIfAbsent(nextTask, k -> new HashSet<>()).add(currentTask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检测任务执行是否会产生死锁
|
* 检测任务执行是否会产生死锁
|
||||||
*
|
*
|
||||||
* @param rows 任务执行顺序数组
|
* @param rows 任务执行顺序数组
|
||||||
* @return 是否存在死锁
|
* @return 是否存在死锁
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean checkDeadlock(List<List<Map<String, Object>>> rows) {
|
public boolean checkDeadlock(List<List<Map<String, Object>>> rows) {
|
||||||
// 保存任务执行顺序数组
|
// 保存任务执行顺序数组
|
||||||
this.rows = rows;
|
this.rows = rows;
|
||||||
@@ -139,15 +164,86 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
|
|
||||||
// 使用DFS检测是否存在环(死锁)
|
// 使用DFS检测是否存在环(死锁)
|
||||||
Map<String, Integer> visited = new HashMap<>();
|
Map<String, Integer> visited = new HashMap<>();
|
||||||
|
Set<String> currentPath = new HashSet<>();
|
||||||
|
List<List<String>> cycles = new ArrayList<>();
|
||||||
|
|
||||||
for (String task : allTasks) {
|
for (String task : allTasks) {
|
||||||
if (visited.getOrDefault(task, 0) == 0) {
|
if (visited.getOrDefault(task, 0) == 0) {
|
||||||
if (hasCycleDFS(task, taskGraph, visited)) {
|
List<String> path = new ArrayList<>();
|
||||||
|
if (detectCycleDFS(task, taskGraph, visited, currentPath, path, cycles)) {
|
||||||
return true; // 存在死锁
|
return true; // 存在死锁
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false; // 不存在死锁
|
// 检查进程冲突
|
||||||
|
Map<String, String> taskToProcess = new HashMap<>();
|
||||||
|
for (List<Map<String, Object>> row : rows) {
|
||||||
|
for (Map<String, Object> task : row) {
|
||||||
|
String taskId = task.get("taskId").toString();
|
||||||
|
String processId = task.get("processId").toString();
|
||||||
|
taskToProcess.put(taskId, processId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Set<String>> processGraph = buildProcessDependencyGraph(rows);
|
||||||
|
Set<Set<String>> conflictProcessPairs = findConflictProcessPairs(processGraph);
|
||||||
|
|
||||||
|
return !conflictProcessPairs.isEmpty(); // 如果存在进程冲突,也视为死锁
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增强版DFS检测环,并收集环中的节点
|
||||||
|
*/
|
||||||
|
private boolean detectCycleDFS(String node, Map<String, Set<String>> graph,
|
||||||
|
Map<String, Integer> visited, Set<String> currentPath,
|
||||||
|
List<String> path, List<List<String>> cycles) {
|
||||||
|
if (node == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记当前节点为访问中
|
||||||
|
visited.put(node, 1);
|
||||||
|
currentPath.add(node);
|
||||||
|
path.add(node);
|
||||||
|
|
||||||
|
// 访问所有邻居
|
||||||
|
if (graph != null && graph.containsKey(node)) {
|
||||||
|
Set<String> neighbors = graph.get(node);
|
||||||
|
if (neighbors != null) {
|
||||||
|
for (String neighbor : neighbors) {
|
||||||
|
if (neighbor == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果邻居在当前路径中,说明存在环
|
||||||
|
if (currentPath.contains(neighbor)) {
|
||||||
|
// 找到环的起始位置
|
||||||
|
int startIdx = path.indexOf(neighbor);
|
||||||
|
if (startIdx >= 0) {
|
||||||
|
List<String> cycle = new ArrayList<>(path.subList(startIdx, path.size()));
|
||||||
|
cycles.add(cycle);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果邻居未被访问,继续DFS
|
||||||
|
if (visited.getOrDefault(neighbor, 0) == 0) {
|
||||||
|
if (detectCycleDFS(neighbor, graph, visited, currentPath, path, cycles)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回溯,移除当前节点
|
||||||
|
currentPath.remove(node);
|
||||||
|
path.remove(path.size() - 1);
|
||||||
|
|
||||||
|
// 标记当前节点为已访问
|
||||||
|
visited.put(node, 2);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,16 +268,24 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
||||||
String taskId = entry.getKey();
|
String taskId = entry.getKey();
|
||||||
Set<String> dependencies = entry.getValue();
|
Set<String> dependencies = entry.getValue();
|
||||||
|
if (dependencies == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String processId = taskToProcess.get(taskId);
|
String processId = taskToProcess.get(taskId);
|
||||||
|
if (processId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (String depTaskId : dependencies) {
|
for (String depTaskId : dependencies) {
|
||||||
String depProcessId = taskToProcess.get(depTaskId);
|
String depProcessId = taskToProcess.get(depTaskId);
|
||||||
|
if (depProcessId == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 如果依赖的是不同进程的任务,则建立进程间依赖
|
// 如果依赖的是不同进程的任务,则建立进程间依赖
|
||||||
if (!processId.equals(depProcessId)) {
|
if (!processId.equals(depProcessId)) {
|
||||||
processGraph.putIfAbsent(processId, new HashSet<>());
|
processGraph.computeIfAbsent(processId, k -> new HashSet<>()).add(depProcessId);
|
||||||
processGraph.get(processId).add(depProcessId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,6 +293,102 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
return processGraph;
|
return processGraph;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查两个进程之间是否存在冲突
|
||||||
|
* 如果进程A的任务必须在进程B的任务之前执行,同时进程B的任务也必须在进程A的任务之前执行,则存在冲突
|
||||||
|
*/
|
||||||
|
private boolean checkProcessConflict(String processA, String processB, Map<String, Set<String>> processGraph) {
|
||||||
|
if (processA == null || processB == null || processGraph == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean aDepB = isProcessDependent(processA, processB, processGraph, new HashSet<>());
|
||||||
|
boolean bDepA = isProcessDependent(processB, processA, processGraph, new HashSet<>());
|
||||||
|
|
||||||
|
return aDepB && bDepA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查进程A是否依赖于进程B
|
||||||
|
*/
|
||||||
|
private boolean isProcessDependent(String processA, String processB,
|
||||||
|
Map<String, Set<String>> processGraph, Set<String> visited) {
|
||||||
|
if (processA == null || processB == null || processGraph == null || visited == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processA.equals(processB)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.contains(processA)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
visited.add(processA);
|
||||||
|
|
||||||
|
if (processGraph.containsKey(processA)) {
|
||||||
|
Set<String> dependencies = processGraph.get(processA);
|
||||||
|
if (dependencies != null) {
|
||||||
|
for (String dep : dependencies) {
|
||||||
|
if (dep != null && (dep.equals(processB) || isProcessDependent(dep, processB, processGraph, visited))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 找出所有冲突的进程对
|
||||||
|
*/
|
||||||
|
private Set<Set<String>> findConflictProcessPairs(Map<String, Set<String>> processGraph) {
|
||||||
|
Set<Set<String>> conflictPairs = new HashSet<>();
|
||||||
|
Set<String> processes = new HashSet<>();
|
||||||
|
|
||||||
|
if (processGraph == null) {
|
||||||
|
return conflictPairs;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集所有进程
|
||||||
|
for (String process : processGraph.keySet()) {
|
||||||
|
if (process != null) {
|
||||||
|
processes.add(process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, Set<String>> entry : processGraph.entrySet()) {
|
||||||
|
Set<String> deps = entry.getValue();
|
||||||
|
if (deps != null) {
|
||||||
|
for (String dep : deps) {
|
||||||
|
if (dep != null) {
|
||||||
|
processes.add(dep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查每对进程是否存在冲突
|
||||||
|
List<String> processList = new ArrayList<>(processes);
|
||||||
|
for (int i = 0; i < processList.size(); i++) {
|
||||||
|
for (int j = i + 1; j < processList.size(); j++) {
|
||||||
|
String processA = processList.get(i);
|
||||||
|
String processB = processList.get(j);
|
||||||
|
|
||||||
|
if (processA != null && processB != null &&
|
||||||
|
checkProcessConflict(processA, processB, processGraph)) {
|
||||||
|
Set<String> pair = new HashSet<>();
|
||||||
|
pair.add(processA);
|
||||||
|
pair.add(processB);
|
||||||
|
conflictPairs.add(pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return conflictPairs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成不会产生死锁的批次分配方案
|
* 生成不会产生死锁的批次分配方案
|
||||||
* 相同processId的任务会合并到一个批次组中
|
* 相同processId的任务会合并到一个批次组中
|
||||||
@@ -210,12 +410,12 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // 构建进程依赖图
|
|
||||||
// Map<String, Set<String>> processGraph = buildProcessDependencyGraph(rows);
|
|
||||||
|
|
||||||
// 构建任务依赖图
|
// 构建任务依赖图
|
||||||
Map<String, Set<String>> taskGraph = buildTaskDependencyGraph(rows);
|
Map<String, Set<String>> taskGraph = buildTaskDependencyGraph(rows);
|
||||||
|
|
||||||
|
// 构建进程依赖图
|
||||||
|
Map<String, Set<String>> processGraph = buildProcessDependencyGraph(rows);
|
||||||
|
|
||||||
// 获取任务与进程的映射关系
|
// 获取任务与进程的映射关系
|
||||||
Map<String, String> taskToProcess = new HashMap<>();
|
Map<String, String> taskToProcess = new HashMap<>();
|
||||||
// 获取任务详细信息
|
// 获取任务详细信息
|
||||||
@@ -231,9 +431,14 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
|
|
||||||
// 检查是否存在死锁
|
// 检查是否存在死锁
|
||||||
boolean hasDeadlock = checkDeadlock(rows);
|
boolean hasDeadlock = checkDeadlock(rows);
|
||||||
if (hasDeadlock) {
|
|
||||||
// 如果存在死锁,使用拓扑排序找出可行的批次分配方案
|
// 找出所有冲突的进程对
|
||||||
List<List<String>> batchGroups = generateBatchGroups(taskGraph, allTasks, taskToProcess);
|
Set<Set<String>> conflictProcessPairs = findConflictProcessPairs(processGraph);
|
||||||
|
|
||||||
|
if (hasDeadlock || !conflictProcessPairs.isEmpty()) {
|
||||||
|
// 如果存在死锁或进程冲突,使用改进的算法找出可行的批次分配方案
|
||||||
|
List<List<String>> batchGroups = generateBatchGroupsWithCycleHandling(
|
||||||
|
taskGraph, allTasks, taskToProcess, conflictProcessPairs);
|
||||||
|
|
||||||
// 将批次转换为BatchGroupVo格式
|
// 将批次转换为BatchGroupVo格式
|
||||||
List<BatchGroupVo> result = new ArrayList<>();
|
List<BatchGroupVo> result = new ArrayList<>();
|
||||||
@@ -264,8 +469,10 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
Map<String, List<String>> processTasks = new HashMap<>();
|
Map<String, List<String>> processTasks = new HashMap<>();
|
||||||
for (String taskId : allTasks) {
|
for (String taskId : allTasks) {
|
||||||
String processId = taskToProcess.get(taskId);
|
String processId = taskToProcess.get(taskId);
|
||||||
|
if (processId != null) {
|
||||||
processTasks.computeIfAbsent(processId, k -> new ArrayList<>()).add(taskId);
|
processTasks.computeIfAbsent(processId, k -> new ArrayList<>()).add(taskId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 创建批次组
|
// 创建批次组
|
||||||
List<BatchGroupVo> result = new ArrayList<>();
|
List<BatchGroupVo> result = new ArrayList<>();
|
||||||
@@ -274,78 +481,28 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
String processId = entry.getKey();
|
String processId = entry.getKey();
|
||||||
List<String> tasks = entry.getValue();
|
List<String> tasks = entry.getValue();
|
||||||
|
|
||||||
|
if (!tasks.isEmpty()) {
|
||||||
BatchGroupVo batchGroup = new BatchGroupVo();
|
BatchGroupVo batchGroup = new BatchGroupVo();
|
||||||
batchGroup.setGroupId("Group-" + groupId++);
|
batchGroup.setGroupId("Group-" + groupId++);
|
||||||
batchGroup.setProcessId(processId);
|
batchGroup.setProcessId(processId);
|
||||||
batchGroup.setTaskIds(tasks);
|
batchGroup.setTaskIds(tasks);
|
||||||
result.add(batchGroup);
|
result.add(batchGroup);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构建任务依赖图
|
* 生成批次分组,处理环结构
|
||||||
*/
|
|
||||||
private Map<String, Set<String>> buildTaskDependencyGraph(List<List<Map<String, Object>>> rows) {
|
|
||||||
Map<String, Set<String>> graph = new HashMap<>();
|
|
||||||
|
|
||||||
// 遍历每一行,记录任务间的依赖关系
|
|
||||||
for (List<Map<String, Object>> row : rows) {
|
|
||||||
// 按sequare排序
|
|
||||||
row.sort(Comparator.comparingInt(task -> Integer.parseInt(task.get("sequence").toString())));
|
|
||||||
|
|
||||||
// 构建依赖关系
|
|
||||||
for (int i = 0; i < row.size() - 1; i++) {
|
|
||||||
String currentTask = row.get(i).get("taskId").toString();
|
|
||||||
String nextTask = row.get(i + 1).get("taskId").toString();
|
|
||||||
|
|
||||||
// 添加依赖:nextTask依赖于currentTask
|
|
||||||
graph.putIfAbsent(nextTask, new HashSet<>());
|
|
||||||
graph.get(nextTask).add(currentTask);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return graph;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DFS检测环
|
|
||||||
*/
|
|
||||||
private boolean hasCycleDFS(String node, Map<String, Set<String>> graph, Map<String, Integer> visited) {
|
|
||||||
// 标记当前节点为访问中
|
|
||||||
visited.put(node, 1);
|
|
||||||
|
|
||||||
// 访问所有邻居
|
|
||||||
if (graph.containsKey(node)) {
|
|
||||||
for (String neighbor : graph.get(node)) {
|
|
||||||
// 如果邻居正在被访问,说明存在环
|
|
||||||
if (visited.getOrDefault(neighbor, 0) == 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果邻居未被访问,继续DFS
|
|
||||||
if (visited.getOrDefault(neighbor, 0) == 0) {
|
|
||||||
if (hasCycleDFS(neighbor, graph, visited)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记当前节点为已访问
|
|
||||||
visited.put(node, 2);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成批次分组
|
|
||||||
* 相同processId的任务会合并到一个列表中
|
* 相同processId的任务会合并到一个列表中
|
||||||
* 不同processId的任务会放在不同的列表中
|
* 不同processId的任务会放在不同的列表中
|
||||||
|
* 处理环结构,确保所有任务都被分配
|
||||||
*/
|
*/
|
||||||
private List<List<String>> generateBatchGroups( Map<String, Set<String>> taskGraph,
|
private List<List<String>> generateBatchGroupsWithCycleHandling(
|
||||||
Set<String> allTasks, Map<String, String> taskToProcess) {
|
Map<String, Set<String>> taskGraph, Set<String> allTasks,
|
||||||
|
Map<String, String> taskToProcess, Set<Set<String>> conflictProcessPairs) {
|
||||||
|
|
||||||
// 计算每个任务的入度
|
// 计算每个任务的入度
|
||||||
Map<String, Integer> inDegree = new HashMap<>();
|
Map<String, Integer> inDegree = new HashMap<>();
|
||||||
for (String task : allTasks) {
|
for (String task : allTasks) {
|
||||||
@@ -355,7 +512,10 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
// 更新入度
|
// 更新入度
|
||||||
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
||||||
String task = entry.getKey();
|
String task = entry.getKey();
|
||||||
inDegree.put(task, entry.getValue().size());
|
Set<String> dependencies = entry.getValue();
|
||||||
|
if (dependencies != null) {
|
||||||
|
inDegree.put(task, dependencies.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建队列,将入度为0的任务加入队列
|
// 创建队列,将入度为0的任务加入队列
|
||||||
@@ -369,6 +529,9 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
// 存储批次分组方案
|
// 存储批次分组方案
|
||||||
List<List<String>> batchGroups = new ArrayList<>();
|
List<List<String>> batchGroups = new ArrayList<>();
|
||||||
|
|
||||||
|
// 记录已经分配到批次的任务
|
||||||
|
Set<String> processedTasks = new HashSet<>();
|
||||||
|
|
||||||
// 记录已经分配到批次的进程ID
|
// 记录已经分配到批次的进程ID
|
||||||
Set<String> processedProcessIds = new HashSet<>();
|
Set<String> processedProcessIds = new HashSet<>();
|
||||||
|
|
||||||
@@ -379,52 +542,67 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
int size = queue.size();
|
int size = queue.size();
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
currentLevelTasks.add(queue.poll());
|
String task = queue.poll();
|
||||||
|
if (task != null) {
|
||||||
|
currentLevelTasks.add(task);
|
||||||
|
processedTasks.add(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 按进程ID分组
|
// 按进程ID分组,但避免将冲突的进程合并
|
||||||
Map<String, List<String>> processBatches = new HashMap<>();
|
Map<String, List<String>> processBatches = new HashMap<>();
|
||||||
for (String task : currentLevelTasks) {
|
for (String task : currentLevelTasks) {
|
||||||
String processId = taskToProcess.get(task);
|
String processId = taskToProcess.get(task);
|
||||||
|
if (processId != null) {
|
||||||
processBatches.computeIfAbsent(processId, k -> new ArrayList<>()).add(task);
|
processBatches.computeIfAbsent(processId, k -> new ArrayList<>()).add(task);
|
||||||
|
}
|
||||||
|
|
||||||
// 更新依赖于当前任务的任务的入度
|
// 更新依赖于当前任务的任务的入度
|
||||||
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
for (Map.Entry<String, Set<String>> entry : taskGraph.entrySet()) {
|
||||||
String dependentTask = entry.getKey();
|
String dependentTask = entry.getKey();
|
||||||
Set<String> dependencies = entry.getValue();
|
Set<String> dependencies = entry.getValue();
|
||||||
|
|
||||||
if (dependencies.contains(task)) {
|
if (dependencies != null && dependencies.contains(task)) {
|
||||||
inDegree.put(dependentTask, inDegree.get(dependentTask) - 1);
|
int newDegree = inDegree.get(dependentTask) - 1;
|
||||||
|
inDegree.put(dependentTask, newDegree);
|
||||||
|
|
||||||
// 如果入度为0,加入队列
|
// 如果入度为0,加入队列
|
||||||
if (inDegree.get(dependentTask) == 0) {
|
if (newDegree == 0) {
|
||||||
queue.add(dependentTask);
|
queue.add(dependentTask);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将每个进程的任务作为一个批次添加
|
// 将每个进程的任务作为一个批次添加,但避免合并冲突的进程
|
||||||
for (Map.Entry<String, List<String>> entry : processBatches.entrySet()) {
|
for (Map.Entry<String, List<String>> entry : processBatches.entrySet()) {
|
||||||
String processId = entry.getKey();
|
String processId = entry.getKey();
|
||||||
List<String> tasks = entry.getValue();
|
List<String> tasks = entry.getValue();
|
||||||
|
|
||||||
if (!tasks.isEmpty()) {
|
if (!tasks.isEmpty()) {
|
||||||
// 检查该进程是否已经有批次
|
|
||||||
boolean merged = false;
|
|
||||||
|
|
||||||
// 如果该进程ID已经处理过,则不能再合并
|
// 如果该进程ID已经处理过,则不能再合并
|
||||||
if (processedProcessIds.contains(processId)) {
|
if (processedProcessIds.contains(processId)) {
|
||||||
batchGroups.add(tasks);
|
batchGroups.add(tasks);
|
||||||
} else {
|
} else {
|
||||||
// 尝试合并到现有批次
|
// 尝试合并到现有批次,但避免合并冲突的进程
|
||||||
|
boolean merged = false;
|
||||||
for (List<String> existingBatch : batchGroups) {
|
for (List<String> existingBatch : batchGroups) {
|
||||||
// 获取批次中第一个任务的进程ID
|
|
||||||
if (!existingBatch.isEmpty()) {
|
if (!existingBatch.isEmpty()) {
|
||||||
String existingProcessId = taskToProcess.get(existingBatch.get(0));
|
String existingProcessId = taskToProcess.get(existingBatch.get(0));
|
||||||
|
|
||||||
// 如果进程ID相同,则合并
|
// 检查是否存在进程冲突
|
||||||
if (processId.equals(existingProcessId)) {
|
boolean hasConflict = false;
|
||||||
|
if (existingProcessId != null && conflictProcessPairs != null) {
|
||||||
|
for (Set<String> conflictPair : conflictProcessPairs) {
|
||||||
|
if (conflictPair.contains(processId) && conflictPair.contains(existingProcessId)) {
|
||||||
|
hasConflict = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果进程ID相同且没有冲突,则合并
|
||||||
|
if (processId.equals(existingProcessId) && !hasConflict) {
|
||||||
existingBatch.addAll(tasks);
|
existingBatch.addAll(tasks);
|
||||||
merged = true;
|
merged = true;
|
||||||
break;
|
break;
|
||||||
@@ -444,9 +622,58 @@ public class WmsBatchServiceImpl implements IWmsBatchService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return batchGroups;
|
// 处理剩余未分配的任务(可能在环中)
|
||||||
|
Set<String> remainingTasks = new HashSet<>(allTasks);
|
||||||
|
remainingTasks.removeAll(processedTasks);
|
||||||
|
|
||||||
|
if (!remainingTasks.isEmpty()) {
|
||||||
|
// 对于环中的任务,我们需要打破环
|
||||||
|
// 策略:将每个未处理的任务单独作为一个批次
|
||||||
|
Map<String, List<String>> remainingProcessBatches = new HashMap<>();
|
||||||
|
|
||||||
|
for (String task : remainingTasks) {
|
||||||
|
String processId = taskToProcess.get(task);
|
||||||
|
if (processId != null) {
|
||||||
|
// 按进程ID分组,但不合并冲突的进程
|
||||||
|
boolean canMerge = true;
|
||||||
|
|
||||||
|
// 检查是否与已处理的进程冲突
|
||||||
|
for (String processedId : processedProcessIds) {
|
||||||
|
boolean hasConflict = false;
|
||||||
|
if (conflictProcessPairs != null) {
|
||||||
|
for (Set<String> conflictPair : conflictProcessPairs) {
|
||||||
|
if (conflictPair.contains(processId) && conflictPair.contains(processedId)) {
|
||||||
|
hasConflict = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hasConflict) {
|
||||||
|
canMerge = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canMerge) {
|
||||||
|
remainingProcessBatches.computeIfAbsent(processId, k -> new ArrayList<>()).add(task);
|
||||||
|
} else {
|
||||||
|
// 如果与已处理的进程冲突,则单独作为一个批次
|
||||||
|
List<String> singleTaskBatch = new ArrayList<>();
|
||||||
|
singleTaskBatch.add(task);
|
||||||
|
batchGroups.add(singleTaskBatch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将剩余的按进程分组的任务添加到批次中
|
||||||
|
for (List<String> tasks : remainingProcessBatches.values()) {
|
||||||
|
if (!tasks.isEmpty()) {
|
||||||
|
batchGroups.add(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return batchGroups;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user