package com.zy.acs.manager.core.service.impl;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.zy.acs.common.enums.AgvStatusType;
|
import com.zy.acs.manager.core.service.GuaranteeRuntimeService;
|
import com.zy.acs.manager.core.service.MainLockWrapService;
|
import com.zy.acs.manager.manager.entity.Agv;
|
import com.zy.acs.manager.manager.entity.AgvDetail;
|
import com.zy.acs.manager.manager.entity.Guarantee;
|
import com.zy.acs.manager.manager.entity.Task;
|
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.service.AgvDetailService;
|
import com.zy.acs.manager.manager.service.AgvService;
|
import com.zy.acs.manager.manager.service.TaskService;
|
import lombok.AllArgsConstructor;
|
import lombok.Data;
|
import lombok.extern.slf4j.Slf4j;
|
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;
|
import java.util.Optional;
|
|
@Slf4j
|
@Service
|
public class GuaranteeRuntimeServiceImpl implements GuaranteeRuntimeService {
|
|
private final AgvService agvService;
|
private final AgvDetailService agvDetailService;
|
private final TaskService taskService;
|
private final MainLockWrapService mainLockWrapService;
|
|
public GuaranteeRuntimeServiceImpl(AgvService agvService,
|
AgvDetailService agvDetailService,
|
TaskService taskService,
|
MainLockWrapService mainLockWrapService) {
|
this.agvService = agvService;
|
this.agvDetailService = agvDetailService;
|
this.taskService = taskService;
|
this.mainLockWrapService = mainLockWrapService;
|
}
|
|
@Override
|
public void prepare(Guarantee plan, LocalDateTime targetTime) {
|
CapacitySnapshot snapshot = evaluate(plan);
|
if (snapshot.availableCount >= plan.getRequiredCount()) {
|
log.debug("Guarantee[{}] already has {} available vehicles for {}",
|
plan.getName(), snapshot.availableCount, targetTime);
|
return;
|
}
|
int shortage = plan.getRequiredCount() - snapshot.availableCount;
|
log.info("Guarantee[{}] shortage {} vehicles for {} (minSoc={}%), scheduling charge.",
|
plan.getName(), shortage, targetTime, plan.getMinSoc());
|
dispatchChargeTasks(snapshot, shortage);
|
}
|
|
@Override
|
public void lock(Guarantee plan, LocalDateTime targetTime) {
|
log.info("Guarantee[{}] entering lock stage for {}", plan.getName(), targetTime);
|
// TODO persist lock state / inform dispatcher once integration is ready
|
}
|
|
@Override
|
public void checkWindow(Guarantee plan, LocalDateTime targetTime) {
|
CapacitySnapshot snapshot = evaluate(plan);
|
if (snapshot.availableCount < plan.getRequiredCount()) {
|
int shortage = plan.getRequiredCount() - snapshot.availableCount;
|
log.warn("Guarantee[{}] window shortage {} vehicles for {}. Trigger quick recharge.",
|
plan.getName(), shortage, targetTime);
|
dispatchChargeTasks(snapshot, shortage);
|
}
|
}
|
|
@Override
|
public void finish(Guarantee plan, LocalDateTime targetTime) {
|
log.info("Guarantee[{}] finished window for {}", plan.getName(), targetTime);
|
// TODO release any lock/flag once detail implementation is defined
|
}
|
|
private CapacitySnapshot evaluate(Guarantee plan) {
|
int minSoc = Optional.ofNullable(plan.getMinSoc()).orElse(50);
|
List<Agv> scopedAgvs = findScopedAgvs(plan);
|
int available = 0;
|
List<ChargeCandidate> candidates = new ArrayList<>();
|
for (Agv agv : scopedAgvs) {
|
AgvDetail detail = agvDetailService.selectByAgvId(agv.getId());
|
if (detail == null || detail.getSoc() == null) {
|
continue;
|
}
|
if (detail.getSoc() >= minSoc && isAvailable(agv, detail)) {
|
available++;
|
} else {
|
candidates.add(new ChargeCandidate(agv, detail));
|
}
|
}
|
candidates.sort(Comparator.comparingInt(o -> Optional.ofNullable(o.detail.getSoc()).orElse(0)));
|
return new CapacitySnapshot(available, candidates);
|
}
|
|
private boolean isAvailable(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;
|
}
|
boolean busy = taskService.count(new LambdaQueryWrapper<Task>()
|
.eq(Task::getAgvId, agv.getId())
|
.in(Task::getTaskSts,
|
TaskStsType.WAITING.val(),
|
TaskStsType.ASSIGN.val(),
|
TaskStsType.PROGRESS.val())) > 0;
|
return !busy;
|
}
|
|
private void dispatchChargeTasks(CapacitySnapshot snapshot, int shortage) {
|
if (shortage <= 0) {
|
return;
|
}
|
int scheduled = 0;
|
for (ChargeCandidate candidate : snapshot.candidates) {
|
if (scheduled >= shortage) {
|
break;
|
}
|
log.info("Scheduling AGV [{}] for charging to support guarantee", candidate.agv.getName());
|
mainLockWrapService.buildMinorTask(candidate.agv.getId(), TaskTypeType.TO_CHARGE, null, null);
|
scheduled++;
|
}
|
}
|
|
private List<Agv> findScopedAgvs(Guarantee plan) {
|
LambdaQueryWrapper<Agv> wrapper = new LambdaQueryWrapper<Agv>()
|
.eq(Agv::getStatus, StatusType.ENABLE.val);
|
if ("MODEL".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 CapacitySnapshot {
|
private final int availableCount;
|
private final List<ChargeCandidate> candidates;
|
}
|
|
@Data
|
@AllArgsConstructor
|
private static class ChargeCandidate {
|
private Agv agv;
|
private AgvDetail detail;
|
}
|
}
|