diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/plan/TripPlan.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/plan/TripPlan.java index 927a384b8..33baccb6e 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/plan/TripPlan.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/plan/TripPlan.java @@ -1,6 +1,7 @@ package club.joylink.rtss.simulation.cbtc.data.plan; import club.joylink.rtss.simulation.cbtc.data.map.Section; +import club.joylink.rtss.simulation.cbtc.data.map.Stand; import club.joylink.rtss.simulation.cbtc.data.map.Station; import lombok.Builder; import lombok.Getter; @@ -356,4 +357,37 @@ public class TripPlan { } return null; } + + public boolean isPassingStand(Stand stand) { + boolean passing = false; + for (StationPlan stationPlan : this.getPlanList()) { + if (Objects.equals(stationPlan.getSection(), stand.getSection())) { + passing = true; + break; + } + } + return passing; + } + + public int getPlanIndex(Section section) { + int index = -1; + for (int i = 0; i < this.planList.size(); i++) { + if (Objects.equals(this.planList.get(i).getSection(), section)) { + index = i; + break; + } + } + return index; + } + + public int getPlanIndex(String sectionCode) { + int index = -1; + for (int i = 0; i < this.planList.size(); i++) { + if (Objects.equals(this.planList.get(i).getSection().getCode(), sectionCode)) { + index = i; + break; + } + } + return index; + } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/PassengerFlowSimulateService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/PassengerFlowSimulateService.java index c34633a1a..3ff2e349f 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/PassengerFlowSimulateService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/PassengerFlowSimulateService.java @@ -166,7 +166,9 @@ public class PassengerFlowSimulateService { private void sendTrainPassengerFlowData(Simulation simulation, Map trainPassengerFlowMap) { List> trainPFNumList = new ArrayList<>(); - for (TrainPassengerFlow flow : trainPassengerFlowMap.values()) { + List superviseTrainList = simulation.getRepository().getSuperviseTrainList(); + for (TrainInfo trainInfo : superviseTrainList) { + TrainPassengerFlow flow = trainPassengerFlowMap.get(trainInfo.getGroupNumber()); Map trainPfNum = new HashMap<>(); trainPfNum.put("code", flow.getTrain().getGroupNumber()); trainPfNum.put("num", flow.getPassengerQuantity()); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/Config.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/Config.java index 6e3d8d596..a3a54aa6b 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/Config.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/Config.java @@ -7,6 +7,8 @@ public final class Config { public static final int STRATEGY_CAL_TIME = 30; /** 站台大客流判断基准值 */ public static final int STAND_LPF_TRIGGER = 800; + /** 站台可跳停的最大客流数 */ + public static final int STAND_JUMP_MAX = 50; /** 站台最大停站时间:单位 - 秒 */ public static final int STAND_MAX_STOP_TIME = 90; /** 列车最大容量 */ diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyCalculateAndRecommendService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyCalculateAndRecommendService.java index 042b1c422..9833fade5 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyCalculateAndRecommendService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyCalculateAndRecommendService.java @@ -44,6 +44,7 @@ public class StrategyCalculateAndRecommendService implements ApplicationContextA } for (Strategy strategy : list) { strategyService.calculate(data.clone(), strategy); + strategy.buildDescription(); strategy.handleTarget(); log.info(String.format("策略[%s]计算指标为:%s", strategy.getDescription(), strategy.targetDebugStr())); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/JumpStrategy.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/JumpStrategy.java index 4e424b8ad..6a52354fb 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/JumpStrategy.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/JumpStrategy.java @@ -1,6 +1,7 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; import club.joylink.rtss.simulation.cbtc.data.map.Stand; +import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; @@ -11,6 +12,11 @@ public class JumpStrategy extends Strategy { */ @JsonIgnore Stand stand; + /** + * 是否已经计算过跳停 + */ + @JsonIgnore + private boolean jumped; private String groupNumber; /** 服务号 */ String serviceNumber; @@ -28,7 +34,16 @@ public class JumpStrategy extends Strategy { @Override public String buildDescription() { - this.description = String.format("站台%s设置跳停", stand.debugStr()); + this.description = String.format("为列车[%s-%s|%s],设置站台%s跳停", + groupNumber, serviceNumber, tripNumber, + stand.debugStr()); return this.description; } + + public void jumpedByTrain(String groupNumber, TripPlan tripPlan) { + this.jumped = true; + this.groupNumber = groupNumber; + this.serviceNumber = tripPlan.getServiceNumber(); + this.tripNumber = tripPlan.getTripNumber(); + } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/ParkTimeStrategy.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/ParkTimeStrategy.java index e363e854a..08d1950f0 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/ParkTimeStrategy.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/ParkTimeStrategy.java @@ -18,7 +18,6 @@ public class ParkTimeStrategy extends Strategy { super(Type.PARK_TIME); this.stand = stand; this.time = parkTime; - this.buildDescription(); } public String getStandCode() { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StrategyCalculateData.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StrategyCalculateData.java index 330cc0fdf..78a13549f 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StrategyCalculateData.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StrategyCalculateData.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; +import club.joylink.rtss.simulation.cbtc.data.map.Section; import club.joylink.rtss.simulation.cbtc.data.map.Stand; import club.joylink.rtss.simulation.cbtc.data.plan.RealRun; import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; @@ -30,6 +31,11 @@ public class StrategyCalculateData { Map standPassengerMap = new ConcurrentHashMap<>(); + /** + * 大客流站台 + */ + List lpfStandList = new ArrayList<>(); + List leftStandList; List rightStandList; @@ -59,6 +65,9 @@ public class StrategyCalculateData { for (StandPassengerFlow standPassengerFlow : allStandPassengerFlow) { StandPassenger standPassenger = new StandPassenger(standPassengerFlow); this.standPassengerMap.put(standPassenger.getStand().getCode(), standPassenger); + if (standPassenger.isLpf()) { + this.lpfStandList.add(standPassenger); + } } List leftList = new ArrayList<>(); List rightList = new ArrayList<>(); @@ -104,6 +113,9 @@ public class StrategyCalculateData { standPassengerMap.put(code, standPassenger.clone()); }); obj.standPassengerMap = standPassengerMap; + for (StandPassenger standPassenger : this.lpfStandList) { + obj.lpfStandList.add(standPassenger.clone()); + } obj.leftStandList = this.leftStandList; obj.rightStandList = this.rightStandList; List tpList = new ArrayList<>(); @@ -148,7 +160,11 @@ public class StrategyCalculateData { return null; } - public RealRun queryPreviousRealRunData(String groupNumber) { + public List queryRealRuns(String groupNumber) { + return this.runMap.get(groupNumber); + } + + public RealRun queryLastRealRunData(String groupNumber) { List realRuns = this.runMap.get(groupNumber); if (!CollectionUtils.isEmpty(realRuns)) { realRuns.sort(Comparator.comparing(RealRun::getTime)); @@ -180,4 +196,13 @@ public class StrategyCalculateData { public StandPassenger getStandPassengerByStand(Stand stand) { return this.standPassengerMap.get(stand.getCode()); } + + public boolean isLpfStand(Section section) { + for (StandPassenger standPassenger : this.lpfStandList) { + if (Objects.equals(standPassenger.getStand().getSection(), section)) { + return true; + } + } + return false; + } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/JumpStrategyServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/JumpStrategyServiceImpl.java index 291a86f79..99584ccc6 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/JumpStrategyServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/JumpStrategyServiceImpl.java @@ -5,6 +5,7 @@ import club.joylink.rtss.simulation.cbtc.data.map.Stand; import club.joylink.rtss.simulation.cbtc.data.plan.RealRun; import club.joylink.rtss.simulation.cbtc.data.plan.StationPlan; import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; +import club.joylink.rtss.simulation.cbtc.passenger.strategy.Config; import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.JumpStrategy; import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StandPassenger; import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StrategyCalculateData; @@ -13,8 +14,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; -import java.time.LocalTime; +import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.Objects; @@ -49,20 +51,217 @@ public class JumpStrategyServiceImpl implements StrategyService { log.debug(String.format("第一个站,不生成跳停策略")); return null; } - List jumpableStandList = new ArrayList<>(); + List jumpableStandList; if (right) { jumpableStandList = rightStandList.subList(0, index); } else { jumpableStandList = leftStandList.subList(0, index); } List trainPassengerList = data.getTrainPassengerList(); - + for (int i = jumpableStandList.size()-1; i > 0; i--) { // 起始站不跳停 + Stand s = jumpableStandList.get(i); + // 该站台人数是否支持跳停 + StandPassenger sp = data.getStandPassengerByStand(s); + if (sp.getWait() >= Config.STAND_JUMP_MAX) { + log.debug(String.format("站台[%s]客流数[%s]>=站台跳停最大客流量[%s],不能跳停", + s.debugStr(), sp.getWait(), Config.STAND_JUMP_MAX)); + continue; + } + // 该站是否被前列车跳过 +// boolean jump = this.isJustJumped(data, s); + list.add(new JumpStrategy(s)); + } log.debug(String.format("生成跳停策略[%s]个", list.size())); return list; } + private boolean isJustJumped(StrategyCalculateData data, Stand stand) { + List trainPassengerList = data.getTrainPassengerList(); + List list = new ArrayList<>(); + for (TrainPassenger trainPassenger : trainPassengerList) { + String groupNumber = trainPassenger.getGroupNumber(); + TripPlan tripPlan = data.queryTripPlan(trainPassenger.getServiceNumber(), trainPassenger.getTripNumber()); + if (tripPlan == null) { + log.warn(String.format("列车[%s-%s|%s]没有找到车次计划", + groupNumber, trainPassenger.getServiceNumber(), trainPassenger.getTripNumber())); + continue; + } + int index = tripPlan.getPlanIndex(stand.getSection()); + if (index < 0) { // 没有对应站台计划,无需判断跳停 + continue; + } + List realRunList = data.queryRealRuns(groupNumber); + if (!CollectionUtils.isEmpty(realRunList)) { + realRunList.sort(Comparator.comparing(RealRun::getTime)); + RealRun last = realRunList.get(realRunList.size() - 1); + int lastRunIndex = tripPlan.getPlanIndex(last.getSectionCode()); + if (lastRunIndex < index) { // 列车实际运行未过站台 + continue; + } + RealRun jumpRun = this.isTrainJumpStand(trainPassenger, realRunList, stand); + if (jumpRun != null) { + list.add(jumpRun); + } + } + } + return false; + } + + private RealRun isTrainJumpStand(TrainPassenger trainPassenger, List realRunList, Stand stand) { + RealRun jumpRun = null; + boolean jump = true; + for (int i = realRunList.size() - 1; i >= 0; i--) { + RealRun realRun = realRunList.get(i); + if (!Objects.equals(realRun.getServiceNumber(), trainPassenger.getServiceNumber()) || + !Objects.equals(realRun.getTripNumber(), trainPassenger.getTripNumber())) { + break; + } + if (Objects.equals(realRun.getSectionCode(), stand.getSection().getCode())) { + if (!realRun.isArrive() && i > 0) { + RealRun pre = realRunList.get(i - 1); + if (Objects.equals(pre.getServiceNumber(), trainPassenger.getServiceNumber()) && + Objects.equals(pre.getTripNumber(), trainPassenger.getTripNumber()) && + Objects.equals(pre.getSectionCode(), stand.getSection().getCode())) { + jump = false; + } + } + if (jump) { + jumpRun = realRun; + } + break; + } + } + return jumpRun; + } + @Override public void calculate(StrategyCalculateData data, JumpStrategy strategy) { + List trainPassengerList = data.getTrainPassengerList(); + LocalDateTime systemTime = data.getSystemTime(); + LocalDateTime startTime = systemTime; + LocalDateTime endTime = systemTime.plusMinutes(Config.STRATEGY_CAL_TIME); + boolean jump = false; + int i = 0; + while (startTime.isBefore(endTime) && i < 1000) { + ++i; + LocalDateTime nextTime = null; + for (TrainPassenger trainPassenger : trainPassengerList) { + String groupNumber = trainPassenger.getGroupNumber(); + List realRunList = data.queryRealRuns(groupNumber); + if (CollectionUtils.isEmpty(realRunList)) { + TripPlan tripPlan = data.queryTripPlan(trainPassenger.getServiceNumber(), trainPassenger.getTripNumber()); + if (tripPlan == null) { + log.warn(String.format("列车[%s-%s|%s]没有找到车次计划", + groupNumber, trainPassenger.getServiceNumber(), trainPassenger.getTripNumber())); + continue; + } + if (tripPlan.isBackup()) { + continue; + } + List planList = tripPlan.getPlanList(); + for (StationPlan stationPlan : planList) { + if (stationPlan.getArriveTime().compareTo(systemTime.toLocalTime()) >= 0) { + this.handleTrainRun(data, strategy, trainPassenger, tripPlan, stationPlan); + break; + } + } + realRunList = data.queryRealRuns(groupNumber); + if (CollectionUtils.isEmpty(realRunList)) { + log.error(String.format("列车[%s-%s|%s],车次[%s],时间[%s]", + groupNumber, trainPassenger.getServiceNumber(), trainPassenger.getTripNumber(), + tripPlan.debugStr(), systemTime.toString())); + } + } else { + RealRun lastRun = realRunList.get(realRunList.size() - 1); + TripPlan tripPlan = data.queryTripPlan(lastRun.getServiceNumber(), lastRun.getTripNumber()); + StationPlan stationPlan = tripPlan.queryStationPlanByStationCode(lastRun.getStationCode()); + BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(stationPlan); + if (lastRun.isArrive()) { + this.handleTrainRun(data, strategy, trainPassenger, tripPlan, stationPlan); + } else { + if (tripPlan.isLastPlan(stationPlan)) { + TripPlan nextTripPlan = data.queryNextTripPlan(tripPlan); + if (nextTripPlan != null) { + StationPlan nextStationPlan = nextTripPlan.getFirstStationPlan(); + this.handleTrainRun(data, strategy, trainPassenger, nextTripPlan, nextStationPlan); + } + } else { + StationPlan nextStationPlan = tripPlan.queryNextStationPlan(stationPlan.getStation()); + this.handleTrainRun(data, strategy, trainPassenger, tripPlan, nextStationPlan); + } + } + } + // 更新nextTime + if (!CollectionUtils.isEmpty(realRunList)) { + RealRun last = realRunList.get(realRunList.size() - 1); + if (nextTime == null || nextTime.isAfter(last.getTime())) { + nextTime = last.getTime(); + } + } + } + systemTime = nextTime; + } + } + + private void handleTrainRun(StrategyCalculateData data, + JumpStrategy strategy, + TrainPassenger trainPassenger, + TripPlan tripPlan, StationPlan stationPlan) { + Stand stand = strategy.getStand(); + String groupNumber = trainPassenger.getGroupNumber(); + List realRunList = data.queryRealRuns(groupNumber); + RealRun lastRun = null; + int offset = 0; + if (!CollectionUtils.isEmpty(realRunList)) { + // 获取上一个实际运行,并计算时间偏移 + lastRun = realRunList.get(realRunList.size() - 1); + TripPlan lastTripPlan = data.queryTripPlan(lastRun.getServiceNumber(), lastRun.getTripNumber()); + StationPlan lastStationPlan = lastTripPlan.queryStationPlanByStationCode(lastRun.getStationCode()); + if (lastRun.isArrive()) { + offset = lastRun.getTime().toLocalTime().toSecondOfDay() - lastStationPlan.getArriveTime().toSecondOfDay(); + } else { + offset = lastRun.getTime().toLocalTime().toSecondOfDay() - lastStationPlan.getLeaveTime().toSecondOfDay(); + } + } + if (Objects.equals(stand.getSection(), stationPlan.getSection()) && !strategy.isJumped()) { // 跳停站台计划 + // 跳停策略指定的跳停站台,构建跳停运行,并更新策略 + RealRun passing = this.buildRealRun(groupNumber, tripPlan, stationPlan, false, + stationPlan.getArriveTime(), offset, data.getSystemTime()); + data.addRealRun(passing); + strategy.jumpedByTrain(groupNumber, tripPlan); + strategy.addTarget1(-Config.TRAIN_PASS_SAVE_TIME); + StandPassenger standPassenger = data.getStandPassengerByStand(stand); + strategy.addTarget2(standPassenger.getWait()); + return; + } + // 非跳停,根据计划更新预测运行 + if (lastRun == null || + !Objects.equals(stationPlan.getStation().getCode(), lastRun.getStationCode())) { + RealRun arrive = this.buildRealRun(groupNumber, tripPlan, stationPlan, true, + stationPlan.getArriveTime(), offset, data.getSystemTime()); + data.addRealRun(arrive); + } + int parkTime = stationPlan.getParkTime(); + // 列车到站乘客上车,更新列车上人数 + if (stationPlan.getSection().getStandList().size() == 0) { + return; + } + StandPassenger standPassenger = data.getStandPassengerByStand(stationPlan.getSection().getStandList().get(0)); + int wait = standPassenger.getWait(); // 站台等待乘客数 + float predict = (parkTime - Config.INVALID_BOARD_TIME) * Config.PASSENGER_BOARD_SPEED; // 根据停站预测的可上车人数 + int remain = Config.TRAIN_CAPACITY - trainPassenger.getNum(); // 列车上剩余可载人数 + float min = Math.min(Math.min(wait, predict), remain); // 实际上车人数 + standPassenger.minus(min); + trainPassenger.plus(min); + // 大客流站计算指标 + if (data.isLpfStand(stationPlan.getSection())) { + // 乘客等待时间(按人数统计,不考虑时间) + strategy.addTarget2(standPassenger.getWait()); + } + // 生成预测实际运行图 + RealRun leave = this.buildRealRun(trainPassenger.getGroupNumber(), tripPlan, stationPlan, false, + stationPlan.getLeaveTime(), offset, data.getSystemTime()); + data.addRealRun(leave); } @Override @@ -70,18 +269,4 @@ public class JumpStrategyServiceImpl implements StrategyService { return "跳停策略服务"; } - private RealRun handleTrainLeave(StrategyCalculateData data, JumpStrategy strategy, TrainPassenger trainPassenger, - TripPlan tripPlan, StationPlan stationPlan, int offsetTime) { - Stand stand = strategy.getStand(); - LocalTime arriveTime = stationPlan.getArriveTime(); - LocalTime leaveTime = stationPlan.getLeaveTime(); - // 生成预测实际运行图 - RealRun leave = this.buildRealRun(trainPassenger.getGroupNumber(), tripPlan, stationPlan, false, leaveTime, offsetTime, data.getSystemTime()); - data.addRealRun(leave); - // 大客流站计算指标 - if (Objects.equals(stationPlan.getSection(), stand.getSection())) { - - } - return leave; - } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/ParkTimeStrategyServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/ParkTimeStrategyServiceImpl.java index 0fe232ce0..1b80e0dbd 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/ParkTimeStrategyServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/service/ParkTimeStrategyServiceImpl.java @@ -78,7 +78,7 @@ public class ParkTimeStrategyServiceImpl implements StrategyService