客流策略——跳停策略逻辑开发

This commit is contained in:
walker-sheng 2021-02-02 18:35:41 +08:00
parent 44965d8054
commit 99ddf2db15
9 changed files with 285 additions and 22 deletions

View File

@ -1,6 +1,7 @@
package club.joylink.rtss.simulation.cbtc.data.plan; 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.Section;
import club.joylink.rtss.simulation.cbtc.data.map.Stand;
import club.joylink.rtss.simulation.cbtc.data.map.Station; import club.joylink.rtss.simulation.cbtc.data.map.Station;
import lombok.Builder; import lombok.Builder;
import lombok.Getter; import lombok.Getter;
@ -356,4 +357,37 @@ public class TripPlan {
} }
return null; 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;
}
} }

View File

@ -166,7 +166,9 @@ public class PassengerFlowSimulateService {
private void sendTrainPassengerFlowData(Simulation simulation, Map<String, TrainPassengerFlow> trainPassengerFlowMap) { private void sendTrainPassengerFlowData(Simulation simulation, Map<String, TrainPassengerFlow> trainPassengerFlowMap) {
List<Map<String, Object>> trainPFNumList = new ArrayList<>(); List<Map<String, Object>> trainPFNumList = new ArrayList<>();
for (TrainPassengerFlow flow : trainPassengerFlowMap.values()) { List<TrainInfo> superviseTrainList = simulation.getRepository().getSuperviseTrainList();
for (TrainInfo trainInfo : superviseTrainList) {
TrainPassengerFlow flow = trainPassengerFlowMap.get(trainInfo.getGroupNumber());
Map<String, Object> trainPfNum = new HashMap<>(); Map<String, Object> trainPfNum = new HashMap<>();
trainPfNum.put("code", flow.getTrain().getGroupNumber()); trainPfNum.put("code", flow.getTrain().getGroupNumber());
trainPfNum.put("num", flow.getPassengerQuantity()); trainPfNum.put("num", flow.getPassengerQuantity());

View File

@ -7,6 +7,8 @@ public final class Config {
public static final int STRATEGY_CAL_TIME = 30; public static final int STRATEGY_CAL_TIME = 30;
/** 站台大客流判断基准值 */ /** 站台大客流判断基准值 */
public static final int STAND_LPF_TRIGGER = 800; 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; public static final int STAND_MAX_STOP_TIME = 90;
/** 列车最大容量 */ /** 列车最大容量 */

View File

@ -44,6 +44,7 @@ public class StrategyCalculateAndRecommendService implements ApplicationContextA
} }
for (Strategy strategy : list) { for (Strategy strategy : list) {
strategyService.calculate(data.clone(), strategy); strategyService.calculate(data.clone(), strategy);
strategy.buildDescription();
strategy.handleTarget(); strategy.handleTarget();
log.info(String.format("策略[%s]计算指标为:%s", strategy.getDescription(), log.info(String.format("策略[%s]计算指标为:%s", strategy.getDescription(),
strategy.targetDebugStr())); strategy.targetDebugStr()));

View File

@ -1,6 +1,7 @@
package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; 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.map.Stand;
import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter; import lombok.Getter;
@ -11,6 +12,11 @@ public class JumpStrategy extends Strategy {
*/ */
@JsonIgnore @JsonIgnore
Stand stand; Stand stand;
/**
* 是否已经计算过跳停
*/
@JsonIgnore
private boolean jumped;
private String groupNumber; private String groupNumber;
/** 服务号 */ /** 服务号 */
String serviceNumber; String serviceNumber;
@ -28,7 +34,16 @@ public class JumpStrategy extends Strategy {
@Override @Override
public String buildDescription() { 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; return this.description;
} }
public void jumpedByTrain(String groupNumber, TripPlan tripPlan) {
this.jumped = true;
this.groupNumber = groupNumber;
this.serviceNumber = tripPlan.getServiceNumber();
this.tripNumber = tripPlan.getTripNumber();
}
} }

View File

@ -18,7 +18,6 @@ public class ParkTimeStrategy extends Strategy {
super(Type.PARK_TIME); super(Type.PARK_TIME);
this.stand = stand; this.stand = stand;
this.time = parkTime; this.time = parkTime;
this.buildDescription();
} }
public String getStandCode() { public String getStandCode() {

View File

@ -1,5 +1,6 @@
package club.joylink.rtss.simulation.cbtc.passenger.strategy.data; 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.map.Stand;
import club.joylink.rtss.simulation.cbtc.data.plan.RealRun; import club.joylink.rtss.simulation.cbtc.data.plan.RealRun;
import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan;
@ -30,6 +31,11 @@ public class StrategyCalculateData {
Map<String, StandPassenger> standPassengerMap = new ConcurrentHashMap<>(); Map<String, StandPassenger> standPassengerMap = new ConcurrentHashMap<>();
/**
* 大客流站台
*/
List<StandPassenger> lpfStandList = new ArrayList<>();
List<Stand> leftStandList; List<Stand> leftStandList;
List<Stand> rightStandList; List<Stand> rightStandList;
@ -59,6 +65,9 @@ public class StrategyCalculateData {
for (StandPassengerFlow standPassengerFlow : allStandPassengerFlow) { for (StandPassengerFlow standPassengerFlow : allStandPassengerFlow) {
StandPassenger standPassenger = new StandPassenger(standPassengerFlow); StandPassenger standPassenger = new StandPassenger(standPassengerFlow);
this.standPassengerMap.put(standPassenger.getStand().getCode(), standPassenger); this.standPassengerMap.put(standPassenger.getStand().getCode(), standPassenger);
if (standPassenger.isLpf()) {
this.lpfStandList.add(standPassenger);
}
} }
List<Stand> leftList = new ArrayList<>(); List<Stand> leftList = new ArrayList<>();
List<Stand> rightList = new ArrayList<>(); List<Stand> rightList = new ArrayList<>();
@ -104,6 +113,9 @@ public class StrategyCalculateData {
standPassengerMap.put(code, standPassenger.clone()); standPassengerMap.put(code, standPassenger.clone());
}); });
obj.standPassengerMap = standPassengerMap; obj.standPassengerMap = standPassengerMap;
for (StandPassenger standPassenger : this.lpfStandList) {
obj.lpfStandList.add(standPassenger.clone());
}
obj.leftStandList = this.leftStandList; obj.leftStandList = this.leftStandList;
obj.rightStandList = this.rightStandList; obj.rightStandList = this.rightStandList;
List<TrainPassenger> tpList = new ArrayList<>(); List<TrainPassenger> tpList = new ArrayList<>();
@ -148,7 +160,11 @@ public class StrategyCalculateData {
return null; return null;
} }
public RealRun queryPreviousRealRunData(String groupNumber) { public List<RealRun> queryRealRuns(String groupNumber) {
return this.runMap.get(groupNumber);
}
public RealRun queryLastRealRunData(String groupNumber) {
List<RealRun> realRuns = this.runMap.get(groupNumber); List<RealRun> realRuns = this.runMap.get(groupNumber);
if (!CollectionUtils.isEmpty(realRuns)) { if (!CollectionUtils.isEmpty(realRuns)) {
realRuns.sort(Comparator.comparing(RealRun::getTime)); realRuns.sort(Comparator.comparing(RealRun::getTime));
@ -180,4 +196,13 @@ public class StrategyCalculateData {
public StandPassenger getStandPassengerByStand(Stand stand) { public StandPassenger getStandPassengerByStand(Stand stand) {
return this.standPassengerMap.get(stand.getCode()); 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;
}
} }

View File

@ -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.RealRun;
import club.joylink.rtss.simulation.cbtc.data.plan.StationPlan; import club.joylink.rtss.simulation.cbtc.data.plan.StationPlan;
import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; 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.JumpStrategy;
import club.joylink.rtss.simulation.cbtc.passenger.strategy.data.StandPassenger; 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.StrategyCalculateData;
@ -13,8 +14,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.time.LocalTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -49,20 +51,217 @@ public class JumpStrategyServiceImpl implements StrategyService<JumpStrategy> {
log.debug(String.format("第一个站,不生成跳停策略")); log.debug(String.format("第一个站,不生成跳停策略"));
return null; return null;
} }
List<Stand> jumpableStandList = new ArrayList<>(); List<Stand> jumpableStandList;
if (right) { if (right) {
jumpableStandList = rightStandList.subList(0, index); jumpableStandList = rightStandList.subList(0, index);
} else { } else {
jumpableStandList = leftStandList.subList(0, index); jumpableStandList = leftStandList.subList(0, index);
} }
List<TrainPassenger> trainPassengerList = data.getTrainPassengerList(); List<TrainPassenger> 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())); log.debug(String.format("生成跳停策略[%s]个", list.size()));
return list; return list;
} }
private boolean isJustJumped(StrategyCalculateData data, Stand stand) {
List<TrainPassenger> trainPassengerList = data.getTrainPassengerList();
List<RealRun> 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<RealRun> 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<RealRun> 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 @Override
public void calculate(StrategyCalculateData data, JumpStrategy strategy) { public void calculate(StrategyCalculateData data, JumpStrategy strategy) {
List<TrainPassenger> 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<RealRun> 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<StationPlan> 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<RealRun> 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 @Override
@ -70,18 +269,4 @@ public class JumpStrategyServiceImpl implements StrategyService<JumpStrategy> {
return "跳停策略服务"; 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;
}
} }

View File

@ -78,7 +78,7 @@ public class ParkTimeStrategyServiceImpl implements StrategyService<ParkTimeStra
// 上一实际到发 // 上一实际到发
RealRun realRun = trainPreviousMap.get(groupNumber); RealRun realRun = trainPreviousMap.get(groupNumber);
if (realRun == null) { if (realRun == null) {
realRun = data.queryPreviousRealRunData(groupNumber); realRun = data.queryLastRealRunData(groupNumber);
if (realRun != null) { if (realRun != null) {
trainPreviousMap.put(groupNumber, realRun); trainPreviousMap.put(groupNumber, realRun);
} }