package com.zy.acs.manager.core.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.zy.acs.common.utils.RedisSupport;
|
import com.zy.acs.framework.common.R;
|
import com.zy.acs.manager.manager.entity.*;
|
import com.zy.acs.manager.manager.enums.StatusType;
|
import com.zy.acs.manager.manager.enums.TaskStsType;
|
import com.zy.acs.manager.manager.enums.TaskTypeType;
|
import com.zy.acs.manager.manager.enums.TravelStateType;
|
import com.zy.acs.manager.manager.service.*;
|
import com.zy.acs.manager.system.service.ConfigService;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.PostConstruct;
|
import javax.annotation.PreDestroy;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.concurrent.*;
|
|
/**
|
* Created by vincent on 11/9/2024
|
*/
|
@Slf4j
|
@Service
|
public class PatrolService {
|
|
private static final int SCHEDULE_TIME_INTERVAL = 100;
|
|
public static final Map<String, ScheduledFuture<?>> AGV_PATROL_MAP = new ConcurrentHashMap<>();
|
|
private final RedisSupport redis = RedisSupport.defaultRedisSupport;
|
|
private ScheduledExecutorService scheduler = null;
|
|
@Autowired
|
private AgvService agvService;
|
@Autowired
|
private AgvDetailService agvDetailService;
|
@Autowired
|
private TaskService taskService;
|
@Autowired
|
private MainLockWrapService mainLockWrapService;
|
@Autowired
|
private CodeService codeService;
|
@Autowired
|
private MapService mapService;
|
@Autowired
|
private TravelService travelService;
|
@Autowired
|
private AllocateService allocateService;
|
@Autowired
|
private ConfigService configService;
|
|
private void executePatrolLogic(String agvNo) {
|
this.patrolOfMove(agvNo);
|
}
|
|
private void patrolOfMove(String agvNo) {
|
Long agvId = agvService.getAgvId(agvNo);
|
AgvDetail agvDetail = agvDetailService.selectMajorByAgvId(agvId);
|
if (0 < travelService.count(new LambdaQueryWrapper<Travel>()
|
.eq(Travel::getAgvId, agvId)
|
.eq(Travel::getState, TravelStateType.RUNNING.toString()))) {
|
return;
|
}
|
// if (taskService.count(new LambdaQueryWrapper<Task>()
|
// .eq(Task::getAgvId, agvId)
|
// .in(Task::getTaskSts, TaskStsType.ASSIGN.val(), TaskStsType.PROGRESS.val())
|
// ) > 0) {
|
// return;
|
// }
|
if (!agvService.judgeEnable(agvId)) {
|
return;
|
}
|
Agv agv = agvService.getById(agvId);
|
Code destinationCode = this.getDestinationCode(agv, agvDetail);
|
if (null == destinationCode) {
|
return;
|
}
|
if (mainLockWrapService.buildMinorTask(agv, TaskTypeType.MOVE, destinationCode.getData(), null)) {
|
log.info(agv.getUuid() + "开始走行演示...");
|
}
|
}
|
|
/**
|
* 4个地方调用了buildMinorTask,在什么时候、哪里设置task的lane
|
* (
|
* HandlerController, 手动 (手动是否需要判断lane)
|
* MaintainScheduler, 自动 (一般不需要考虑 lane)
|
* PatrolService, 自动 (需要预处理 lane) ✔
|
* TrafficService, 自动 (寻址时已经处理过 lane) ✔
|
* )
|
* 评估HandlerController没有调用buildMajorTask,手动创建task的可行性
|
* agv地图图标变化
|
*/
|
public Code getDestinationCode(Agv agv, AgvDetail agvDetail) {
|
Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class);
|
|
Code startCode = codeService.getById(agvDetail.getRecentCode());
|
|
List<String> notInCodeList = new ArrayList<>();
|
notInCodeList.add("00000301");
|
notInCodeList.add("00000302");
|
notInCodeList.add("00000303");
|
notInCodeList.add("00000351");
|
notInCodeList.add("00000353");
|
notInCodeList.add("00000401");
|
notInCodeList.add("00000402");
|
|
notInCodeList.add("00000311");
|
notInCodeList.add("00000312");
|
notInCodeList.add("00000313");
|
notInCodeList.add("00000361");
|
notInCodeList.add("00000363");
|
notInCodeList.add("00000411");
|
notInCodeList.add("00000412");
|
|
notInCodeList.add("00000046");
|
notInCodeList.add("00000047");
|
List<Code> list = codeService.list(new LambdaQueryWrapper<Code>().notIn(Code::getData, notInCodeList));
|
|
Collections.shuffle(list);
|
|
for (Code endCode : list) {
|
// valid lane
|
if (!allocateService.validCapacityOfLane(agv, endCode)) {
|
continue;
|
}
|
|
// valid path length
|
List<String> pathList = mapService.validFeasibility(startCode, endCode);
|
if (pathList.size() >= 5) {
|
return endCode;
|
}
|
}
|
|
return list.stream().findFirst().orElse(null);
|
}
|
|
|
// ---------------------------------------------------------------------------
|
|
public boolean isPatrolling(String agvNo) {
|
ScheduledFuture<?> scheduledFuture = AGV_PATROL_MAP.get(agvNo);
|
if (scheduledFuture == null) {
|
return false;
|
}
|
return !scheduledFuture.isCancelled() && !scheduledFuture.isDone();
|
}
|
|
public R startupPatrol(String agvNo) {
|
if (AGV_PATROL_MAP.containsKey(agvNo)) {
|
return R.error("AGV " + agvNo + " 的跑库任务已经在运行中。");
|
}
|
|
Runnable patrolTask = () -> {
|
try {
|
executePatrolLogic(agvNo);
|
} catch (Exception e) {
|
log.error("执行AGV " + agvNo + " 跑库任务时发生异常: " + e.getMessage());
|
e.printStackTrace();
|
}
|
};
|
|
ScheduledFuture<?> scheduledFuture = scheduler.scheduleAtFixedRate(patrolTask, 0, SCHEDULE_TIME_INTERVAL, TimeUnit.MILLISECONDS);
|
|
AGV_PATROL_MAP.put(agvNo, scheduledFuture);
|
log.info("已启动AGV " + agvNo + " 的跑库任务。");
|
return R.ok();
|
}
|
|
|
public R shutdownPatrol(String agvNo) {
|
ScheduledFuture<?> scheduledFuture = AGV_PATROL_MAP.get(agvNo);
|
if (scheduledFuture == null) {
|
return R.error("AGV " + agvNo + " 没有正在运行的跑库任务。");
|
}
|
|
boolean cancelled = scheduledFuture.cancel(true);
|
if (cancelled) {
|
AGV_PATROL_MAP.remove(agvNo);
|
log.info("已停止AGV " + agvNo + " 的跑库任务。");
|
return R.ok("已停止AGV " + agvNo + " 的跑库任务。");
|
} else {
|
log.error("未能成功停止AGV " + agvNo + " 的跑库任务。");
|
return R.error("未能成功停止AGV " + agvNo + " 的跑库任务。");
|
}
|
}
|
|
@PostConstruct
|
public void init() {
|
int count = agvService.count(new LambdaQueryWrapper<Agv>().eq(Agv::getStatus, StatusType.ENABLE.val));
|
if (count > 0) {
|
this.scheduler = Executors.newScheduledThreadPool(count);
|
}
|
}
|
|
@PreDestroy
|
public void destroy() throws InterruptedException {
|
for (Map.Entry<String, ScheduledFuture<?>> entry : AGV_PATROL_MAP.entrySet()) {
|
entry.getValue().cancel(true);
|
}
|
scheduler.shutdown();
|
if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) {
|
scheduler.shutdownNow();
|
}
|
}
|
|
}
|