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 ea78828a4..96af6df82 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 @@ -9,8 +9,10 @@ public final class Config { public static final int STAND_MAX_STOP_TIME = 90; /** 列车最大容量 */ public static final int TRAIN_CAPACITY = 1500; + /** 站台无效的上车时间(开关门6s+下车4s+发车前确认5s) */ + public static final int INVALID_BOARD_TIME = 15; /** 站台乘客上列车速度: 人/秒 (列车停站上车时间应该是总停站时间-(开关门6s+下车4s+发车前确认5s)) */ - public static final float PASSENGER_ON_SPEED = 12; + public static final float PASSENGER_BOARD_SPEED = 12; /** 列车跳停节省时间,单位 - 秒 */ public static final int TRAIN_PASS_SAVE_TIME = 60; } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/LargePassengerFlowStrategyService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/LargePassengerFlowStrategyService.java index 54ff983d3..f4ed6797f 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/LargePassengerFlowStrategyService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/LargePassengerFlowStrategyService.java @@ -13,6 +13,7 @@ import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -51,9 +52,11 @@ public class LargePassengerFlowStrategyService { private StrategyCalculateData buildStrategyCalculateData(PassengerFlowSimulationData passengerFlowSimulationData) { Simulation simulation = this.passengerFlowSimulateService.getSimulationByGroup(passengerFlowSimulationData.getGroup()); + LocalDateTime systemTime = simulation.getSystemTime(); SimulationDataRepository repository = simulation.getRepository(); Map> serviceTripsMap = repository.getServiceTripsMap(); StrategyCalculateData strategyCalculateData = new StrategyCalculateData( + systemTime, serviceTripsMap, repository.getRealRunRecordList(), passengerFlowSimulationData.getAllStandPassengerFlow(), diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/ParkTimeStrategyServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/ParkTimeStrategyServiceImpl.java index 012e52ba2..2cf2ab7e6 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/ParkTimeStrategyServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/ParkTimeStrategyServiceImpl.java @@ -1,14 +1,181 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy; -import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.Strategy; +import club.joylink.rtss.exception.BusinessExceptionAssertEnum; +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.data.ParkTimeStrategy; +import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StandPassenger; import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StrategyCalculateData; +import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.TrainPassenger; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import java.time.LocalTime; +import java.util.*; + +@Slf4j @Component -public class ParkTimeStrategyServiceImpl implements StrategyService { +public class ParkTimeStrategyServiceImpl implements StrategyService { @Override - public Strategy calculate(StrategyCalculateData data) { - return null; + public List generateStrategy(StrategyCalculateData data) { + List list = new ArrayList<>(); + Stand stand = null; + // 暂时按一个站大客流处理 + List standPassengerList = data.getAllStandPassengerList(); + for (StandPassenger standPassenger : standPassengerList) { + if (standPassenger.isLpf()) { + stand = standPassenger.getStand(); + break; + } + } + if (stand == null) { + return null; + } + Map> planMap = data.getPlanMap(); + int planParkTime = 0; // 计划停站时间 + for (List planList : planMap.values()) { + for (TripPlan tripPlan : planList) { + List stationPlanList = tripPlan.getPlanList(); + for (StationPlan stationPlan : stationPlanList) { + if (Objects.equals(stationPlan.getSection(), stand.getSection()) && + stationPlan.getParkTime() > 0) { + planParkTime = stationPlan.getParkTime(); + break; + } + } + } + if (planParkTime > 0) { + break; + } + } + if (planParkTime <= 0) { + throw new IllegalArgumentException(String.format("计划或站台[%s]数据异常,找到的计划停站时间异常:为[%s]", stand.debugStr(), planParkTime)); + } + // 最大停站时间内每隔5秒生成一个策略(策略的停站时间为计划停站时间+5*i) + int iter = (Config.STAND_MAX_STOP_TIME - planParkTime) / 5; + for (int i = 1; i <= iter; i++) { + list.add(new ParkTimeStrategy(stand, planParkTime + i * 5)); + } + log.debug(String.format("生成增大停站时间策略[%s]个", list.size())); + return list; + } + + @Override + public void calculate(StrategyCalculateData data, ParkTimeStrategy strategy) { + Stand stand = strategy.getStand(); + Integer parkTime = strategy.getTime(); + // 系统时间 + LocalTime systemTime = data.getSystemTime().toLocalTime();// 系统当前时间 + LocalTime endTime = systemTime.plusMinutes(Config.STRATEGY_CAL_TIME);// 预测计算终点 + LocalTime nextTime = systemTime; + int target1 = 0; + int target2 = 0; + List trainPassengerList = data.getTrainPassengerList(); + Map trainPreviousMap = new HashMap<>(); // 列车上一个到站/发车数据 + while (systemTime.isBefore(endTime)) { // 在计算时间内 + 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; + } + if (tripPlan.isBackup()) { + continue; + } + // 上一实际到发 + RealRun realRun = trainPreviousMap.get(groupNumber); + if (realRun == null) { + realRun = data.queryPreviousRealRunData(groupNumber); + if (realRun != null) { + trainPreviousMap.put(groupNumber, realRun); + } + } + List planList = tripPlan.getPlanList(); + int offsetTime = 0; + StationPlan nextStationPlan = null; + if (realRun != null && + Objects.equals(tripPlan.getServiceNumber(), realRun.getServiceNumber()) && + Objects.equals(tripPlan.getTripNumber(), realRun.getTripNumber())) { + // 上一实际运行数据存在且在当前计划中 + // 查询实际运行到的车站计划 + StationPlan stationPlan = tripPlan.queryStationPlanByStationCode(realRun.getStationCode()); + BusinessExceptionAssertEnum.DATA_ERROR.assertNotNull(stationPlan); + LocalTime leaveTime = stationPlan.getLeaveTime(); + if (realRun.isArrive()) { + // 是到达车站的计划,预测离站 + LocalTime arriveTime = stationPlan.getArriveTime(); + if (!tripPlan.isFirstPlan(stationPlan)) { + offsetTime = realRun.getTime().toLocalTime().toSecondOfDay() - arriveTime.toSecondOfDay(); + } + RealRun leave = this.handleTrainLeave(data, strategy, trainPassenger, tripPlan, stationPlan, offsetTime); + + trainPreviousMap.put(groupNumber, leave); + if (leave.getTime().toLocalTime().isBefore(nextTime)) { + nextTime = leave.getTime().toLocalTime(); + } + } else { + offsetTime = realRun.getTime().toLocalTime().toSecondOfDay() - leaveTime.toSecondOfDay(); + } + if (tripPlan.isLastPlan(stationPlan)) { + // 最后一个到发计划,获取下一个折返后车次计划 + TripPlan nextTripPlan = data.queryNextTripPlan(tripPlan); + if (nextTripPlan != null) { + tripPlan = nextTripPlan; + nextStationPlan = nextTripPlan.getFirstStationPlan(); + } + } else { + nextStationPlan = tripPlan.queryNextStationPlanByStationCode(realRun.getStationCode()); + } + } else { + for (StationPlan stationPlan : planList) { + if (stationPlan.getArriveTime().compareTo(systemTime) >= 0) { + nextStationPlan = stationPlan; + break; + } + } + } + if (nextStationPlan != null) { + LocalTime arriveTime = nextStationPlan.getArriveTime(); + RealRun arrive = this.buildRealRun(groupNumber, tripPlan, nextStationPlan, true, arriveTime, offsetTime, data.getSystemTime()); + data.addRealRun(arrive); + RealRun leave = this.handleTrainLeave(data, strategy, trainPassenger, tripPlan, nextStationPlan, offsetTime); + data.addRealRun(leave); + trainPreviousMap.put(groupNumber, leave); + if (leave.getTime().toLocalTime().isBefore(nextTime)) { + nextTime = leave.getTime().toLocalTime(); + } + } + } + } + } + + private RealRun handleTrainLeave(StrategyCalculateData data, ParkTimeStrategy strategy, TrainPassenger trainPassenger, + TripPlan tripPlan, StationPlan stationPlan, int offsetTime) { + Stand stand = strategy.getStand(); + int parkTime = stationPlan.getParkTime(); + LocalTime arriveTime = stationPlan.getArriveTime(); + LocalTime leaveTime = stationPlan.getLeaveTime(); + if (Objects.equals(stationPlan.getSection(), stand.getSection())) { + parkTime = strategy.getTime(); + leaveTime = arriveTime.plusSeconds(parkTime); + strategy.addTarget1(parkTime - stationPlan.getParkTime()); + } + // 列车到站乘客上车,更新列车上人数 + 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); + // 生成预测实际运行图 + RealRun leave = this.buildRealRun(trainPassenger.getGroupNumber(), tripPlan, stationPlan, false, leaveTime, offsetTime, data.getSystemTime()); + data.addRealRun(leave); + return leave; } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyService.java index 8f758f7b9..c23f9e265 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/StrategyService.java @@ -1,10 +1,33 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy; +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.data.Strategy; import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StrategyCalculateData; -public interface StrategyService { +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; - Strategy calculate(StrategyCalculateData data); +public interface StrategyService { + List generateStrategy(StrategyCalculateData data); + + void calculate(StrategyCalculateData data, T strategy); + + default RealRun buildRealRun(String groupNumber, TripPlan tripPlan, + StationPlan nextStationPlan, boolean arrive, LocalTime time, int offsetTime, + LocalDateTime systemTime) { + return RealRun.builder() + .groupNumber(groupNumber) + .serviceNumber(tripPlan.getServiceNumber()) + .tripNumber(tripPlan.getTripNumber()) + .right(tripPlan.isRight()) + .stationCode(nextStationPlan.getStation().getCode()) + .sectionCode(nextStationPlan.getSection().getCode()) + .arrive(arrive) + .time(time.plusSeconds(offsetTime).atDate(systemTime.toLocalDate())) + .build(); + } } 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 545b08cd2..937ea756c 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 @@ -1,11 +1,22 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; +import club.joylink.rtss.simulation.cbtc.data.map.Stand; +import lombok.Getter; + +@Getter public class ParkTimeStrategy extends Strategy { - /** 站台code */ - String standCode; + /** + * 站台 + */ + Stand stand; /** 停站时间 */ Integer time; + public ParkTimeStrategy(Stand stand, int parkTime) { + this.stand = stand; + this.time = parkTime; + } + @Override public String buildDescription() { this.description = ""; diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StandPassenger.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StandPassenger.java index ae084a058..d39aa9fcf 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StandPassenger.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/StandPassenger.java @@ -35,4 +35,8 @@ public class StandPassenger { obj.wait = this.wait; return obj; } + + public void minus(float n) { + this.wait -= n; + } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/Strategy.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/Strategy.java index 3918694f7..6c9876c65 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/Strategy.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/Strategy.java @@ -1,15 +1,34 @@ package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; +import lombok.Getter; + +@Getter public abstract class Strategy { String description; /** 指标1:时刻表偏差 */ - Float target1; + int target1; /** 指标2:乘客等待时间 */ - Float target2; + int target2; /** 综合指标:对指标1,2加权求和 */ - Float coTarget; + int coTarget; public abstract String buildDescription(); + + public void setTarget1(int target1) { + this.target1 = target1; + } + + public void setTarget2(int target2) { + this.target2 = target2; + } + + public void setCoTarget(int target) { + this.coTarget = target; + } + + public void addTarget1(int i) { + this.target1 += i; + } } 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 a6bc9189e..13685ca46 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,27 +1,31 @@ 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.RealRun; import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; import club.joylink.rtss.simulation.cbtc.passenger.data.StandPassengerFlow; import club.joylink.rtss.simulation.cbtc.passenger.data.TrainPassengerFlow; +import lombok.Getter; +import org.springframework.util.CollectionUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +@Getter public class StrategyCalculateData { + LocalDateTime systemTime; /** * key-服务号 */ - Map> planMap; + Map> planMap = new ConcurrentHashMap<>(); /** - * key-服务车次号 + * key-列车车组号 */ - Map> runMap; + Map> runMap = new ConcurrentHashMap<>(); - Map standPassengerMap; + Map standPassengerMap = new ConcurrentHashMap<>(); List trainPassengerList; @@ -29,28 +33,26 @@ public class StrategyCalculateData { } - public StrategyCalculateData(Map> serviceTripsMap, + public StrategyCalculateData(LocalDateTime systemTime, + Map> serviceTripsMap, List realRunRecordList, List allStandPassengerFlow, List allTrainPassengerFlow) { - this.planMap = new HashMap<>(serviceTripsMap); - Map> realRunMap = new HashMap<>(); + this.systemTime = systemTime; + this.planMap.putAll(serviceTripsMap); for (RealRun realRun : realRunRecordList) { - String stNumber = realRun.getSTNumber(); - List list = realRunMap.get(stNumber); + String groupNumber = realRun.getGroupNumber(); + List list = this.runMap.get(groupNumber); if (list == null) { list = new ArrayList<>(); - realRunMap.put(stNumber, list); + this.runMap.put(groupNumber, list); } list.add(realRun); } - this.runMap = realRunMap; - Map spMap = new HashMap<>(); for (StandPassengerFlow standPassengerFlow : allStandPassengerFlow) { StandPassenger standPassenger = new StandPassenger(standPassengerFlow); - spMap.put(standPassenger.getStand().getCode(), standPassenger); + this.standPassengerMap.put(standPassenger.getStand().getCode(), standPassenger); } - this.standPassengerMap = spMap; List tpList = new ArrayList<>(); for (TrainPassengerFlow trainPassengerFlow : allTrainPassengerFlow) { TrainPassenger trainPassenger = new TrainPassenger(trainPassengerFlow); @@ -76,4 +78,50 @@ public class StrategyCalculateData { return obj; } + public List getAllStandPassengerList() { + return new ArrayList<>(this.standPassengerMap.values()); + } + + public TripPlan queryTripPlan(String serviceNumber, String tripNumber) { + List tripPlanList = this.planMap.get(serviceNumber); + for (TripPlan tripPlan : tripPlanList) { + if (Objects.equals(tripPlan.getTripNumber(), tripNumber)) { + return tripPlan; + } + } + return null; + } + + public RealRun queryPreviousRealRunData(String groupNumber) { + List realRuns = this.runMap.get(groupNumber); + if (!CollectionUtils.isEmpty(realRuns)) { + realRuns.sort(Comparator.comparing(RealRun::getTime)); + return realRuns.get(realRuns.size() - 1); + } + return null; + } + + public void addRealRun(RealRun realRun) { + List realRuns = this.runMap.get(realRun.getGroupNumber()); + if (realRuns == null) { + realRuns = new ArrayList<>(); + this.runMap.put(realRun.getGroupNumber(), realRuns); + } + realRuns.add(realRun); + } + + public TripPlan queryNextTripPlan(TripPlan tripPlan) { + List planList = this.planMap.get(tripPlan.getServiceNumber()); + for (int i = 0; i < planList.size() - 1; i++) { + TripPlan plan = planList.get(i); + if (Objects.equals(plan, tripPlan)) { + return planList.get(i + 1); + } + } + return null; + } + + public StandPassenger getStandPassengerByStand(Stand stand) { + return this.standPassengerMap.get(stand.getCode()); + } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/TrainPassenger.java b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/TrainPassenger.java index 24bb51080..c12911cdc 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/TrainPassenger.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/passenger/strategy/data/TrainPassenger.java @@ -13,7 +13,7 @@ public class TrainPassenger { String tripNumber; /** 列车上人数 */ - int remain; + int num; public TrainPassenger() { } @@ -22,7 +22,7 @@ public class TrainPassenger { this.groupNumber = tpf.getTrain().getGroupNumber(); this.serviceNumber = tpf.getTrain().getServiceNumber(); this.tripNumber = tpf.getTrain().getTripNumber(); - this.remain = tpf.getPassengerQuantity(); + this.num = tpf.getPassengerQuantity(); } public TrainPassenger clone() { @@ -30,7 +30,18 @@ public class TrainPassenger { obj.groupNumber = this.groupNumber; obj.serviceNumber = this.serviceNumber; obj.tripNumber = this.tripNumber; - obj.remain = this.remain; + obj.num = this.num; return obj; } + + public String getSTNumber() { + if (this.serviceNumber == null && this.tripNumber == null) { + return null; + } + return String.format("%s%s", this.serviceNumber, this.tripNumber == null ? "" : this.tripNumber); + } + + public void plus(float n) { + this.num += n; + } }