diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsRouteSettingService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsRouteSettingService.java index 645752e74..6dd4c7976 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsRouteSettingService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsRouteSettingService.java @@ -681,7 +681,7 @@ public class AtsRouteSettingService { SimulationDataRepository repository = simulation.getRepository(); for (TrainInfo trainInfo : trainList) { if (trainInfo.hasPositionAndDirection()) { - map.put(trainInfo.getGroupNumber(), repository.buildSectionPositionOfTrainInfo(trainInfo)); + map.put(trainInfo.getGroupNumber(), repository.buildHeadPositionOfTrainInfo(trainInfo)); } } return map; diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStandService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStandService.java index e01ee37f3..346450f7d 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStandService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStandService.java @@ -154,7 +154,7 @@ public class AtsStandService { if (Objects.nonNull(trainInfo.getEstimatedArriveStandTrack()) && Objects.equals(section.getCode(), trainInfo.getEstimatedArriveStandTrack())) { // 是列车预计到达站台 - SectionPosition position = repository.buildSectionPositionOfTrainInfo(trainInfo); + SectionPosition position = repository.buildHeadPositionOfTrainInfo(trainInfo); if (CalculateService.isTargetSectionOnDirectionExist(position.getSection(), trainInfo.getRight(), section)) { // 可以到达,通知跳停 this.onboardAtpApiService.setJump(simulation, trainInfo.getGroupNumber()); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainService.java index 7390092d0..9507ac143 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainService.java @@ -499,7 +499,7 @@ public class AtsTrainService { return; } /* 寻找下一目标区段 */ - nextTarget = findNextTarget4HeadTrain(simulation, train, repository, headSection, headStation, trainRight, destDefinition); + nextTarget = findNextTarget4HeadTrain(simulation, train, repository, headSection, headStation, destDefinition); /* 判断换端并更新目标 */ if (nextTarget != null) { if (nextTarget.getCode().equals(estimatedArriveStandTrack)) { @@ -569,8 +569,8 @@ public class AtsTrainService { /** * 寻找头码车的下一个目标区段 */ - private Section findNextTarget4HeadTrain(Simulation simulation, TrainInfo train, SimulationDataRepository repository, Section headSection, - Station headStation, Boolean trainRight, DestinationCodeDefinition destinationCodeDefinition) { + public static Section findNextTarget4HeadTrain(Simulation simulation, TrainInfo train, SimulationDataRepository repository, Section headSection, + Station headStation, DestinationCodeDefinition destinationCodeDefinition) { Section nextTarget = null; Boolean destinationRight = destinationCodeDefinition.getRight(); switch (destinationCodeDefinition.getType()) { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsHeadTrainRouteSelectServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsHeadTrainRouteSelectServiceImpl.java index 1dfcfa611..1309871e6 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsHeadTrainRouteSelectServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsHeadTrainRouteSelectServiceImpl.java @@ -1,17 +1,76 @@ package club.joylink.rtss.simulation.cbtc.ATS.service.ars; import club.joylink.rtss.simulation.cbtc.Simulation; +import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; +import club.joylink.rtss.simulation.cbtc.data.map.DestinationCodeDefinition; import club.joylink.rtss.simulation.cbtc.data.map.Route; +import club.joylink.rtss.simulation.cbtc.data.map.Section; +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.support.RoutePath; import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; /** * 头码车进路选择 */ +@Slf4j @Component public class AtsHeadTrainRouteSelectServiceImpl implements AtsRouteSelectService { @Override public Route select(Simulation simulation, TrainInfo trainInfo) { - return null; + if (!trainInfo.isHeadCodeTrain()) { + return null; + } + SimulationDataRepository repository = simulation.getRepository(); + // 基础检查 + Boolean right = trainInfo.getRight(); + if (right == null) + return null; + DestinationCodeDefinition dcd = repository.findDestinationCodeDefinition(trainInfo.getDestinationCode()); + if (dcd == null) + return null; + Section destSection = dcd.getSection(); + Section headSection = repository.getByCode(trainInfo.getPhysicalSection(), Section.class); + if (Objects.equals(destSection, headSection)) + return null; + String estimatedArriveStandTrack = trainInfo.getEstimatedArriveStandTrack(); + if (estimatedArriveStandTrack == null) + return null; + Section targetSection = repository.getByCode(estimatedArriveStandTrack, Section.class); + if (!CollectionUtils.isEmpty(dcd.getRoutes())) { + return dcd.queryNextRoute(headSection); + } else { + // 根据车次计划查找可触发进路列表 + Object[] results = this.queryByDestinationCode(repository, trainInfo, headSection, targetSection); + // 根据计划筛选需触发进路 + return filterRoutes(repository, trainInfo, results, dcd); + } + } + + private Object[] queryByDestinationCode(SimulationDataRepository repository, TrainInfo trainInfo, Section headSection, Section targetSection) { + List routePathList = repository.queryRoutePathsByEndAndContainsSection(targetSection, headSection); + if (CollectionUtils.isEmpty(routePathList)) + return null; + Object[] result = this.queryTriggerRoutesOfRoutePath(repository, trainInfo, routePathList); + if (!(boolean) result[1]) { //不存在未触发的进路 + return null; + } + return result; + } + + private Route filterRoutes(SimulationDataRepository repository, TrainInfo trainInfo, Object[] results, DestinationCodeDefinition dcd) { + if (results == null) + return null; + List triggers = (List) results[0]; + if (CollectionUtils.isEmpty(triggers)) + return null; + return triggers.stream().min(Comparator.comparingInt(Route::getReverseSwitchQuantity)).get(); } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsPlanTrainRouteSelectServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsPlanTrainRouteSelectServiceImpl.java index a4fc4ba32..f8593a785 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsPlanTrainRouteSelectServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsPlanTrainRouteSelectServiceImpl.java @@ -72,6 +72,9 @@ public class AtsPlanTrainRouteSelectServiceImpl implements AtsRouteSelectService return route; } + /** + * 检查进路是否和未完成的计划冲突 + */ private boolean checkConflict(SimulationDataRepository repository, TrainInfo trainInfo, TripPlan tripPlan, Route route, boolean turnBack) { Section section = repository.getByCode(trainInfo.getPlanStandTrack(), Section.class); @@ -246,6 +249,9 @@ public class AtsPlanTrainRouteSelectServiceImpl implements AtsRouteSelectService return route; } + /** + * @return (List)[0]: 可触发的进路;(Boolean)[1]:是否需要折返 + */ private Object[] queryByStationPlan(SimulationDataRepository repository, TrainInfo trainInfo, TripPlan tripPlan) { Route route = null; Object[] result = new Object[3]; diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/stage/AtsHeadTrainStageService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/stage/AtsHeadTrainStageService.java index 50905cd8b..e917b45a6 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/stage/AtsHeadTrainStageService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/stage/AtsHeadTrainStageService.java @@ -1,49 +1,159 @@ package club.joylink.rtss.simulation.cbtc.ATS.service.stage; +import club.joylink.rtss.simulation.cbtc.ATS.service.AtsStandService; +import club.joylink.rtss.simulation.cbtc.ATS.service.AtsTrainService; import club.joylink.rtss.simulation.cbtc.Simulation; +import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; -import club.joylink.rtss.simulation.cbtc.data.map.Routing; +import club.joylink.rtss.simulation.cbtc.data.map.DestinationCodeDefinition; 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.support.SectionPosition; import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; +import club.joylink.rtss.simulation.cbtc.onboard.ATP.ATPService; +import club.joylink.rtss.simulation.cbtc.onboard.ATP.OnboardAtpApiService; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import java.util.List; +import java.util.Objects; /** * 头码车车阶段处理服务 */ @Component public class AtsHeadTrainStageService implements AtsStageService { + @Autowired + private ATPService atpService; + + @Autowired + private AtsTrainService atsTrainService; + + @Autowired + private OnboardAtpApiService onboardAtpApiService; + + @Autowired + private AtsStandService atsStandService; + @Override public void handleTransferTrackParking(Simulation simulation, TrainInfo trainInfo, Section parkSection) { - String destinationCode = trainInfo.getDestinationCode(); + Boolean right = trainInfo.getRight(); + if (right == null) + return; SimulationDataRepository repository = simulation.getRepository(); - if (destinationCode.equals(parkSection.getDestinationCode())) { + DestinationCodeDefinition dcd = repository.findDestinationCodeDefinition(trainInfo.getDestinationCode()); + if (dcd == null) + return; + SectionPosition headPosition = repository.buildHeadPositionOfTrainInfo(trainInfo); + if (Objects.equals(dcd.getSection(), parkSection)) { // 列车到达目的地 trainInfo.finishPlanPrepareInbound(); } + if (isArriveDestination(dcd, headPosition, right)) { + handleArriveDestination(simulation, trainInfo); + } } @Override public void handleNormalStandParking(Simulation simulation, TrainInfo trainInfo, Section parkSection) { - SimulationDataRepository repository = simulation.getRepository(); - List routings = repository.queryRoutingByDestCode(trainInfo.getDestinationCode()); - } - - @Override - public void handleTurnBackTrackParking(Simulation simulation, TrainInfo trainInfo, Section parkSection) { - if (this.isArriveDestination(simulation.getRepository(), trainInfo, parkSection)) { - + Boolean right = trainInfo.getRight(); + if (right == null) + return; + Station station = parkSection.getStation(); + List standList = station.getNormalStand(right); + int remainTime = standList.get(0).getRemainTime(); + if (remainTime < 2 && !trainInfo.isDoorOpen() && + (this.atsStandService.isStandDoorCloseOrInterlockRelease(simulation, parkSection)) && + !this.atsStandService.isHoldTrain(parkSection)) { + this.updateNextTarget(simulation, trainInfo, right); + } else if (remainTime < 9) { + this.onboardAtpApiService.departure(simulation, trainInfo.getGroupNumber()); } } @Override - public void handlePassStand(Simulation simulation, TrainInfo trainInfo) { - + public void handleTurnBackTrackParking(Simulation simulation, TrainInfo trainInfo, Section parkSection) { + Boolean right = trainInfo.getRight(); + if (right == null) + return; + this.updateNextTarget(simulation, trainInfo, right); } - private boolean isArriveDestination(SimulationDataRepository repository, TrainInfo trainInfo, Section parkSection) { + @Override + public void handlePassStand(Simulation simulation, TrainInfo trainInfo) { + Boolean right = trainInfo.getRight(); + if (right == null) + return; + updateNextTarget(simulation, trainInfo, right); + } - return false; + /** + * 如果列车到达目的地 + */ + private boolean isArriveDestination(DestinationCodeDefinition dcd, SectionPosition headPosition, boolean right) { + Section destSection = dcd.getSection(); + Section headSection = headPosition.getSection(); + return destSection.equals(headSection) && headPosition.isWithinParkingRange(right); + } + + /** + * 处理列车到达目的地 + */ + private void handleArriveDestination(Simulation simulation, TrainInfo trainInfo) { + if (simulation.getRepository().getConfig().isSetManualWhenHeadTrainArriveTarget()) { + atsTrainService.setManualTrain(simulation, trainInfo.getGroupNumber()); + } + } + + private void updateNextTarget(Simulation simulation, TrainInfo trainInfo, Boolean right) { + SimulationDataRepository repository = simulation.getRepository(); + DestinationCodeDefinition dcd = repository.findDestinationCodeDefinition(trainInfo.getDestinationCode()); + if (dcd == null) + return; + SectionPosition headPosition = repository.buildHeadPositionOfTrainInfo(trainInfo); + //如果到达终点 + if (isArriveDestination(dcd, headPosition, right)) { + handleArriveDestination(simulation, trainInfo); + return; + } + //如果还未到达目标轨 + Section headSection = headPosition.getSection(); + if (StringUtils.hasText(trainInfo.getEstimatedArriveStandTrack())) { + Section target = repository.getByCode(trainInfo.getEstimatedArriveStandTrack(), Section.class); + if (!target.equals(headSection) && target.isAheadOf(repository, headSection, right)) { + return; + } + } +// SectionPosition targetPosition = new SectionPosition(target, target.getStopPointByDirection(right)); +// if (!headSection.equals(target)) { +// if (targetPosition.isAheadOf(headPosition, right)) { +// return; +// } +// } else { +// if (!headPosition.isWithinParkingRange(right) && targetPosition.isAheadOf(headPosition, right)) { +// return; +// } +// } + //寻找目标区段 + Section nextTarget = AtsTrainService.findNextTarget4HeadTrain(simulation, trainInfo, repository, headSection, headSection.getStation(), dcd); + if (nextTarget == null) + return; + //判断调头 + if (!nextTarget.isAheadOf(repository, headSection, right)) {//下一目标区段不在当前车头方向的前方 + atpService.turnDirectionImmediately(repository.getOnlineTrainBy(trainInfo.getGroupNumber())); //调头 + } + //更新预计到站 + SectionPosition stopPosition = new SectionPosition(nextTarget, nextTarget.getStopPointByDirection(right)); + Float distance = CalculateService.calculateDistance(headPosition, stopPosition, right); + int runningTime; + if (distance != null) { + runningTime = (int) (distance / (45 / 3.6)); + } else { + runningTime = 180; + } + trainInfo.updateEstimatedArriveInfo(nextTarget, simulation.getSystemTime().toLocalTime().plusSeconds(runningTime)); + onboardAtpApiService.updateNextArriveInfo(simulation, trainInfo.getGroupNumber(), nextTarget, true, runningTime); } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/SimulationDataRepository.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/SimulationDataRepository.java index 55d61f2fa..da48b0329 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/SimulationDataRepository.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/SimulationDataRepository.java @@ -913,8 +913,8 @@ public class SimulationDataRepository { TrainInfo front = approachedTrainList.get(0); for (int i = 1; i < approachedTrainList.size(); i++) { TrainInfo b = approachedTrainList.get(i); - SectionPosition aHead = this.buildSectionPositionOfTrainInfo(front); - SectionPosition bHead = this.buildSectionPositionOfTrainInfo(b); + SectionPosition aHead = this.buildHeadPositionOfTrainInfo(front); + SectionPosition bHead = this.buildHeadPositionOfTrainInfo(b); if (bHead.isAheadOf(aHead, right)) { front = b; } @@ -926,7 +926,7 @@ public class SimulationDataRepository { } } - public SectionPosition buildSectionPositionOfTrainInfo(TrainInfo train) { + public SectionPosition buildHeadPositionOfTrainInfo(TrainInfo train) { if (!train.hasPositionAndDirection()) { throw new SimulationException(SimulationExceptionType.System_Fault, String.format("列车[%s]没有位置/方向", train.getGroupNumber())); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/support/SectionPosition.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/support/SectionPosition.java index 37714781c..a468accbd 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/support/SectionPosition.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/support/SectionPosition.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.data.support; +import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants; import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.map.Section; import lombok.Getter; @@ -98,4 +99,16 @@ public class SectionPosition { return false; } } + + /** + * 该位置位于对应区段的停车范围内 + */ + public boolean isWithinParkingRange(boolean right) { + SectionPosition targetPosition = new SectionPosition(this.section, this.section.getStopPointByDirection(right)); + SectionPosition max = CalculateService.calculateNextPositionByStartAndLen(targetPosition, + right, SimulationConstants.PARK_POINT_MAX_OFFSET); + SectionPosition min = CalculateService.calculateNextPositionByStartAndLen(targetPosition, + !right, SimulationConstants.PARK_POINT_MAX_OFFSET); + return this.isAheadOf(min, right) && max.isAheadOf(this, right); + } }