package com.zy.asrs.task;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.core.common.Cools;
|
import com.zy.asrs.entity.BasCrnp;
|
import com.zy.asrs.entity.BasStation;
|
import com.zy.asrs.entity.WrkMast;
|
import com.zy.asrs.service.BasCrnpService;
|
import com.zy.asrs.service.BasStationService;
|
import com.zy.asrs.service.WrkMastService;
|
import com.zy.asrs.utils.Utils;
|
import com.zy.common.entity.FindCrnNoResult;
|
import com.zy.common.service.CommonService;
|
import com.zy.core.News;
|
import com.zy.core.cache.SlaveConnection;
|
import com.zy.core.enums.SlaveType;
|
import com.zy.core.enums.WrkIoType;
|
import com.zy.core.enums.WrkStsType;
|
import com.zy.core.model.StationObjModel;
|
import com.zy.core.model.protocol.StationProtocol;
|
import com.zy.core.move.StationMoveCoordinator;
|
import com.zy.core.move.StationMoveSession;
|
import com.zy.core.thread.StationThread;
|
import com.zy.core.utils.CrnOperateProcessUtils;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.enums.RedisKeyType;
|
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.stereotype.Component;
|
|
import java.util.Comparator;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
|
@Component
|
public class InboundCrnMoveDispatchScheduler {
|
|
private final WrkMastService wrkMastService;
|
private final BasStationService basStationService;
|
private final CommonService commonService;
|
private final BasCrnpService basCrnpService;
|
private final CrnOperateProcessUtils crnOperateProcessUtils;
|
private final StationMoveCoordinator stationMoveCoordinator;
|
private final RedisUtil redisUtil;
|
|
public InboundCrnMoveDispatchScheduler(WrkMastService wrkMastService,
|
BasStationService basStationService,
|
CommonService commonService,
|
BasCrnpService basCrnpService,
|
CrnOperateProcessUtils crnOperateProcessUtils,
|
StationMoveCoordinator stationMoveCoordinator,
|
RedisUtil redisUtil) {
|
this.wrkMastService = wrkMastService;
|
this.basStationService = basStationService;
|
this.commonService = commonService;
|
this.basCrnpService = basCrnpService;
|
this.crnOperateProcessUtils = crnOperateProcessUtils;
|
this.stationMoveCoordinator = stationMoveCoordinator;
|
this.redisUtil = redisUtil;
|
}
|
|
@Scheduled(fixedDelay = 1000L)
|
public void dispatchInboundCrnMove() {
|
List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
|
.eq("io_type", WrkIoType.IN.id)
|
.eq("wrk_sts", WrkStsType.INBOUND_STATION_RUN.sts)
|
.isNotNull("sta_no"));
|
for (WrkMast wrkMast : wrkMasts) {
|
if (wrkMast == null || wrkMast.getWrkNo() == null || wrkMast.getStaNo() == null || Cools.isEmpty(wrkMast.getLocNo())) {
|
continue;
|
}
|
|
BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>()
|
.eq("station_id", wrkMast.getStaNo())
|
.last("limit 1"));
|
if (basStation == null || basStation.getDeviceNo() == null) {
|
continue;
|
}
|
|
StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
|
if (stationThread == null) {
|
continue;
|
}
|
|
Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
|
StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(basStation.getStationId());
|
tryDispatchInboundCrnMove(wrkMast, stationProtocol);
|
}
|
}
|
|
private void tryDispatchInboundCrnMove(WrkMast wrkMast, StationProtocol targetStationProtocol) {
|
if (targetStationProtocol != null
|
&& targetStationProtocol.isLoading()
|
&& targetStationProtocol.getTaskNo() > 0
|
&& targetStationProtocol.isInEnable()) {
|
return;
|
}
|
|
FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(wrkMast.getLocNo());
|
if (findCrnNoResult == null || !Objects.equals(findCrnNoResult.getCrnType(), SlaveType.Crn) || findCrnNoResult.getCrnNo() == null) {
|
return;
|
}
|
Integer crnNo = findCrnNoResult.getCrnNo();
|
|
BasCrnp basCrnp = basCrnpService.getOne(new QueryWrapper<BasCrnp>()
|
.eq("crn_no", crnNo)
|
.last("limit 1"));
|
if (basCrnp == null) {
|
return;
|
}
|
|
String inboundPickupLocNo = resolveInboundPickupLocNo(basCrnp, wrkMast.getStaNo());
|
if (Cools.isEmpty(inboundPickupLocNo)) {
|
return;
|
}
|
|
// 同堆垛机没有需要参与当前调度判断的出库任务时,优先直接到当前入库任务取货位等待。
|
if (!hasBlockingOutboundTask(crnNo)) {
|
boolean dispatched = crnOperateProcessUtils.dispatchCrnMove(crnNo, inboundPickupLocNo);
|
if (dispatched) {
|
News.info("检测到仅有入库任务,已触发堆垛机直接移动到入库任务取货位等待,工作号={},堆垛机号={},取货位={}",
|
wrkMast.getWrkNo(), crnNo, inboundPickupLocNo);
|
}
|
return;
|
}
|
|
StationMoveSession session = stationMoveCoordinator == null ? null : stationMoveCoordinator.loadSession(wrkMast.getWrkNo());
|
if (!isInboundCrnMoveDispatchWindow(wrkMast, session)) {
|
return;
|
}
|
|
boolean dispatched = crnOperateProcessUtils.dispatchCrnMove(crnNo, inboundPickupLocNo);
|
if (dispatched) {
|
News.info("入库任务即将到达取货位,已触发堆垛机预移车,工作号={},堆垛机号={},取货位={}",
|
wrkMast.getWrkNo(), crnNo, inboundPickupLocNo);
|
}
|
}
|
|
private String resolveInboundPickupLocNo(BasCrnp basCrnp, Integer targetStationId) {
|
if (basCrnp == null || targetStationId == null) {
|
return null;
|
}
|
for (StationObjModel stationObjModel : basCrnp.getInStationList$()) {
|
if (stationObjModel == null || !Objects.equals(stationObjModel.getStationId(), targetStationId)) {
|
continue;
|
}
|
if (stationObjModel.getDeviceRow() == null || stationObjModel.getDeviceBay() == null || stationObjModel.getDeviceLev() == null) {
|
continue;
|
}
|
return Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
|
}
|
return null;
|
}
|
|
private boolean hasBlockingOutboundTask(Integer crnNo) {
|
if (crnNo == null) {
|
return false;
|
}
|
List<WrkMast> pendingOutboundTasks = wrkMastService.list(new QueryWrapper<WrkMast>()
|
.eq("crn_no", crnNo)
|
.eq("io_type", WrkIoType.OUT.id)
|
.in("wrk_sts",
|
WrkStsType.NEW_OUTBOUND.sts,
|
WrkStsType.OUTBOUND_RUN.sts,
|
WrkStsType.OUTBOUND_RUN_COMPLETE.sts,
|
WrkStsType.STATION_RUN.sts,
|
WrkStsType.STATION_RUN_COMPLETE.sts,
|
WrkStsType.OUTBOUND_MANUAL.sts)
|
.orderByAsc("wrk_no"));
|
if (pendingOutboundTasks == null || pendingOutboundTasks.isEmpty()) {
|
return false;
|
}
|
|
// 非批次任务或缺少批次序号的任务仍按原逻辑处理,避免放宽到无法确认执行顺序的出库任务。
|
boolean hasNonBatchTask = pendingOutboundTasks.stream().anyMatch(task -> !isBatchTaskWithSeq(task));
|
if (hasNonBatchTask) {
|
return true;
|
}
|
|
String activeBatch = resolveActiveOutboundBatch(pendingOutboundTasks);
|
if (Cools.isEmpty(activeBatch)) {
|
return true;
|
}
|
|
return pendingOutboundTasks.stream()
|
.filter(this::isBatchTaskWithSeq)
|
.filter(task -> isWithinCurrentBatchExecuteWindow(task, pendingOutboundTasks))
|
.filter(this::isCrnMoveBlockingOutboundTask)
|
.anyMatch(task -> Objects.equals(activeBatch, task.getBatch()));
|
}
|
|
private String resolveActiveOutboundBatch(List<WrkMast> pendingOutboundTasks) {
|
if (pendingOutboundTasks == null || pendingOutboundTasks.isEmpty()) {
|
return null;
|
}
|
Set<String> activeBatchSet = pendingOutboundTasks.stream()
|
.filter(this::isBatchTaskWithSeq)
|
.filter(task -> !Objects.equals(task.getWrkSts(), WrkStsType.NEW_OUTBOUND.sts))
|
.map(WrkMast::getBatch)
|
.filter(batch -> !Cools.isEmpty(batch))
|
.collect(Collectors.toSet());
|
if (activeBatchSet.size() != 1) {
|
return null;
|
}
|
return activeBatchSet.iterator().next();
|
}
|
|
private boolean isBatchTaskWithSeq(WrkMast wrkMast) {
|
return wrkMast != null
|
&& Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)
|
&& !Cools.isEmpty(wrkMast.getBatch())
|
&& wrkMast.getBatchSeq() != null;
|
}
|
|
private boolean isCrnMoveBlockingOutboundTask(WrkMast wrkMast) {
|
if (wrkMast == null || wrkMast.getWrkSts() == null) {
|
return false;
|
}
|
return Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_OUTBOUND.sts)
|
|| Objects.equals(wrkMast.getWrkSts(), WrkStsType.OUTBOUND_RUN.sts)
|
|| Objects.equals(wrkMast.getWrkSts(), WrkStsType.OUTBOUND_MANUAL.sts);
|
}
|
|
private boolean isWithinCurrentBatchExecuteWindow(WrkMast wrkMast, List<WrkMast> pendingOutboundTasks) {
|
if (!isBatchTaskWithSeq(wrkMast) || pendingOutboundTasks == null || pendingOutboundTasks.isEmpty()) {
|
return false;
|
}
|
int batchRunningLimit = getSystemConfigInt("crnOutBatchRunningLimit", 5);
|
if (batchRunningLimit <= 0) {
|
return true;
|
}
|
List<WrkMast> sameBatchTasks = pendingOutboundTasks.stream()
|
.filter(this::isBatchTaskWithSeq)
|
.filter(task -> Objects.equals(task.getBatch(), wrkMast.getBatch()))
|
.sorted(Comparator.comparing(WrkMast::getBatchSeq).thenComparing(WrkMast::getWrkNo))
|
.collect(Collectors.toList());
|
if (sameBatchTasks.isEmpty()) {
|
return false;
|
}
|
int windowSize = Math.min(batchRunningLimit, sameBatchTasks.size());
|
for (int i = 0; i < windowSize; i++) {
|
WrkMast current = sameBatchTasks.get(i);
|
if (current != null && Objects.equals(current.getWrkNo(), wrkMast.getWrkNo())) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private int getSystemConfigInt(String code, int defaultValue) {
|
Object systemConfigMapObj = redisUtil == null ? null : redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
|
if (!(systemConfigMapObj instanceof HashMap)) {
|
return defaultValue;
|
}
|
try {
|
HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
|
return Integer.parseInt(systemConfigMap.getOrDefault(code, String.valueOf(defaultValue)));
|
} catch (Exception ignore) {
|
return defaultValue;
|
}
|
}
|
|
private boolean isInboundCrnMoveDispatchWindow(WrkMast wrkMast, StationMoveSession session) {
|
if (wrkMast == null || session == null || !session.isActive() || wrkMast.getStaNo() == null) {
|
return false;
|
}
|
List<Integer> fullPathStationIds = session.getFullPathStationIds();
|
Integer currentStationId = session.getCurrentStationId();
|
if (fullPathStationIds == null || fullPathStationIds.isEmpty() || currentStationId == null) {
|
return false;
|
}
|
int currentIndex = fullPathStationIds.lastIndexOf(currentStationId);
|
if (currentIndex < 0 || currentIndex >= fullPathStationIds.size() - 1) {
|
return false;
|
}
|
int remainingStationCount = fullPathStationIds.size() - currentIndex - 1;
|
if (remainingStationCount != 1) {
|
return false;
|
}
|
Integer nextStationId = fullPathStationIds.get(currentIndex + 1);
|
return Objects.equals(nextStationId, wrkMast.getStaNo());
|
}
|
}
|