#
vincentlu
1 天以前 c6d75c0d50859fbc68eaea05d49d8e4738ad525d
zy-acs-manager/src/main/java/com/zy/acs/manager/core/service/GuaranteeRuntimeService.java
@@ -1,32 +1,136 @@
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.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.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;
/**
 * Runtime guarantee orchestration entry.
 */
public interface GuaranteeRuntimeService {
@Slf4j
@Service
public class GuaranteeRuntimeService {
    /**
     * Lead time entrance: make sure enough vehicles can reach the target window.
     */
    void prepare(Guarantee plan, LocalDateTime targetTime);
    private final AgvService agvService;
    private final AgvDetailService agvDetailService;
    private final TaskService taskService;
    private final MainLockWrapService mainLockWrapService;
    /**
     * Lock stage: restrict non-essential dispatching for reserve vehicles.
     */
    void lock(Guarantee plan, LocalDateTime targetTime);
    public GuaranteeRuntimeService(AgvService agvService,
                                   AgvDetailService agvDetailService,
                                   TaskService taskService,
                                   MainLockWrapService mainLockWrapService) {
        this.agvService = agvService;
        this.agvDetailService = agvDetailService;
        this.taskService = taskService;
        this.mainLockWrapService = mainLockWrapService;
    }
    /**
     * Executed repeatedly during the guarantee window to keep SLA.
     */
    void checkWindow(Guarantee plan, LocalDateTime targetTime);
    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() == null ? 50 : plan.getMinSoc();
        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 (!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;
        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());
        }
    }
    /**
     * Called once after the window ends to release state/records.
     */
    void finish(Guarantee plan, LocalDateTime targetTime);
    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;
        }
        long busyCount = taskService.count(new LambdaQueryWrapper<Task>()
                .eq(Task::getAgvId, agv.getId())
                .in(Task::getTaskSts,
                        TaskStsType.WAITING.val(),
                        TaskStsType.ASSIGN.val(),
                        TaskStsType.PROGRESS.val()));
        return busyCount == 0;
    }
    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);
    }
    private static class ChargeCandidate {
        private final Agv agv;
        private final int soc;
        ChargeCandidate(Agv agv, int soc) {
            this.agv = agv;
            this.soc = soc;
        }
        public Agv getAgv() {
            return agv;
        }
        public int getSoc() {
            return soc;
        }
    }
}