| | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.atomic.AtomicReference; |
| | | |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.zy.asrs.entity.BasDevp; |
| | |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.core.News; |
| | | import com.zy.core.model.StationObjModel; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | |
| | | |
| | | @Component |
| | | public class NavigateUtils { |
| | | private static final long STATION_PATH_RUNTIME_SNAPSHOT_TTL_MS = 2000L; |
| | | |
| | | private static final String CFG_STATION_PATH_LEN_WEIGHT_PERCENT = "stationPathLenWeightPercent"; |
| | | private static final String CFG_STATION_PATH_CONG_WEIGHT_PERCENT = "stationPathCongWeightPercent"; |
| | |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | |
| | | public synchronized List<NavigateNode> calcByStationId(Integer startStationId, Integer endStationId) { |
| | | private final AtomicReference<CachedStationPathRuntimeSnapshot> runtimeSnapshotRef = new AtomicReference<>(); |
| | | |
| | | @Scheduled(fixedDelay = 1500, initialDelay = 3000) |
| | | public void refreshStationPathCaches() { |
| | | try { |
| | | runtimeSnapshotRef.set(new CachedStationPathRuntimeSnapshot(System.currentTimeMillis(), buildStationPathRuntimeSnapshot())); |
| | | } catch (Exception ignore) { |
| | | } |
| | | } |
| | | |
| | | public List<NavigateNode> calcByStationId(Integer startStationId, Integer endStationId) { |
| | | BasStation startStation = basStationService.selectById(startStationId); |
| | | if (startStation == null) { |
| | | throw new CoolException("未找到该 起点 对应的站点数据"); |
| | |
| | | return fitlerList; |
| | | } |
| | | |
| | | public synchronized List<NavigateNode> calcByTrackSiteNo(int lev, Integer startTrackSiteNo, Integer endTrackSiteNo) { |
| | | public List<NavigateNode> calcReachablePathByStationId(Integer startStationId, Integer endStationId) { |
| | | BasStation startStation = basStationService.selectById(startStationId); |
| | | if (startStation == null) { |
| | | throw new CoolException("未找到该 起点 对应的站点数据"); |
| | | } |
| | | Integer lev = startStation.getStationLev(); |
| | | |
| | | NavigateSolution navigateSolution = new NavigateSolution(); |
| | | List<List<NavigateNode>> stationMap = navigateSolution.getStationMap(lev); |
| | | |
| | | NavigateNode startNode = navigateSolution.findStationNavigateNode(stationMap, startStationId); |
| | | if (startNode == null) { |
| | | throw new CoolException("未找到该 起点 对应的节点"); |
| | | } |
| | | |
| | | NavigateNode endNode = navigateSolution.findStationNavigateNode(stationMap, endStationId); |
| | | if (endNode == null) { |
| | | throw new CoolException("未找到该 终点 对应的节点"); |
| | | } |
| | | |
| | | long startTime = System.currentTimeMillis(); |
| | | News.info("[WCS Debug] 站点快速可达路径开始计算,startStationId={},endStationId={}", startStationId, endStationId); |
| | | NavigateNode resNode = navigateSolution.astarSearchJava(stationMap, startNode, endNode); |
| | | if (resNode == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | News.info("[WCS Debug] 站点快速可达路径计算完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | | return buildStationPathFromFatherChain(resNode, stationMap); |
| | | } |
| | | |
| | | public List<NavigateNode> calcByTrackSiteNo(int lev, Integer startTrackSiteNo, Integer endTrackSiteNo) { |
| | | NavigateSolution navigateSolution = new NavigateSolution(); |
| | | List<List<NavigateNode>> rgvTrackMap = navigateSolution.getRgvTrackMap(lev); |
| | | |
| | |
| | | return fitlerList; |
| | | } |
| | | |
| | | public synchronized List<NavigateNode> findLiftStationList(int lev) { |
| | | public List<NavigateNode> findLiftStationList(int lev) { |
| | | NavigateSolution navigateSolution = new NavigateSolution(); |
| | | List<List<NavigateNode>> stationMap = navigateSolution.getStationMap(lev); |
| | | |
| | |
| | | return liftStationList; |
| | | } |
| | | |
| | | public synchronized List<NavigateNode> findStationBestPath(List<List<NavigateNode>> allList) { |
| | | private List<NavigateNode> buildStationPathFromFatherChain(NavigateNode endNode, List<List<NavigateNode>> stationMap) { |
| | | ArrayList<NavigateNode> list = new ArrayList<>(); |
| | | HashSet<NavigateNode> visited = new HashSet<>(); |
| | | int maxSteps = stationMap.size() * stationMap.get(0).size() + 5; |
| | | int steps = 0; |
| | | NavigateNode fatherNode = null; |
| | | NavigateNode currentNode = endNode; |
| | | while (currentNode != null && visited.add(currentNode) && steps++ < maxSteps) { |
| | | currentNode.setIsInflectionPoint(false); |
| | | currentNode.setIsLiftTransferPoint(false); |
| | | |
| | | try { |
| | | JSONObject valueObject = JSON.parseObject(currentNode.getNodeValue()); |
| | | if (valueObject != null) { |
| | | Object isLiftTransfer = valueObject.get("isLiftTransfer"); |
| | | if (isLiftTransfer != null) { |
| | | String isLiftTransferStr = isLiftTransfer.toString(); |
| | | if ("1".equals(isLiftTransferStr) || "true".equalsIgnoreCase(isLiftTransferStr)) { |
| | | currentNode.setIsLiftTransferPoint(true); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | |
| | | HashMap<String, Object> result = searchInflectionPoint(currentNode, fatherNode, currentNode.getFather()); |
| | | if (Boolean.parseBoolean(result.get("result").toString())) { |
| | | currentNode.setIsInflectionPoint(true); |
| | | currentNode.setDirection(result.get("direction").toString()); |
| | | } |
| | | |
| | | list.add(currentNode); |
| | | fatherNode = currentNode; |
| | | currentNode = currentNode.getFather(); |
| | | } |
| | | if (steps >= maxSteps) { |
| | | throw new CoolException("路径回溯超出安全上限,疑似存在父链循环"); |
| | | } |
| | | |
| | | Collections.reverse(list); |
| | | for (NavigateNode navigateNode : list) { |
| | | navigateNode.setFather(null); |
| | | } |
| | | |
| | | HashSet<Integer> stationIdSet = new HashSet<>(); |
| | | List<NavigateNode> filterList = new ArrayList<>(); |
| | | for (NavigateNode navigateNode : list) { |
| | | JSONObject valueObject = JSON.parseObject(navigateNode.getNodeValue()); |
| | | if (valueObject.containsKey("rgvCalcFlag")) { |
| | | continue; |
| | | } |
| | | if (stationIdSet.add(valueObject.getInteger("stationId"))) { |
| | | filterList.add(navigateNode); |
| | | } |
| | | } |
| | | return filterList; |
| | | } |
| | | |
| | | public List<NavigateNode> findStationBestPath(List<List<NavigateNode>> allList) { |
| | | if (allList == null || allList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> statusMap = new HashMap<>(); |
| | | try { |
| | | DeviceConfigService deviceConfigService = SpringUtils.getBean(DeviceConfigService.class); |
| | | if (deviceConfigService != null) { |
| | | List<DeviceConfig> devpList = deviceConfigService.selectList(new EntityWrapper<DeviceConfig>() |
| | | .eq("device_type", String.valueOf(SlaveType.Devp))); |
| | | for (DeviceConfig deviceConfig : devpList) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, deviceConfig.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> m = stationThread.getStatusMap(); |
| | | if (m != null && !m.isEmpty()) { |
| | | statusMap.putAll(m); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | Set<Integer> outStationIdSet = loadAllOutStationIdSet(); |
| | | |
| | | double lenWeightPercent = 50.0; |
| | | double congWeightPercent = 50.0; |
| | | double passOtherOutStationWeightPercent = 100.0; |
| | | boolean forceSkipPassOtherOutStation = false; |
| | | try { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService != null) { |
| | | lenWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_LEN_WEIGHT_PERCENT, lenWeightPercent); |
| | | congWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_CONG_WEIGHT_PERCENT, congWeightPercent); |
| | | passOtherOutStationWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT, passOtherOutStationWeightPercent); |
| | | forceSkipPassOtherOutStation = loadBooleanConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP, false); |
| | | } |
| | | } catch (Exception ignore) {} |
| | | StationPathRuntimeSnapshot runtimeSnapshot = loadStationPathRuntimeSnapshot(); |
| | | Map<Integer, StationProtocol> statusMap = runtimeSnapshot.statusMap; |
| | | Set<Integer> outStationIdSet = runtimeSnapshot.outStationIdSet; |
| | | double lenWeightPercent = runtimeSnapshot.lenWeightPercent; |
| | | double congWeightPercent = runtimeSnapshot.congWeightPercent; |
| | | double passOtherOutStationWeightPercent = runtimeSnapshot.passOtherOutStationWeightPercent; |
| | | boolean forceSkipPassOtherOutStation = runtimeSnapshot.forceSkipPassOtherOutStation; |
| | | |
| | | List<List<NavigateNode>> candidates = new ArrayList<>(); |
| | | List<Integer> lens = new ArrayList<>(); |
| | |
| | | || "on".equalsIgnoreCase(value); |
| | | } |
| | | |
| | | private StationPathRuntimeSnapshot loadStationPathRuntimeSnapshot() { |
| | | long now = System.currentTimeMillis(); |
| | | CachedStationPathRuntimeSnapshot cached = runtimeSnapshotRef.get(); |
| | | if (cached != null && now - cached.loadTimeMs < STATION_PATH_RUNTIME_SNAPSHOT_TTL_MS && cached.snapshot != null) { |
| | | return cached.snapshot; |
| | | } |
| | | StationPathRuntimeSnapshot snapshot = buildStationPathRuntimeSnapshot(); |
| | | runtimeSnapshotRef.set(new CachedStationPathRuntimeSnapshot(now, snapshot)); |
| | | return snapshot; |
| | | } |
| | | |
| | | private StationPathRuntimeSnapshot buildStationPathRuntimeSnapshot() { |
| | | StationPathRuntimeSnapshot snapshot = new StationPathRuntimeSnapshot(); |
| | | snapshot.statusMap = loadAllStationStatusMap(); |
| | | snapshot.outStationIdSet = loadAllOutStationIdSet(); |
| | | |
| | | try { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService != null) { |
| | | snapshot.lenWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_LEN_WEIGHT_PERCENT, snapshot.lenWeightPercent); |
| | | snapshot.congWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_CONG_WEIGHT_PERCENT, snapshot.congWeightPercent); |
| | | snapshot.passOtherOutStationWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT, snapshot.passOtherOutStationWeightPercent); |
| | | snapshot.forceSkipPassOtherOutStation = loadBooleanConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP, false); |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | return snapshot; |
| | | } |
| | | |
| | | private Map<Integer, StationProtocol> loadAllStationStatusMap() { |
| | | Map<Integer, StationProtocol> statusMap = new HashMap<>(); |
| | | try { |
| | | DeviceConfigService deviceConfigService = SpringUtils.getBean(DeviceConfigService.class); |
| | | if (deviceConfigService == null) { |
| | | return statusMap; |
| | | } |
| | | List<DeviceConfig> devpList = deviceConfigService.selectList(new EntityWrapper<DeviceConfig>() |
| | | .eq("device_type", String.valueOf(SlaveType.Devp))); |
| | | for (DeviceConfig deviceConfig : devpList) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, deviceConfig.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> m = stationThread.getStatusMap(); |
| | | if (m != null && !m.isEmpty()) { |
| | | statusMap.putAll(m); |
| | | } |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | return statusMap; |
| | | } |
| | | |
| | | //判断当前节点到下一个节点是否为拐点 |
| | | public HashMap<String,Object> searchInflectionPoint(NavigateNode currentNode, NavigateNode fatherNode, NavigateNode nextNode) { |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | |
| | | |
| | | return direction; |
| | | } |
| | | |
| | | private static class StationPathRuntimeSnapshot { |
| | | private Map<Integer, StationProtocol> statusMap = new HashMap<>(); |
| | | private Set<Integer> outStationIdSet = new HashSet<>(); |
| | | private double lenWeightPercent = 50.0; |
| | | private double congWeightPercent = 50.0; |
| | | private double passOtherOutStationWeightPercent = 100.0; |
| | | private boolean forceSkipPassOtherOutStation = false; |
| | | } |
| | | |
| | | private static class CachedStationPathRuntimeSnapshot { |
| | | private final long loadTimeMs; |
| | | private final StationPathRuntimeSnapshot snapshot; |
| | | |
| | | private CachedStationPathRuntimeSnapshot(long loadTimeMs, StationPathRuntimeSnapshot snapshot) { |
| | | this.loadTimeMs = loadTimeMs; |
| | | this.snapshot = snapshot; |
| | | } |
| | | } |
| | | } |