| | |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | import com.zy.asrs.entity.BasDevp; |
| | | import com.zy.asrs.entity.BasStation; |
| | | import com.zy.asrs.service.BasDevpService; |
| | | import com.zy.asrs.service.BasStationService; |
| | | import com.zy.core.News; |
| | | import com.zy.core.model.StationObjModel; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.mapper.EntityWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.core.common.SpringUtils; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.common.model.NavigateNode; |
| | |
| | | @Component |
| | | public class NavigateUtils { |
| | | |
| | | private static final String CFG_STATION_PATH_LEN_WEIGHT_PERCENT = "stationPathLenWeightPercent"; |
| | | private static final String CFG_STATION_PATH_CONG_WEIGHT_PERCENT = "stationPathCongWeightPercent"; |
| | | private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT = "stationPathPassOtherOutStationWeightPercent"; |
| | | private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP = "stationPathPassOtherOutStationForceSkip"; |
| | | |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | | |
| | | public synchronized List<NavigateNode> calcByStationId(Integer startStationId, Integer endStationId) { |
| | | BasStation startStation = basStationService.selectById(startStationId); |
| | | BasStation startStation = basStationService.getById(startStationId); |
| | | if (startStation == null) { |
| | | throw new CoolException("未找到该 起点 对应的站点数据"); |
| | | } |
| | |
| | | List<List<NavigateNode>> stationMap = navigateSolution.getStationMap(lev); |
| | | |
| | | NavigateNode startNode = navigateSolution.findStationNavigateNode(stationMap, startStationId); |
| | | if (startNode == null){ |
| | | if (startNode == null) { |
| | | throw new CoolException("未找到该 起点 对应的节点"); |
| | | } |
| | | |
| | | NavigateNode endNode = navigateSolution.findStationNavigateNode(stationMap, endStationId); |
| | | if (endNode == null){ |
| | | if (endNode == null) { |
| | | throw new CoolException("未找到该 终点 对应的节点"); |
| | | } |
| | | |
| | |
| | | News.info("[WCS Debug] 站点路径开始计算,startStationId={},endStationId={}", startStationId, endStationId); |
| | | List<List<NavigateNode>> allList = navigateSolution.allSimplePaths(stationMap, startNode, endNode, 120, 500, 300); |
| | | if (allList.isEmpty()) { |
| | | throw new CoolException("未找到该路径"); |
| | | // throw new CoolException("未找到该路径"); |
| | | return new ArrayList<>(); |
| | | } |
| | | News.info("[WCS Debug] 站点路径计算完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | |
| | | //去重 |
| | | HashSet<Integer> set = new HashSet<>(); |
| | | List<NavigateNode> fitlerList = new ArrayList<>(); |
| | | for(NavigateNode navigateNode : list){ |
| | | for (NavigateNode navigateNode : list) { |
| | | JSONObject valuObject = JSON.parseObject(navigateNode.getNodeValue()); |
| | | if(set.add(valuObject.getInteger("stationId"))){ |
| | | if (valuObject.containsKey("rgvCalcFlag")) { |
| | | continue; |
| | | } |
| | | if (set.add(valuObject.getInteger("stationId"))) { |
| | | fitlerList.add(navigateNode); |
| | | } |
| | | } |
| | |
| | | for (int i = 0; i < fitlerList.size(); i++) { |
| | | NavigateNode currentNode = fitlerList.get(i); |
| | | 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) {} |
| | | |
| | | NavigateNode nextNode = (i + 1 < fitlerList.size()) ? fitlerList.get(i + 1) : null; |
| | | NavigateNode prevNode = (i - 1 >= 0) ? fitlerList.get(i - 1) : null; |
| | |
| | | News.info("[WCS Debug] RGV路径开始计算,startTrackSiteNo:{},endTrackSiteNo={}", startTrackSiteNo, endTrackSiteNo); |
| | | NavigateNode res_node = navigateSolution.astarSearchJava(rgvTrackMap, startNode, endNode); |
| | | if (res_node == null) { |
| | | throw new CoolException("未找到该路径"); |
| | | // throw new CoolException("未找到该路径"); |
| | | return new ArrayList<>(); |
| | | } |
| | | News.info("[WCS Debug] RGV路径计算完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | |
| | | try { |
| | | DeviceConfigService deviceConfigService = SpringUtils.getBean(DeviceConfigService.class); |
| | | if (deviceConfigService != null) { |
| | | List<DeviceConfig> devpList = deviceConfigService.selectList(new EntityWrapper<DeviceConfig>() |
| | | List<DeviceConfig> devpList = deviceConfigService.list(new QueryWrapper<DeviceConfig>() |
| | | .eq("device_type", String.valueOf(SlaveType.Devp))); |
| | | for (DeviceConfig deviceConfig : devpList) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, deviceConfig.getDeviceNo()); |
| | |
| | | } |
| | | } 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) {} |
| | | |
| | | List<List<NavigateNode>> candidates = new ArrayList<>(); |
| | | List<Integer> lens = new ArrayList<>(); |
| | | List<Integer> tasksList = new ArrayList<>(); |
| | | List<Double> congs = new ArrayList<>(); |
| | | List<Integer> passOtherOutStationCounts = new ArrayList<>(); |
| | | int skippedByPassOtherOutStation = 0; |
| | | |
| | | for (List<NavigateNode> path : allList) { |
| | | if (path == null || path.isEmpty()) { |
| | |
| | | } |
| | | } |
| | | double cong = len <= 0 ? 0.0 : (double) tasks / (double) len; |
| | | int passOtherOutStationCount = countPassOtherOutStations(path, outStationIdSet); |
| | | if (forceSkipPassOtherOutStation && passOtherOutStationCount > 0) { |
| | | skippedByPassOtherOutStation++; |
| | | News.info("[WCS Debug] 站点路径候选已跳过,因经过其他出库站点,startStationId={},endStationId={},passOtherOutStationCount={}", |
| | | extractStationId(path.get(0)), |
| | | extractStationId(path.get(path.size() - 1)), |
| | | passOtherOutStationCount); |
| | | continue; |
| | | } |
| | | candidates.add(path); |
| | | lens.add(len); |
| | | tasksList.add(tasks); |
| | | congs.add(cong); |
| | | passOtherOutStationCounts.add(passOtherOutStationCount); |
| | | } |
| | | |
| | | if (candidates.isEmpty()) { |
| | | if (forceSkipPassOtherOutStation && skippedByPassOtherOutStation > 0) { |
| | | News.info("[WCS Debug] 所有站点路径候选均因经过其他出库站点被强制跳过"); |
| | | return new ArrayList<>(); |
| | | } |
| | | return allList.get(0); |
| | | } |
| | | |
| | |
| | | int maxLen = Integer.MIN_VALUE; |
| | | double minCong = Double.MAX_VALUE; |
| | | double maxCong = -Double.MAX_VALUE; |
| | | int minPassOtherOutStationCount = Integer.MAX_VALUE; |
| | | int maxPassOtherOutStationCount = Integer.MIN_VALUE; |
| | | for (int i = 0; i < candidates.size(); i++) { |
| | | int l = lens.get(i); |
| | | double c = congs.get(i); |
| | | int p = passOtherOutStationCounts.get(i); |
| | | if (l < minLen) minLen = l; |
| | | if (l > maxLen) maxLen = l; |
| | | if (c < minCong) minCong = c; |
| | | if (c > maxCong) maxCong = c; |
| | | if (p < minPassOtherOutStationCount) minPassOtherOutStationCount = p; |
| | | if (p > maxPassOtherOutStationCount) maxPassOtherOutStationCount = p; |
| | | } |
| | | |
| | | //长度权重百分比 |
| | | double lenWeightPercent = 50.0; |
| | | //拥堵权重百分比 |
| | | double congWeightPercent = 50.0; |
| | | try { |
| | | ConfigService configService = SpringUtils.getBean(ConfigService.class); |
| | | if (configService != null) { |
| | | Config cfgLen = configService.selectOne(new EntityWrapper<Config>().eq("code", "stationPathLenWeightPercent")); |
| | | if (cfgLen != null && cfgLen.getValue() != null) { |
| | | String v = cfgLen.getValue().trim(); |
| | | if (v.endsWith("%")) v = v.substring(0, v.length() - 1); |
| | | try { lenWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {} |
| | | } |
| | | Config cfgCong = configService.selectOne(new EntityWrapper<Config>().eq("code", "stationPathCongWeightPercent")); |
| | | if (cfgCong != null && cfgCong.getValue() != null) { |
| | | String v = cfgCong.getValue().trim(); |
| | | if (v.endsWith("%")) v = v.substring(0, v.length() - 1); |
| | | try { congWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | |
| | | double weightSum = lenWeightPercent + congWeightPercent; |
| | | double weightSum = lenWeightPercent + congWeightPercent + passOtherOutStationWeightPercent; |
| | | double lenW = weightSum <= 0 ? 0.5 : lenWeightPercent / weightSum; |
| | | double congW = weightSum <= 0 ? 0.5 : congWeightPercent / weightSum; |
| | | double passOtherOutStationW = weightSum <= 0 ? 0.0 : passOtherOutStationWeightPercent / weightSum; |
| | | |
| | | List<NavigateNode> best = null; |
| | | double bestCost = Double.MAX_VALUE; |
| | | int bestPassOtherOutStationCount = Integer.MAX_VALUE; |
| | | int bestTasks = Integer.MAX_VALUE; |
| | | int bestLen = Integer.MAX_VALUE; |
| | | for (int i = 0; i < candidates.size(); i++) { |
| | | int l = lens.get(i); |
| | | int t = tasksList.get(i); |
| | | double c = congs.get(i); |
| | | int p = passOtherOutStationCounts.get(i); |
| | | //归一化 |
| | | double lenNorm = (maxLen - minLen) <= 0 ? 0.0 : (l - minLen) / (double) (maxLen - minLen); |
| | | double congNorm = (maxCong - minCong) <= 0 ? 0.0 : (c - minCong) / (double) (maxCong - minCong); |
| | | double passOtherOutStationNorm = (maxPassOtherOutStationCount - minPassOtherOutStationCount) <= 0 |
| | | ? 0.0 |
| | | : (p - minPassOtherOutStationCount) / (double) (maxPassOtherOutStationCount - minPassOtherOutStationCount); |
| | | //获取权重 |
| | | double cost = lenNorm * lenW + congNorm * congW; |
| | | double cost = lenNorm * lenW + congNorm * congW + passOtherOutStationNorm * passOtherOutStationW; |
| | | if (cost < bestCost |
| | | || (cost == bestCost && t < bestTasks) |
| | | || (cost == bestCost && t == bestTasks && l < bestLen)) { |
| | | || (cost == bestCost && p < bestPassOtherOutStationCount) |
| | | || (cost == bestCost && p == bestPassOtherOutStationCount && t < bestTasks) |
| | | || (cost == bestCost && p == bestPassOtherOutStationCount && t == bestTasks && l < bestLen)) { |
| | | best = candidates.get(i); |
| | | bestCost = cost; |
| | | bestPassOtherOutStationCount = p; |
| | | bestTasks = t; |
| | | bestLen = l; |
| | | } |
| | |
| | | return best; |
| | | } |
| | | |
| | | private Set<Integer> loadAllOutStationIdSet() { |
| | | Set<Integer> outStationIdSet = new HashSet<>(); |
| | | try { |
| | | BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | if (basDevpService == null) { |
| | | return outStationIdSet; |
| | | } |
| | | List<BasDevp> basDevpList = basDevpService.list(new QueryWrapper<BasDevp>().eq("status", 1)); |
| | | for (BasDevp basDevp : basDevpList) { |
| | | List<StationObjModel> outStationList = basDevp.getOutStationList$(); |
| | | for (StationObjModel stationObjModel : outStationList) { |
| | | if (stationObjModel != null && stationObjModel.getStationId() != null) { |
| | | outStationIdSet.add(stationObjModel.getStationId()); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | return outStationIdSet; |
| | | } |
| | | |
| | | private int countPassOtherOutStations(List<NavigateNode> path, Set<Integer> outStationIdSet) { |
| | | if (path == null || path.size() < 3 || outStationIdSet == null || outStationIdSet.isEmpty()) { |
| | | return 0; |
| | | } |
| | | Integer startStationId = extractStationId(path.get(0)); |
| | | Integer endStationId = extractStationId(path.get(path.size() - 1)); |
| | | Set<Integer> hitStationIdSet = new HashSet<>(); |
| | | for (int i = 1; i < path.size() - 1; i++) { |
| | | Integer stationId = extractStationId(path.get(i)); |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | if (startStationId != null && startStationId.equals(stationId)) { |
| | | continue; |
| | | } |
| | | if (endStationId != null && endStationId.equals(stationId)) { |
| | | continue; |
| | | } |
| | | if (outStationIdSet.contains(stationId)) { |
| | | hitStationIdSet.add(stationId); |
| | | } |
| | | } |
| | | return hitStationIdSet.size(); |
| | | } |
| | | |
| | | private Integer extractStationId(NavigateNode node) { |
| | | if (node == null || node.getNodeValue() == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | JSONObject value = JSON.parseObject(node.getNodeValue()); |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | return value.getInteger("stationId"); |
| | | } catch (Exception ignore) {} |
| | | return null; |
| | | } |
| | | |
| | | private double loadDoubleConfig(ConfigService configService, String code, double defaultValue) { |
| | | if (configService == null || code == null) { |
| | | return defaultValue; |
| | | } |
| | | Config config = configService.getOne(new QueryWrapper<Config>().eq("code", code)); |
| | | if (config == null || config.getValue() == null) { |
| | | return defaultValue; |
| | | } |
| | | String value = config.getValue().trim(); |
| | | if (value.endsWith("%")) { |
| | | value = value.substring(0, value.length() - 1); |
| | | } |
| | | try { |
| | | return Double.parseDouble(value); |
| | | } catch (Exception ignore) {} |
| | | return defaultValue; |
| | | } |
| | | |
| | | private boolean loadBooleanConfig(ConfigService configService, String code, boolean defaultValue) { |
| | | if (configService == null || code == null) { |
| | | return defaultValue; |
| | | } |
| | | Config config = configService.getOne(new QueryWrapper<Config>().eq("code", code)); |
| | | if (config == null || config.getValue() == null) { |
| | | return defaultValue; |
| | | } |
| | | String value = config.getValue().trim(); |
| | | if (value.isEmpty()) { |
| | | return defaultValue; |
| | | } |
| | | return "1".equals(value) |
| | | || "true".equalsIgnoreCase(value) |
| | | || "yes".equalsIgnoreCase(value) |
| | | || "y".equalsIgnoreCase(value) |
| | | || "on".equalsIgnoreCase(value); |
| | | } |
| | | |
| | | //判断当前节点到下一个节点是否为拐点 |
| | | public HashMap<String,Object> searchInflectionPoint(NavigateNode currentNode, NavigateNode fatherNode, NavigateNode nextNode) { |
| | | HashMap<String, Object> map = new HashMap<>(); |