package com.zy.acs.manager.core.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.zy.acs.common.enums.AgvStatusType;
|
import com.zy.acs.manager.manager.entity.*;
|
import com.zy.acs.manager.manager.enums.*;
|
import com.zy.acs.manager.manager.service.AgvDetailService;
|
import com.zy.acs.manager.manager.service.AgvService;
|
import com.zy.acs.manager.manager.service.SegmentService;
|
import com.zy.acs.manager.manager.service.TaskService;
|
import lombok.Data;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Comparator;
|
import java.util.List;
|
import java.util.Objects;
|
|
@Slf4j
|
@Service
|
public class GuaranteeRuntimeService {
|
|
@Autowired
|
private SegmentService segmentService;
|
|
private final AgvService agvService;
|
private final AgvDetailService agvDetailService;
|
private final TaskService taskService;
|
private final MainLockWrapService mainLockWrapService;
|
|
public GuaranteeRuntimeService(AgvService agvService,
|
AgvDetailService agvDetailService,
|
TaskService taskService,
|
MainLockWrapService mainLockWrapService) {
|
this.agvService = agvService;
|
this.agvDetailService = agvDetailService;
|
this.taskService = taskService;
|
this.mainLockWrapService = mainLockWrapService;
|
}
|
|
public void prepare(Guarantee plan, LocalDateTime targetTime) {
|
int requiredCount = plan.getRequiredCount() == null ? 0 : plan.getRequiredCount();
|
if (requiredCount <= 0) {
|
log.warn("Guarantee[{}] requiredCount is not configured, skip", plan.getName());
|
return;
|
}
|
int minSoc = plan.getMinSoc();
|
List<Agv> scopedAgvList = findScopedAgvList(plan);
|
int available = 0;
|
List<ChargeCandidate> candidates = new ArrayList<>();
|
for (Agv agv : scopedAgvList) {
|
AgvDetail detail = agvDetailService.selectMajorByAgvId(agv.getId());
|
if (null == detail || null == detail.getSoc() || null == detail.getAgvStatus()) {
|
continue;
|
}
|
if (detail.getAgvStatus().equals(AgvStatusType.CHARGE)) {
|
continue;
|
}
|
if (!isIdle(agv, detail)) {
|
continue;
|
}
|
int soc = detail.getSoc();
|
if (soc >= minSoc) {
|
available++;
|
} else {
|
candidates.add(new ChargeCandidate(agv, soc));
|
}
|
}
|
if (available >= requiredCount) {
|
// log.debug("Guarantee[{}] already has {} vehicles >= {}% SOC for {}", plan.getName(), available, minSoc, targetTime);
|
return;
|
}
|
int shortage = requiredCount - available; // 还差多少辆
|
candidates.sort(Comparator.comparingInt(ChargeCandidate::getSoc));
|
int scheduled = 0; // 已经安排充电的AGV数量
|
for (ChargeCandidate candidate : candidates) {
|
if (scheduled >= shortage) {
|
break;
|
}
|
log.info("Guarantee[{}] schedule AGV {} charging (soc={}%) for target {}",
|
plan.getName(), candidate.getAgv().getName(), candidate.getSoc(), targetTime);
|
mainLockWrapService.buildMinorTask(candidate.getAgv().getId(), TaskTypeType.TO_CHARGE, null, null);
|
scheduled++;
|
}
|
if (scheduled < shortage) {
|
log.warn("Guarantee[{}] still short of {} vehicles for {} (only {} idle low-soc AGVs)",
|
plan.getName(), shortage - scheduled, targetTime, candidates.size());
|
}
|
}
|
|
private boolean isIdle(Agv agv, AgvDetail detail) {
|
if (!Objects.equals(agv.getStatus(), StatusType.ENABLE.val)) {
|
return false;
|
}
|
if (detail.getAgvStatus() != null && detail.getAgvStatus().equals(AgvStatusType.CHARGE)) {
|
return false;
|
}
|
|
if (0 < taskService.count(new LambdaQueryWrapper<Task>()
|
.eq(Task::getAgvId, agv.getId())
|
// .and(i -> {
|
// i.eq(Task::getTaskSts, TaskStsType.WAITING.val())
|
// .or().eq(Task::getTaskSts, TaskStsType.ASSIGN.val())
|
// .or().eq(Task::getTaskSts, TaskStsType.PROGRESS.val());
|
// })
|
.in(Task::getTaskSts,
|
TaskStsType.WAITING.val(),
|
TaskStsType.ASSIGN.val(),
|
TaskStsType.PROGRESS.val())
|
)) {
|
return false;
|
}
|
if (0 < segmentService.count(new LambdaQueryWrapper<Segment>()
|
.eq(Segment::getAgvId, agv.getId())
|
.and( i -> {
|
// i.eq(Segment::getState, SegmentStateType.WAITING.toString()).or()
|
i.eq(Segment::getState, SegmentStateType.RUNNING.toString());
|
})
|
)) {
|
return false;
|
}
|
return true;
|
}
|
|
private List<Agv> findScopedAgvList(Guarantee plan) {
|
LambdaQueryWrapper<Agv> wrapper = new LambdaQueryWrapper<Agv>()
|
.eq(Agv::getStatus, StatusType.ENABLE.val);
|
if (GuaranteeScopeType.MODEL.toString().equalsIgnoreCase(plan.getScopeType()) && plan.getScopeValue() != null) {
|
try {
|
wrapper.eq(Agv::getAgvModel, Long.valueOf(plan.getScopeValue()));
|
} catch (NumberFormatException ignore) {
|
log.warn("Guarantee[{}] invalid scopeValue {}", plan.getName(), plan.getScopeValue());
|
}
|
}
|
return agvService.list(wrapper);
|
}
|
|
@Data
|
private static class ChargeCandidate {
|
private final Agv agv;
|
private final int soc;
|
|
ChargeCandidate(Agv agv, int soc) {
|
this.agv = agv;
|
this.soc = soc;
|
}
|
|
}
|
}
|