| | |
| | | import com.core.common.Cools; |
| | | import com.core.common.DateUtils; |
| | | import com.core.common.SpringUtils; |
| | | import com.zy.asrs.domain.vo.StationCycleCapacityVo; |
| | | import com.zy.asrs.domain.vo.StationCycleLoopVo; |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.BasStationOpt; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.asrs.entity.DeviceDataLog; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.asrs.service.BasStationOptService; |
| | | import com.zy.asrs.service.StationCycleCapacityService; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.common.model.NavigateNode; |
| | | import com.zy.common.utils.NavigateUtils; |
| | |
| | | |
| | | import java.text.MessageFormat; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | |
| | | private static final int RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS = 60 * 60 * 24; |
| | | private static final int SHORT_PATH_REPEAT_AVOID_THRESHOLD = 2; |
| | | private static final int SMALL_LOOP_REPEAT_AVOID_THRESHOLD = 2; |
| | | |
| | | private List<StationProtocol> statusList = new ArrayList<>(); |
| | | private DeviceConfig deviceConfig; |
| | |
| | | } |
| | | |
| | | RunBlockRerouteState rerouteState = loadRunBlockRerouteState(taskNo, stationId); |
| | | TaskLoopRerouteState taskLoopRerouteState = loadTaskLoopRerouteState(taskNo); |
| | | LoopIdentity currentLoopIdentity = resolveStationLoopIdentity(stationId); |
| | | rerouteState.setTaskNo(taskNo); |
| | | rerouteState.setBlockStationId(stationId); |
| | | rerouteState.setLastTargetStationId(targetStationId); |
| | | rerouteState.setPlanCount((rerouteState.getPlanCount() == null ? 0 : rerouteState.getPlanCount()) + 1); |
| | | rerouteState.setLastPlanTime(System.currentTimeMillis()); |
| | | taskLoopRerouteState.setTaskNo(taskNo); |
| | | |
| | | List<List<NavigateNode>> candidatePathList = calcCandidatePathNavigateNodes(taskNo, stationId, targetStationId); |
| | | if (candidatePathList.isEmpty()) { |
| | |
| | | |
| | | StationCommand rerouteCommand = selectAvailableRerouteCommand( |
| | | rerouteState, |
| | | taskLoopRerouteState, |
| | | currentLoopIdentity, |
| | | candidatePathList, |
| | | taskNo, |
| | | stationId, |
| | |
| | | rerouteState.resetIssuedRoutes(); |
| | | rerouteCommand = selectAvailableRerouteCommand( |
| | | rerouteState, |
| | | taskLoopRerouteState, |
| | | currentLoopIdentity, |
| | | candidatePathList, |
| | | taskNo, |
| | | stationId, |
| | |
| | | |
| | | if (rerouteCommand != null) { |
| | | saveRunBlockRerouteState(rerouteState); |
| | | touchTaskLoopRerouteState(taskLoopRerouteState, currentLoopIdentity); |
| | | saveTaskLoopRerouteState(taskLoopRerouteState); |
| | | log.info("输送线堵塞重规划选中候选路线,taskNo={}, planCount={}, stationId={}, targetStationId={}, route={}", |
| | | taskNo, rerouteState.getPlanCount(), stationId, targetStationId, JSON.toJSONString(rerouteCommand.getNavigatePath())); |
| | | return rerouteCommand; |
| | |
| | | } |
| | | |
| | | private StationCommand selectAvailableRerouteCommand(RunBlockRerouteState rerouteState, |
| | | TaskLoopRerouteState taskLoopRerouteState, |
| | | LoopIdentity currentLoopIdentity, |
| | | List<List<NavigateNode>> candidatePathList, |
| | | Integer taskNo, |
| | | Integer stationId, |
| | |
| | | } |
| | | |
| | | Set<String> issuedRouteSignatureSet = rerouteState.getIssuedRouteSignatureSet(); |
| | | int currentLoopIssuedCount = resolveCurrentLoopIssuedCount(taskLoopRerouteState, currentLoopIdentity); |
| | | List<RerouteCandidateCommand> candidateCommandList = new ArrayList<>(); |
| | | for (List<NavigateNode> candidatePath : candidatePathList) { |
| | | StationCommand rerouteCommand = buildMoveCommand(taskNo, stationId, targetStationId, palletSize, candidatePath); |
| | |
| | | candidateCommand.setRouteSignature(routeSignature); |
| | | candidateCommand.setPathLength(rerouteCommand.getNavigatePath().size()); |
| | | candidateCommand.setIssuedCount(rerouteState.getRouteIssueCountMap().getOrDefault(routeSignature, 0)); |
| | | candidateCommand.setLoopFingerprint(currentLoopIdentity.getLoopFingerprint()); |
| | | candidateCommand.setLoopIssuedCount(currentLoopIssuedCount); |
| | | candidateCommand.setCurrentLoopHitCount(countCurrentLoopStationHit(rerouteCommand.getNavigatePath(), currentLoopIdentity.getStationIdSet())); |
| | | candidateCommandList.add(candidateCommand); |
| | | } |
| | | if (candidateCommandList.isEmpty()) { |
| | |
| | | } |
| | | |
| | | int shortestPathLength = Integer.MAX_VALUE; |
| | | int shortestPathLoopHitCount = Integer.MAX_VALUE; |
| | | boolean shortestPathOverused = false; |
| | | boolean currentLoopOverused = false; |
| | | boolean hasLongerCandidate = false; |
| | | for (RerouteCandidateCommand candidateCommand : candidateCommandList) { |
| | | if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) { |
| | |
| | | if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) { |
| | | continue; |
| | | } |
| | | if (candidateCommand.getPathLength() == shortestPathLength) { |
| | | shortestPathLoopHitCount = Math.min(shortestPathLoopHitCount, safeInt(candidateCommand.getCurrentLoopHitCount())); |
| | | } |
| | | if (candidateCommand.getPathLength() > shortestPathLength) { |
| | | hasLongerCandidate = true; |
| | | } |
| | |
| | | && candidateCommand.getIssuedCount() >= SHORT_PATH_REPEAT_AVOID_THRESHOLD) { |
| | | shortestPathOverused = true; |
| | | } |
| | | if (!Cools.isEmpty(candidateCommand.getLoopFingerprint()) |
| | | && candidateCommand.getLoopIssuedCount() != null |
| | | && candidateCommand.getLoopIssuedCount() >= SMALL_LOOP_REPEAT_AVOID_THRESHOLD) { |
| | | currentLoopOverused = true; |
| | | } |
| | | } |
| | | if (!shortestPathOverused || !hasLongerCandidate) { |
| | | if (!shortestPathOverused && !currentLoopOverused) { |
| | | return candidateCommandList; |
| | | } |
| | | if (shortestPathLoopHitCount == Integer.MAX_VALUE) { |
| | | shortestPathLoopHitCount = 0; |
| | | } |
| | | |
| | | boolean hasLoopExitCandidate = false; |
| | | for (RerouteCandidateCommand candidateCommand : candidateCommandList) { |
| | | if (candidateCommand == null) { |
| | | continue; |
| | | } |
| | | if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) { |
| | | hasLoopExitCandidate = true; |
| | | break; |
| | | } |
| | | } |
| | | if (!hasLongerCandidate && !hasLoopExitCandidate) { |
| | | return candidateCommandList; |
| | | } |
| | | |
| | | List<RerouteCandidateCommand> reorderedList = new ArrayList<>(); |
| | | if (currentLoopOverused && hasLoopExitCandidate) { |
| | | for (RerouteCandidateCommand candidateCommand : candidateCommandList) { |
| | | if (candidateCommand == null) { |
| | | continue; |
| | | } |
| | | if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) { |
| | | appendCandidateIfAbsent(reorderedList, candidateCommand); |
| | | } |
| | | } |
| | | } |
| | | for (RerouteCandidateCommand candidateCommand : candidateCommandList) { |
| | | if (candidateCommand != null |
| | | && candidateCommand.getPathLength() != null |
| | | && candidateCommand.getPathLength() > shortestPathLength) { |
| | | reorderedList.add(candidateCommand); |
| | | appendCandidateIfAbsent(reorderedList, candidateCommand); |
| | | } |
| | | } |
| | | for (RerouteCandidateCommand candidateCommand : candidateCommandList) { |
| | | if (candidateCommand == null || candidateCommand.getPathLength() == null) { |
| | | continue; |
| | | } |
| | | if (candidateCommand.getPathLength() <= shortestPathLength) { |
| | | reorderedList.add(candidateCommand); |
| | | } |
| | | appendCandidateIfAbsent(reorderedList, candidateCommand); |
| | | } |
| | | return reorderedList; |
| | | } |
| | | |
| | | private void appendCandidateIfAbsent(List<RerouteCandidateCommand> reorderedList, |
| | | RerouteCandidateCommand candidateCommand) { |
| | | if (reorderedList == null || candidateCommand == null) { |
| | | return; |
| | | } |
| | | if (!reorderedList.contains(candidateCommand)) { |
| | | reorderedList.add(candidateCommand); |
| | | } |
| | | } |
| | | |
| | | private RunBlockRerouteState loadRunBlockRerouteState(Integer taskNo, Integer blockStationId) { |
| | |
| | | ); |
| | | } |
| | | |
| | | private TaskLoopRerouteState loadTaskLoopRerouteState(Integer taskNo) { |
| | | if (redisUtil == null || taskNo == null || taskNo <= 0) { |
| | | return new TaskLoopRerouteState(); |
| | | } |
| | | Object stateObj = redisUtil.get(RedisKeyType.STATION_RUN_BLOCK_TASK_LOOP_STATE_.key + taskNo); |
| | | if (stateObj == null) { |
| | | return new TaskLoopRerouteState(); |
| | | } |
| | | try { |
| | | TaskLoopRerouteState state = JSON.parseObject(String.valueOf(stateObj), TaskLoopRerouteState.class); |
| | | return state == null ? new TaskLoopRerouteState() : state.normalize(); |
| | | } catch (Exception ignore) { |
| | | return new TaskLoopRerouteState(); |
| | | } |
| | | } |
| | | |
| | | private void saveTaskLoopRerouteState(TaskLoopRerouteState taskLoopRerouteState) { |
| | | if (redisUtil == null |
| | | || taskLoopRerouteState == null |
| | | || taskLoopRerouteState.getTaskNo() == null |
| | | || taskLoopRerouteState.getTaskNo() <= 0) { |
| | | return; |
| | | } |
| | | taskLoopRerouteState.normalize(); |
| | | redisUtil.set( |
| | | RedisKeyType.STATION_RUN_BLOCK_TASK_LOOP_STATE_.key + taskLoopRerouteState.getTaskNo(), |
| | | JSON.toJSONString(taskLoopRerouteState), |
| | | RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS |
| | | ); |
| | | } |
| | | |
| | | private void touchTaskLoopRerouteState(TaskLoopRerouteState taskLoopRerouteState, |
| | | LoopIdentity currentLoopIdentity) { |
| | | if (taskLoopRerouteState == null || currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) { |
| | | return; |
| | | } |
| | | taskLoopRerouteState.getLoopIssueCountMap().put( |
| | | currentLoopIdentity.getLoopFingerprint(), |
| | | taskLoopRerouteState.getLoopIssueCountMap().getOrDefault(currentLoopIdentity.getLoopFingerprint(), 0) + 1 |
| | | ); |
| | | taskLoopRerouteState.setLastLoopFingerprint(currentLoopIdentity.getLoopFingerprint()); |
| | | taskLoopRerouteState.setLastIssueTime(System.currentTimeMillis()); |
| | | } |
| | | |
| | | private int resolveCurrentLoopIssuedCount(TaskLoopRerouteState taskLoopRerouteState, |
| | | LoopIdentity currentLoopIdentity) { |
| | | if (taskLoopRerouteState == null || currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) { |
| | | return 0; |
| | | } |
| | | return taskLoopRerouteState.getLoopIssueCountMap().getOrDefault(currentLoopIdentity.getLoopFingerprint(), 0); |
| | | } |
| | | |
| | | private int countCurrentLoopStationHit(List<Integer> path, Set<Integer> currentLoopStationIdSet) { |
| | | if (path == null || path.isEmpty() || currentLoopStationIdSet == null || currentLoopStationIdSet.isEmpty()) { |
| | | return 0; |
| | | } |
| | | int hitCount = 0; |
| | | for (Integer stationId : path) { |
| | | if (stationId != null && currentLoopStationIdSet.contains(stationId)) { |
| | | hitCount++; |
| | | } |
| | | } |
| | | return hitCount; |
| | | } |
| | | |
| | | private String buildPathSignature(List<Integer> path) { |
| | | if (path == null || path.isEmpty()) { |
| | | return ""; |
| | |
| | | |
| | | private String buildRunBlockRerouteStateKey(Integer taskNo, Integer blockStationId) { |
| | | return RedisKeyType.STATION_RUN_BLOCK_REROUTE_STATE_.key + taskNo + "_" + blockStationId; |
| | | } |
| | | |
| | | private LoopIdentity resolveStationLoopIdentity(Integer stationId) { |
| | | if (stationId == null || stationId <= 0) { |
| | | return LoopIdentity.empty(); |
| | | } |
| | | try { |
| | | StationCycleCapacityService stationCycleCapacityService = SpringUtils.getBean(StationCycleCapacityService.class); |
| | | if (stationCycleCapacityService == null) { |
| | | return LoopIdentity.empty(); |
| | | } |
| | | StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot(); |
| | | if (capacityVo == null || capacityVo.getLoopList() == null || capacityVo.getLoopList().isEmpty()) { |
| | | return LoopIdentity.empty(); |
| | | } |
| | | for (StationCycleLoopVo loopVo : capacityVo.getLoopList()) { |
| | | List<Integer> loopStationIdList = normalizeLoopStationIdList(loopVo == null ? null : loopVo.getStationIdList()); |
| | | if (loopStationIdList.isEmpty() || !loopStationIdList.contains(stationId)) { |
| | | continue; |
| | | } |
| | | return new LoopIdentity(buildLoopFingerprint(loopStationIdList), new HashSet<>(loopStationIdList)); |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | return LoopIdentity.empty(); |
| | | } |
| | | |
| | | private List<Integer> normalizeLoopStationIdList(List<Integer> stationIdList) { |
| | | if (stationIdList == null || stationIdList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | List<Integer> normalizedList = new ArrayList<>(); |
| | | Set<Integer> seenStationIdSet = new HashSet<>(); |
| | | for (Integer stationId : stationIdList) { |
| | | if (stationId == null || stationId <= 0 || !seenStationIdSet.add(stationId)) { |
| | | continue; |
| | | } |
| | | normalizedList.add(stationId); |
| | | } |
| | | Collections.sort(normalizedList); |
| | | return normalizedList; |
| | | } |
| | | |
| | | private String buildLoopFingerprint(List<Integer> stationIdList) { |
| | | if (stationIdList == null || stationIdList.isEmpty()) { |
| | | return ""; |
| | | } |
| | | StringBuilder builder = new StringBuilder(); |
| | | for (Integer stationId : stationIdList) { |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | if (builder.length() > 0) { |
| | | builder.append("|"); |
| | | } |
| | | builder.append(stationId); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private int safeInt(Integer value) { |
| | | return value == null ? 0 : value; |
| | | } |
| | | |
| | | @Data |
| | |
| | | private String routeSignature; |
| | | private Integer pathLength; |
| | | private Integer issuedCount; |
| | | private String loopFingerprint; |
| | | private Integer loopIssuedCount; |
| | | private Integer currentLoopHitCount; |
| | | } |
| | | |
| | | @Data |
| | | private static class TaskLoopRerouteState { |
| | | private Integer taskNo; |
| | | private String lastLoopFingerprint; |
| | | private Long lastIssueTime; |
| | | private Map<String, Integer> loopIssueCountMap = new HashMap<>(); |
| | | |
| | | private TaskLoopRerouteState normalize() { |
| | | if (loopIssueCountMap == null) { |
| | | loopIssueCountMap = new HashMap<>(); |
| | | } |
| | | return this; |
| | | } |
| | | } |
| | | |
| | | @Data |
| | | private static class LoopIdentity { |
| | | private String loopFingerprint; |
| | | private Set<Integer> stationIdSet = new HashSet<>(); |
| | | |
| | | private LoopIdentity(String loopFingerprint, Set<Integer> stationIdSet) { |
| | | this.loopFingerprint = loopFingerprint; |
| | | this.stationIdSet = stationIdSet == null ? new HashSet<>() : stationIdSet; |
| | | } |
| | | |
| | | private static LoopIdentity empty() { |
| | | return new LoopIdentity("", new HashSet<>()); |
| | | } |
| | | } |
| | | } |