diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/AtpSectionService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/AtpSectionService.java index 5b4664996..4defaeee0 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/AtpSectionService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/AtpSectionService.java @@ -119,12 +119,11 @@ public class AtpSectionService { removes.add(atpSection); continue; } - atpSection.communicateTrainOccupy(right); +// atpSection.communicateTrainOccupy(right); } if (!removes.isEmpty()) { atpSectionList.removeAll(removes); } -// atpSectionList.forEach(atpSection -> atpSection.communicateTrainOccupy(right)); } else { // 非通信车 List
sectionList = repository.queryTrainOccupySectionList(train.getGroupNumber()); if (!CollectionUtils.isEmpty(sectionList)) { @@ -137,7 +136,7 @@ public class AtpSectionService { } } //将列车占压的物理区段及其关联的逻辑区段全部设为非通信车占用 - atpSectionList.forEach(atpSection -> atpSection.nonCommunicateTrainOccupy(right)); +// atpSectionList.forEach(atpSection -> atpSection.nonCommunicateTrainOccupy(right)); } return atpSectionList; } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/GroundAtpApiServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/GroundAtpApiServiceImpl.java index 92b772d70..43625dad7 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/GroundAtpApiServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/GroundAtpApiServiceImpl.java @@ -10,11 +10,8 @@ import club.joylink.rtss.simulation.cbtc.data.map.Switch; import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Component public class GroundAtpApiServiceImpl implements GroundAtpApiService { @@ -39,23 +36,42 @@ public class GroundAtpApiServiceImpl implements GroundAtpApiService { SimulationDataRepository repository = simulation.getRepository(); // 区段ATP占用检测 Map> trainAtpSectionMap = new HashMap<>(); // 真实列车占用 + Set
clearList = new HashSet<>(); + Map> ctSectionsMap = new HashMap<>(); + Map> nctSectionMap = new HashMap<>(); List onlineTrainList = repository.getOnlineTrainList(); for (VirtualRealityTrain train : onlineTrainList) { List
trainAtpSectionList = this.atpSectionService.atpSectionOccupyCheck(simulation, train); trainAtpSectionMap.put(train.getGroupNumber(), trainAtpSectionList); - // 清除旧的占用 - List
oldList = repository.queryTrainOccupyAtpSectionList(train.getGroupNumber()); - if (!CollectionUtils.isEmpty(oldList)) { - oldList.removeAll(trainAtpSectionList); - oldList.forEach(section -> { - if (!section.isNctOccupied()) { // 计轴不再占用,清除区段占用 - section.clearOccupy(); - } - }); + for (Section section : trainAtpSectionList) { + if (section.getParent() != null) { + clearList.add(section.getParent()); + } + } + if (train.isCommunicable()) { + ctSectionsMap.put(train, trainAtpSectionList); + } else { + nctSectionMap.put(train, trainAtpSectionList); } - // 更新保存新的占用 - repository.addOrUpdateTrainOccupyAtpSectionList(train.getGroupNumber(), trainAtpSectionList); } + // 先清除占用 + for (Section section : clearList) { + for (Section logic : section.getLogicList()) { + logic.clearOccupy(); + } + } + // 通信车占用 + ctSectionsMap.forEach(((train, sections) -> { + for (Section section : sections) { + section.communicateTrainOccupy(train.isRight()); + } + })); + // 非通信车占用 + nctSectionMap.forEach(((train, sections) -> { + for (Section section : sections) { + section.nonCommunicateTrainOccupy(train.isRight()); + } + })); // 信号机接近信息、停稳信息通知 Map nctApproachSignalMap = this.atpSectionService.collectAndSendSignalApproachMessage2CI(simulation, onlineTrainList, trainAtpSectionMap); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSLogicLoop.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSLogicLoop.java index e133d9f2e..934ef09a0 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSLogicLoop.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSLogicLoop.java @@ -1,6 +1,8 @@ package club.joylink.rtss.simulation.cbtc.ATS; -import club.joylink.rtss.simulation.cbtc.ATS.service.*; +import club.joylink.rtss.simulation.cbtc.ATS.service.AtsStandService; +import club.joylink.rtss.simulation.cbtc.ATS.service.AtsStationService; +import club.joylink.rtss.simulation.cbtc.ATS.service.AtsTrainMonitorService; import club.joylink.rtss.simulation.cbtc.ATS.tools.TrainOutboundLoadTool; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants; @@ -18,47 +20,8 @@ public class ATSLogicLoop { @Autowired private AtsStandService atsStandService; - - @Autowired - private AtsPlanService atsPlanService; - - @Autowired - private AtsRouteSettingService atsRouteSettingService; - - @Autowired - private AtsTrainService atsTrainService; - @Autowired private AtsStationService atsStationService; - -// public void run(Simulation simulation) { -//// long start = System.currentTimeMillis(); -// // 列车按计划上线 -// this.atsPlanService.initOutboundTrain(simulation); -// // ATS自动排列列车进路 -// List superviseTrainList = simulation.getRepository().getSuperviseTrainList(); -// this.atsRouteSettingService.checkAndSettingTrainRoute(simulation, superviseTrainList); -// -// // ATS按计划处理列车 -// superviseTrainList.forEach(train -> { -// if (train.isPlanTrain()) { -// this.atsPlanService.planContinue(simulation, train); -// } else { -// this.atsTrainService.runContinue(simulation, train); -// } -// }); -// -// // 站台倒计时 -// this.atsStandService.countDown(simulation); -// // 车站随时间变化的属性更新 -// this.atsStationService.updateField(simulation); -// // 如果站台没有扣车,取消列车的扣车状态 -// simulation.getRepository().getStandList().stream().filter(stand -> !stand.isHoldTrain()) -// .forEach(stand -> atsStandService.checkAndCancelTrainHold(simulation, stand)); -//// long end = System.currentTimeMillis(); -//// System.out.println(String.format("------------ATS自动排列进路逻辑耗时: %s ms", (end-start))); -// } - @Autowired private AtsTrainMonitorService atsTrainMonitorService; @Autowired diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsHeadTrainService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsHeadTrainService.java deleted file mode 100644 index 125c8d7eb..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsHeadTrainService.java +++ /dev/null @@ -1,10 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import org.springframework.stereotype.Component; - -/** - * ATS头码车服务 - */ -@Component -public class AtsHeadTrainService { -} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsManualTrainService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsManualTrainService.java deleted file mode 100644 index 925dd26d4..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsManualTrainService.java +++ /dev/null @@ -1,10 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import org.springframework.stereotype.Component; - -/** - * 人工车服务 - */ -@Component -public class AtsManualTrainService { -} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanService.java deleted file mode 100644 index 5ed788683..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanService.java +++ /dev/null @@ -1,738 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import club.joylink.rtss.simulation.cbtc.ATS.ATSMessageCollectAndDispatcher; -import club.joylink.rtss.simulation.cbtc.Simulation; -import club.joylink.rtss.simulation.cbtc.constant.RunLevel; -import club.joylink.rtss.simulation.cbtc.constant.SimulationModule; -import club.joylink.rtss.simulation.cbtc.data.CalculateService; -import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; -import club.joylink.rtss.simulation.cbtc.data.map.*; -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.data.support.RoutePath; -import club.joylink.rtss.simulation.cbtc.data.support.SectionPosition; -import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; -import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain; -import club.joylink.rtss.simulation.cbtc.exception.SimulationException; -import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType; -import club.joylink.rtss.simulation.cbtc.onboard.ATP.OnboardAtpApiService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Objects; - -/** - * 运行计划服务 - */ -@Slf4j -@Component -public class AtsPlanService { - @Autowired - private AtsStandService atsStandService; - - @Autowired - private OnboardAtpApiService onboardAtpApiService; - - @Autowired - private ATSMessageCollectAndDispatcher atsMessageCollectAndDispatcher; - - /** - * @param simulation - * @param train 停靠列车 - * @param arriveStation - * @param parkingSection 停靠站台 - * @param arrive true 到站; false 发车 - */ - public void recordTrainRealRun(Simulation simulation, TrainInfo train, - Station arriveStation, Section parkingSection, boolean arrive) { - SimulationDataRepository repository = simulation.getRepository(); - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - LocalDateTime systemTime = simulation.getSystemTime(); - Boolean frontTurnBack = null; - RealRun lastRealRun = repository.queryLastRealRun(train.getGroupNumber()); - if (lastRealRun != null && lastRealRun.getRight() != null && !Objects.equals(lastRealRun.getRight(), tripPlan.isRight())) { - frontTurnBack = !arrive; - } - RealRun realRun = RealRun.builder(). - groupNumber(train.getGroupNumber()). - serviceNumber(train.getServiceNumber()). - tripNumber(train.getTripNumber()). - stationCode(arriveStation.getCode()). - sectionCode(parkingSection.getCode()). - arrive(arrive). - right(tripPlan.isRight()). - time(systemTime). - second(systemTime.toLocalTime().toSecondOfDay()). - frontTurnBack(frontTurnBack). - build(); - repository.addRealRunRecord(realRun); - - this.atsMessageCollectAndDispatcher.sendTrainRunData(simulation, realRun); - } - - /** - * 根据计划处理列车接下来的行动 - */ - public void planContinue(Simulation simulation, TrainInfo train) { - if (!train.isPlanTrain()) { // 只处理计划车 - return; - } - SimulationDataRepository repository = simulation.getRepository(); - if (train.isInbound()) { // 准备回库列车 - int deleteRemain = train.getDeleteRemain(); - deleteRemain -= SimulationModule.ATS.getRateMs(); - if (deleteRemain <= 0) { // 删除列车 - repository.deleteOnlineTrain(train.getGroupNumber()); - repository.deleteSuperviseTrain(train.getGroupNumber()); - } else { - train.updateDeleteRemain(deleteRemain); - } - return; - } - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - if (train.isParking()) { // 列车站台停靠 - this.handleTrainParking(simulation, train, tripPlan); - } - if (train.isStop()) { // 列车停车中,判断是否折返/入库 - this.handleStoppedTrain(simulation, train, tripPlan); - } else { - // 列车运行中 - this.checkAndUpdateTrainNextTarget(simulation, train, tripPlan); - } - } - - private void checkAndUpdateTrainNextTarget(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - if (!train.hasPositionAndDirection()) { - return; - } - SimulationDataRepository repository = simulation.getRepository(); - Boolean right = train.getRight(); - String arriveStandTrackCode = train.getEstimatedArriveStandTrack(); - if (Objects.nonNull(arriveStandTrackCode)) { - boolean jump = false; - boolean hold = false; - // 列车预计到达站台轨存在,根据列车当前位置,判断是否存在到达目标的路径 - Section target = repository.getByCode(arriveStandTrackCode, Section.class); - Section headSection = repository.getByCode(train.getPhysicalSection(), Section.class); - Float distance = CalculateService - .calculateDistance(new SectionPosition(headSection, 0), - new SectionPosition(target, 0), right); - if (Objects.isNull(distance)) { - // 如果无法到达预计到达的区段,则尝试查询是否导向其他站台 - Station targetStation = target.getStation(); - int count = 0; - Section nextStopStandSection = null; - Section base = headSection; - while (count < 20) { - ++count; - if ((base.isNormalStandTrack() || base.isTransferTrack()) && - Objects.equals(targetStation, base.getStation())) { - nextStopStandSection = base; - break; - } - Section section = base.getNextRunningSectionOf(right); - if (Objects.isNull(section)) { - // 未找到 - break; - } else { - base = section; - } - } - if (Objects.nonNull(nextStopStandSection)) { - // 找到,更新 - if (!Objects.equals(arriveStandTrackCode, nextStopStandSection.getCode())) { - train.updateEstimatedArriveInfo(nextStopStandSection, train.getEstimatedArriveTime()); - this.onboardAtpApiService.updateNextSection(simulation, train.getGroupNumber(), - nextStopStandSection.getCode()); - } - jump = this.atsStandService.isJump(nextStopStandSection, train.getGroupNumber()); - hold = this.atsStandService.isHoldTrain(nextStopStandSection); - } - } else { - // 可以到达 - jump = this.atsStandService.isJump(target, train.getGroupNumber()); - hold = this.atsStandService.isHoldTrain(target); - } - // 更新跳停状态 - if (jump && Objects.equals(tripPlan.getLastStationPlan().getStation(), target.getStation())) { - // 最后一站,不跳停 - jump = true; - } - if (!Objects.equals(train.isJump(), jump)) { - if (jump) { - this.onboardAtpApiService.setJump(simulation, train.getGroupNumber()); - } else { - this.onboardAtpApiService.cancelJump(simulation, train.getGroupNumber()); - } - } - // 更新扣车状态 - if (!Objects.equals(train.isHold(), hold)) { - if (hold) { - this.onboardAtpApiService.standHoldTrain(simulation, train.getGroupNumber()); - } else { - this.onboardAtpApiService.standCancelHoldTrain(simulation, train.getGroupNumber()); - } - } - } else { - // 如果预计到达下一站台不存在 - log.warn(String.format("列车[%s]没有预计到达", train.getGroupNumber())); - } - } - - /** - * 处理停车的列车 - */ - private void handleStoppedTrain(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - if (this.isTrainOnTurnBackTrackPrepareUpdateTripPlan(simulation, train, tripPlan)) { - // 到达计划终点 - if (tripPlan.isBackup()) { - // 备用计划 - log.debug(String.format("列车[%s]到达备用计划终点", train.getGroupNumber())); - this.handleBackupPlanTrain(simulation, train, tripPlan); - } else if (tripPlan.isTurnBack()) { - // 折返计划 - log.debug(String.format("列车[%s]折返", train.getGroupNumber())); - this.handleTrainTurnBack(simulation, train, tripPlan); - } - } - } - - private boolean isTrainOnTurnBackTrackPrepareUpdateTripPlan(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - if (!train.hasPositionAndDirection()) { - return false; - } - Section headSection = repository.getByCode(train.getPhysicalSection(), Section.class); - Section axleSection = headSection; -// if (headSection.isSwitchTrack()) { -// axleSection = headSection.getParent(); -// } - Boolean right = train.getRight(); - StationPlan lastStationPlan = tripPlan.getLastStationPlan(); - Station station = lastStationPlan.getStation(); - // 列车是否到达终点站的折返轨,且列车应该已经到达过最后一个车站 - if (axleSection.isTurnBackTrack() && Objects.equals(station, axleSection.getStation())) { - // 判断是否整个车停在折返轨 - Section pre = axleSection.getSectionOf(!right); - if (axleSection.isSwitchTrack()) { - if (pre == null) { - pre = axleSection.getRelSwitch().getA().getSectionOf(!right); - } - if (pre != null && pre.isOccupied()) { - return false; - } - } else { - if (pre.isOccupied()) { - return false; - } - } - if (tripPlan.isTurnBackSection(axleSection)) { // 列车在计划折返轨 - return true; - } else { // 若列车不在计划的折返轨,判断车次最后到达车站计划到列车位置进路是否到列车当前位置 - Signal signal = lastStationPlan.getSection().getSignalOf(right); - if (Objects.nonNull(signal) && !CollectionUtils.isEmpty(signal.getRouteList())) { - for (Route route : signal.getRouteList()) { - if (route.isRouteSection(axleSection)) { - return true; - } - } - } - } - } - return false; - } - - /** - * 处理折返列车 - */ - private void handleTrainTurnBack(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - TripPlan nextTripPlan = repository.queryTurnBackNextTrip(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - if (Objects.isNull(nextTripPlan)) { - log.debug(String.format("列车[%s]折返未找到[%s]下一计划车次", - train.debugStr(), tripPlan.debugStr())); - return; - } -// LocalTime systemTime = simulation.getSystemTime().toLocalTime(); -// if (systemTime.isAfter(nextTripPlan.getFirstStationPlan().getArriveTime().minusSeconds(60))) { -// this.turnBackTrain(simulation, train, nextTripPlan); -// } - this.turnBackTrain(simulation, train, nextTripPlan); - } - - private void turnBackTrain(Simulation simulation, TrainInfo train, TripPlan nextTripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - this.onboardAtpApiService.updateTripPlan(simulation, train.getGroupNumber(), nextTripPlan); - train.applyNewTripPlan(nextTripPlan); - // 更新下一站 - Section section = repository.getByCode(train.getPhysicalSection(), Section.class); - StationPlan nextStationPlan = nextTripPlan.getFirstStationPlan(); - if (Objects.equals(nextStationPlan.getSection(), section)) { - // 站前折返 - nextStationPlan = nextTripPlan.queryNextStationPlan(nextStationPlan.getStation()); - } - train.updatePlanInfo(nextStationPlan); - long runningTime = ChronoUnit.SECONDS.between(nextTripPlan.getStartTime(), nextStationPlan.getArriveTime()); - // 第一站默认停车 - this.onboardAtpApiService.updateNextStation(simulation, train.getGroupNumber(), - nextStationPlan.getStation().getCode(), - nextStationPlan.getSection().getCode(), runningTime, - true); - train.updateEstimatedArriveInfo(nextStationPlan.getSection(), nextStationPlan.getArriveTime()); - } - - /** - * 处理备用计划车 - */ - private void handleBackupPlanTrain(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - TripPlan nextTripPlan = repository.getBackUpNextTripPlan(tripPlan.getLastStationPlan().getStation(), tripPlan.getServiceNumber()); - if (Objects.isNull(nextTripPlan)) { - //找不到计划,不处理 - return; - } - if (simulation.getSystemTime().toLocalTime().plusSeconds(60).isAfter(nextTripPlan.getStartTime())) { - //有计划折返,且到发车时间 - this.turnBackTrain(simulation, train, nextTripPlan); - } - } - - /** - * 处理列车站台停靠 - */ - private void handleTrainParking(Simulation simulation, TrainInfo train, TripPlan tripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - Section section = repository.getByCode(train.getActualArriveStandTrack(), Section.class); - Boolean right = train.getRight(); -// if (this.atsStandService.isHoldTrain(section)) { -// // 站台扣车中 -// if (!train.isHold()) { -// this.onboardAtpApiService.holdTrain(simulation, train.getGroupNumber()); -// } -// return; -// } -// // 站台无扣车,如果列车还扣车中,取消列车扣车 -// if (train.isHold()) { -// this.onboardAtpApiService.cancelHoldTrain(simulation, train.getGroupNumber()); -// } - LocalTime systemTime = simulation.getSystemTime().toLocalTime(); - LocalTime estimatedLeaveTime = train.getEstimatedLeaveTime(); - Station station = repository.getByCode(train.getStationCode(), Station.class); - StationPlan stationPlan = tripPlan.queryStationPlanByStation(station); - // 检查是否人为站前折返 - if (repository.getConfig().isAtsAutoHandleManualFrontTurnBack()) { - int ftbRemain = train.getFtbRemain(); - if (ftbRemain > 0) { - // 站前折返延时执行中 - ftbRemain -= SimulationModule.ATS.getRateMs(); - if (ftbRemain <= 0) { - TripPlan nextTripPlan = repository.queryTurnBackNextTrip(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - if (Objects.nonNull(nextTripPlan)) { - train.frontTurnBackEnd(); - this.turnBackTrain(simulation, train, nextTripPlan); - } else { - log.debug(String.format("列车[%s]人为站前折返未找到[%s]下一计划车次", - train.debugStr(), tripPlan.debugStr())); - } - } else { - train.updateFtbRemain(ftbRemain); - } - return; - } - if (ftbRemain <= 0 && this.checkIfFrontTurnBack(simulation, train, section, tripPlan, stationPlan)) { - train.frontTurnBackStart(); - return; - } - } - if (!Objects.equals(section, stationPlan.getSection()) && - !tripPlan.isFirstPlan(stationPlan) && - !tripPlan.isLastPlan(stationPlan)) { - // 停车站台轨不是到发计划的站台轨,不处理 - return; - } - StationPlan nextStationPlan = tripPlan.queryNextStationPlan(station); - // 列车启动出发,记录并更新相关信息 - if (train.getSpeed() > 0) { // 列车出发了 - // 站台停站状态取消 - this.atsStandService.trainLeaveStand(section); - // 记录列车离开 - this.recordTrainRealRun(simulation, train, stationPlan.getStation(), section, false); - // 更新列车实际离开信息 - train.updateLeaveInfo(section, systemTime); - // 修改预期到达的下一站和时间 - if (Objects.nonNull(nextStationPlan)) { - int intervalRunTime = this.atsStandService.getIntervalRunTime(simulation, section); - if (intervalRunTime <= 0) { - intervalRunTime = nextStationPlan.getArriveTime().toSecondOfDay() - stationPlan.getLeaveTime().toSecondOfDay(); - } - LocalTime arriveTime = systemTime.plusSeconds(intervalRunTime); - train.updateEstimatedArriveInfo(nextStationPlan.getSection(), arriveTime); - if (!tripPlan.isLastPlan(nextStationPlan) && - this.atsStandService.isJump(nextStationPlan.getSection(), train.getGroupNumber())) { - this.onboardAtpApiService.setJump(simulation, train.getGroupNumber()); - } - } else { - train.updateEstimatedArriveInfo(null, null); - } - } else { // 列车是否达到出发时间 - // 到达出发时间,通知列车出发 - // 下一到发计划存在,更新下一到站 - if (Objects.nonNull(nextStationPlan)) { - if (Objects.equals(train.getPlanStandTrack(), nextStationPlan.getSection().getCode())) { - return; - } else { - long planTime = ChronoUnit.SECONDS.between(stationPlan.getLeaveTime(), nextStationPlan.getArriveTime()); - long runningTime = planTime; - if (estimatedLeaveTime != null) { - runningTime = ChronoUnit.SECONDS.between(estimatedLeaveTime, - nextStationPlan.getArriveTime()); - } - if (runningTime > planTime * 2) { - runningTime = planTime * 2; - } - train.updatePlanInfo(nextStationPlan); - this.onboardAtpApiService.updateNextStation(simulation, - train.getGroupNumber(), - nextStationPlan.getStation().getCode(), - nextStationPlan.getSection().getCode(), runningTime, - tripPlan.isPlanParking(nextStationPlan)); - } - } else { // 下一计划不存在 - // 如果是最后一站 - if (train.isTurnBack()) { - return; - } else if (tripPlan.isLastPlan(stationPlan) && - Objects.equals(stationPlan.getSection(), section)) { - Signal signal = section.getSignalOf(right); - Section tbSection = null; - if (Objects.nonNull(signal) && signal.isNormalOpen()) { - for (Route route : signal.getRouteList()) { - if (route.isOpen()) { - Section lastRouteSection = route.getLastRouteSection(); - if (lastRouteSection.isTurnBackTrack()) { - tbSection = lastRouteSection; - } - } - } - } - if (Objects.nonNull(tbSection)) { - log.info(String.format("列车[%s]开始站后折返,折返轨[%s(%s)]", - train.getGroupNumber(), tbSection.getName(), tbSection.getCode())); - // 站后折返 - train.startTurnBack(tbSection); - // 开始折返 - this.onboardAtpApiService.startTurnBack(simulation, train.getGroupNumber(), - tbSection.getCode()); - } - } - } - } - } - - /** - * 检查是否站前折返 - */ - private boolean checkIfFrontTurnBack(Simulation simulation, TrainInfo train, - Section trainSection, TripPlan tripPlan, StationPlan stationPlan) { - // 检查人为站前折返 - SimulationDataRepository repository = simulation.getRepository(); - if (tripPlan.isLastPlan(stationPlan)) { - // 计划站前折返 - if (tripPlan.isFrontTurnBack() && Objects.equals(trainSection, stationPlan.getSection())) { - return true; - } - // 人工控制站前折返 - // 到最后一站计划 - if (tripPlan.isTurnBack() && !tripPlan.isFrontTurnBack()) { - TripPlan nextTripPlan = repository.queryServiceNextTripPlan(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - if (Objects.nonNull(nextTripPlan) && - train.isParking() && Objects.equals(nextTripPlan.getFirstStationPlan().getSection(), trainSection)) { - // 列车到达计划最后车站 - return true; - } - } - if (Objects.equals(trainSection.getStation(), stationPlan.getStation()) && - stationPlan.getStation().isTurnBack() && tripPlan.isTurnBack()) { - // 是折返车站 - // 折返进路信号开放 - Boolean right = train.getRight(); - Signal oppositeSignal = trainSection.getSignalOf(!right); - if (Objects.nonNull(oppositeSignal) && oppositeSignal.isNormalOpen()) { - // 折返进路开放,可以折返 - return true; - } - } - } - return false; - } - - public void handleTrainPassingStation(Simulation simulation, TrainInfo train, Station station, Section section) { - if (!train.isPlanTrain()) { // 暂时只处理计划车 - return; - } - SimulationDataRepository repository = simulation.getRepository(); - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - LocalTime systemTime = simulation.getSystemTime().toLocalTime(); - // 记录列车过站 - this.recordTrainRealRun(simulation, train, station, section, false); - // 更新列车实际离开信息 - train.updateLeaveInfo(section, systemTime); - // 更新列车计划 - StationPlan stationPlan = tripPlan.queryStationPlan(station); - if (tripPlan.isLastPlan(stationPlan)) { - // 暂时只处理正常按计划运行情况,不考虑终点站人为站前折返跳停情况 - if (tripPlan.isBehindTurnBack()) { - // 站后折返 - train.startTurnBack(tripPlan.getEndSection()); - // 开始折返 - this.onboardAtpApiService.startTurnBack(simulation, train.getGroupNumber(), - tripPlan.getEndSection().getCode()); - } - } else { - StationPlan nextStationPlan = tripPlan.queryNextStationPlan(station); - if (Objects.nonNull(nextStationPlan)) { - train.updatePlanInfo(nextStationPlan); - long runningTime = ChronoUnit.SECONDS.between(stationPlan.getLeaveTime(), nextStationPlan.getArriveTime()); - this.onboardAtpApiService.updateNextStation(simulation, train.getGroupNumber(), - nextStationPlan.getStation().getCode(), nextStationPlan.getSection().getCode(), runningTime, - tripPlan.isPlanParking(nextStationPlan)); - // 判断如果车站设置的是指定列车跳停,则自动取消指定的本车组号列车跳停 - this.atsStandService.checkAndCancelGivenTrainJump(simulation, section, train.getGroupNumber()); - if (train.isJump() && !this.atsStandService.isJump(nextStationPlan.getSection(), train.getGroupNumber())) { - // 取消列车跳停 - this.onboardAtpApiService.cancelJump(simulation, train.getGroupNumber()); - } - // 通知列车出发 - this.onboardAtpApiService.departure(simulation, train.getGroupNumber()); - // 更新预计达到 - int intervalRunTime = this.atsStandService.getIntervalRunTime(simulation, section); - if (intervalRunTime <= 0) { - intervalRunTime = nextStationPlan.getArriveTime().toSecondOfDay() - stationPlan.getLeaveTime().toSecondOfDay(); - } - LocalTime arriveTime = systemTime.plusSeconds(intervalRunTime); - train.updateEstimatedArriveInfo(nextStationPlan.getSection(), arriveTime); - } - } - } - - /** - * 加载出库列车上线 - */ - public void initOutboundTrain(Simulation simulation) { - if (!simulation.isPlanRunning()) { // 未开始计划运行 - return; - } - SimulationDataRepository repository = simulation.getRepository(); - List tripPlanList = repository.getOutboundTripPlan(); - for (TripPlan tripPlan : tripPlanList) { - if (tripPlan.isTimeToOutBound(simulation.getSystemTime().toLocalTime())) { - if (!tripPlan.isDeparture()) { // 计划未发车 - this.tryInitOutboundTrain(simulation, tripPlan); - } else { // 计划车次已发车,查询对应的列车是否上线运行,若未上线且按计划未回库,且没有列车跑此服务的记录,将列车以人工车方式加载到转换轨 - this.checkAndInitManualOutboundTrain(simulation, tripPlan); - } - } - } - } - - private void checkAndInitManualOutboundTrain(Simulation simulation, TripPlan tripPlan) { - if (tripPlan.isDispatched()) { // 已经派出列车 - return; - } - SimulationDataRepository repository = simulation.getRepository(); - VirtualRealityTrain train = repository.queryTrain(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - if (Objects.isNull(train)) { - throw new SimulationException(SimulationExceptionType.System_Fault); - } - if (repository.isVrTrainOnline(train.getGroupNumber())) { // 已经在线上运行 - return; - } - for (RealRun realRun : repository.getRealRunRecordList()) { - if (Objects.equals(realRun.getGroupNumber(), train.getGroupNumber())) { // 在线上运行过可能已经回库 - return; - } - } - List serverTripPlanList = repository.getTripPlanList(tripPlan.getServiceNumber()); - TripPlan inboundPlan = serverTripPlanList.get(serverTripPlanList.size() - 1); - if (simulation.getSystemTime().toLocalTime().isAfter(inboundPlan.getEndTime())) { // 已过列车回库时间 - return; - } - // 加载人工车 - boolean right = tripPlan.isRight(); - Section section = tripPlan.getStartSection(); - SectionPosition headPosition = new SectionPosition(section, section.getStopPointByDirection(right)); - // 列车上线并构建ATS监控列车信息 - train.initManualTrain(headPosition, right); - TrainInfo trainInfo = TrainInfo.constructManualTrain(train); - trainInfo.tracking(train); - repository.addOnlineTrain(train); - repository.addTrainInfo(trainInfo); - } - - private void tryInitOutboundTrain(Simulation simulation, TripPlan tripPlan) { - SimulationDataRepository repository = simulation.getRepository(); - VirtualRealityTrain train = repository.getTrain(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - StationPlan firstStationPlan = tripPlan.getFirstStationPlan(); - Section startSection = firstStationPlan.getSection(); - StationPlan secondStationPlan = tripPlan.getSecondStationPlan(); - Section endSection = secondStationPlan.getSection(); -// Objects.nonNull(secondStationPlan)?secondStationPlan.getSection():tripPlan.getEndSection(); - if (tripPlan.isBehindDepart()) { - endSection = startSection; - startSection = tripPlan.getStartSection(); - } - // 根据路径获取加载方向 - List routePaths = repository.getRoutePaths(startSection, endSection); - RoutePath routePath = routePaths.get(0); - boolean right = routePath.isRight(); - if (startSection.isOccupied() || - (Objects.nonNull(startSection.getSectionOf(right)) && - startSection.getSectionOf(right).isOccupied())) { - // 转换轨有车占用,不加载 - return; - } - float offset = startSection.getStopPointByDirection(right); - SectionPosition headPosition = new SectionPosition(startSection, offset); - // 下一计划到达:第一车站到发计划 - boolean isBackUp = false; - for (VirtualRealityTrain realityTrain : repository.getOnlineTrainList()) { - if (Objects.equals(realityTrain.getHeadPosition().getSection(), startSection)) { - // 转换轨有车占用,不加载 - if (!realityTrain.isBackUp()) { - return; - } - //且是备用车 - isBackUp = true; - train = realityTrain; - } - } - // 加载列车到转换轨 - train.initPlanTrain(headPosition, right, tripPlan); - if (Objects.equals(RunLevel.ITC, repository.getConfig().getRunMode())) { - train.setITCMode(); - } - tripPlan.dispatch(); - repository.addOnlineTrain(train); - // 添加监控 - TrainInfo trainInfo; - if (isBackUp) { - trainInfo = repository.getSupervisedTrainByGroup(train.getGroupNumber()); - } else { - trainInfo = new TrainInfo(train.getGroupNumber()); - } - trainInfo.initPlan(tripPlan, firstStationPlan, repository.getConfig()); - StationPlan nextPlan = firstStationPlan; - if (Objects.equals(firstStationPlan.getSection(), startSection)) { -// if (Objects.isNull(secondStationPlan)) { -// secondStationPlan = StationPlan.builder().park(true).arriveTime(tripPlan.getEndTime()).leaveTime(tripPlan.getEndTime()).section(tripPlan.getEndSection()).station(tripPlan.getEndSection().getStation()).build(); -// } - nextPlan = secondStationPlan; - } - trainInfo.updatePlanInfo(nextPlan); - long runningTime = ChronoUnit.SECONDS.between(tripPlan.getStartTime(), nextPlan.getArriveTime()); - this.onboardAtpApiService.updateNextStation(simulation, train.getGroupNumber(), - nextPlan.getStation().getCode(), nextPlan.getSection().getCode(), runningTime, - nextPlan.isPark()); - if (!isBackUp) { - simulation.getRepository().addTrainInfo(trainInfo); - } - } - - /** - * 处理列车到站 - */ - public void handleArriveStation(Simulation simulation, TrainInfo train, Station station, Section section) { - log.info(String.format("ATS收到列车[%s-%s|%s|%s]车站[%s(%s)]站台轨[%s(%s)]停靠消息", - train.getGroupNumber(), train.getServiceNumber(), - train.getTripNumber(), train.getDestinationCode(), - station.getName(), station.getCode(), - section.getName(), section.getCode())); - if (train.isPlanTrain()) { //先记录实迹运行图,以免ATS主逻辑改掉服务-车次号 - this.recordTrainRealRun(simulation, train, station, section, true); - } - // 列车到站状态 - LocalTime systemTime = simulation.getSystemTime().toLocalTime(); - int parkTime = this.atsStandService.trainParkingAndGetParkTime(simulation, section); - train.updateArriveInfo(systemTime, station, section); - if (parkTime < 0) { - if (train.isPlanTrain()) { - SimulationDataRepository repository = simulation.getRepository(); - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - // 更新追踪列车到站状态 - StationPlan stationPlan = tripPlan.queryStationPlanByStation(station); - parkTime = stationPlan.getParkTime(); - if (tripPlan.isFirstPlan(stationPlan)) { // 第一个计划 - int actualParkTime = stationPlan.getLeaveTime().toSecondOfDay() - systemTime.toSecondOfDay(); - if (actualParkTime > parkTime) { - parkTime = actualParkTime; - } - } - if (this.checkIfFrontTurnBack(simulation, train, section, tripPlan, stationPlan)) { - //是站台折返车 - //如果停车时间为0 ,则取后一个车次计划的首站发车时间 - TripPlan nextTripPlan = repository.queryServiceNextTripPlan(tripPlan.getServiceNumber(), tripPlan.getTripNumber()); - StationPlan nextFirstStationPlan = nextTripPlan.queryStationPlanByStation(station); - int actual = nextFirstStationPlan.getLeaveTime().toSecondOfDay() - systemTime.toSecondOfDay(); - if (actual > parkTime) { - parkTime = actual; - } - } - } else { - // 头码车和人工车,取默认站台停站时间 - parkTime = 30; - } - } - LocalTime leaveTime = systemTime.plusSeconds(parkTime); - train.updateEstimatedLeaveInfo(section, leaveTime); - this.atsStandService.updateStandParkTime(section, parkTime); - // 更新停站时间 - this.onboardAtpApiService.updateStationParkTime(simulation, train.getGroupNumber(), parkTime); - // 更新扣车状态 - List standList = section.getStandList(); - if (!CollectionUtils.isEmpty(standList)) { - if (standList.stream().anyMatch(Stand::isHoldTrain)) { - onboardAtpApiService.standHoldTrain(simulation, train.getGroupNumber()); - } else { - onboardAtpApiService.standCancelHoldTrain(simulation, train.getGroupNumber()); - } - } - } - - public void handleTrainOnTransfer(Simulation simulation, TrainInfo train, Section section) { - if (!section.isTransferTrack()) { - throw new SimulationException(SimulationExceptionType.System_Fault, - String.format("区段[%s]不是转换轨", section.debugStr())); - } - if (train.isInbound()) { - return; - } - SimulationDataRepository repository = simulation.getRepository(); - if (!train.isPlanTrain()) { - //非计划车转|换轨入库 - List routePaths = repository.queryRoutePathsByEnd(section); - if (!CollectionUtils.isEmpty(routePaths)) { - boolean right = routePaths.get(0).isRight(); - if (Objects.equals(right, train.getRight())) { - log.debug(String.format("非计划列车[%s]入库", train.getGroupNumber())); - train.finishPlanPrepareInbound(); - } - } - return; - } - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - if (Objects.equals(tripPlan.getEndSection(), section)) { - // 入库列车 - log.debug(String.format("列车[%s]入库", train.getGroupNumber())); - train.finishPlanPrepareInbound(); - this.recordTrainRealRun(simulation, train, tripPlan.getEndSection().getStation(), section, false); - } - } - -} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanTrainService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanTrainService.java deleted file mode 100644 index 532296182..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsPlanTrainService.java +++ /dev/null @@ -1,20 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import club.joylink.rtss.simulation.cbtc.Simulation; -import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; -import org.springframework.stereotype.Component; - -/** - * 计划车服务 - */ -@Component -public class AtsPlanTrainService { - - public void run(Simulation simulation, TrainInfo trainInfo) { - if (!trainInfo.isPlanTrain()) { - return; - } - - } - -} 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 deleted file mode 100644 index 0901a69fa..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsRouteSettingService.java +++ /dev/null @@ -1,797 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import club.joylink.rtss.simulation.cbtc.CI.CiApiService; -import club.joylink.rtss.simulation.cbtc.Simulation; -import club.joylink.rtss.simulation.cbtc.constant.DriveMode; -import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; -import club.joylink.rtss.simulation.cbtc.data.map.*; -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.support.SectionPosition; -import club.joylink.rtss.simulation.cbtc.data.support.StationTurnBackStrategyOption; -import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; -import club.joylink.rtss.simulation.cbtc.exception.SimulationException; -import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType; -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.util.CollectionUtils; - -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.util.*; - -@Slf4j -@Component -public class AtsRouteSettingService { - - @Autowired - private CiApiService ciApiService; - - public void checkAndSettingTrainRoute(Simulation simulation, List superviseTrainList) { - //获取需要排列进路的列车以及对应进路信息 - List waitSetList = this.queryWaitSettingRoutes(simulation, superviseTrainList); - - //根据一定规则清除掉冲突进路 -// this.filterConflictTrainRouteByPlanTime(simulation, waitSetList); - - // 执行进路排列 - for (TrainRoute trainRoute : waitSetList) { - Route route = trainRoute.getRoute(); - route.setTrain(trainRoute.getTrain()); - this.ciApiService.settingRoute(simulation, route.getCode()); - } - } - - private List queryWaitSettingRoutes(Simulation simulation, List trainList) { - SimulationDataRepository repository = simulation.getRepository(); - // 根据列车位置,确定需要排列进路的列车和信号机 - Map trainPositionMap = this.getAllTrainPosition(simulation, trainList); - Map signalTrainMap = getTrain2SignalOfSetRoute(simulation, trainList, trainPositionMap); -// log.debug(String.format("触发的关闭信号有[%s], 共[%s]个", -// String.join(",", signalTrainMap.keySet().stream() -// .map(Signal::getName).collect(Collectors.toList())), -// signalTrainMap.size())); - List waitSettingList = new ArrayList<>(); - LocalDateTime systemTime = simulation.getSystemTime(); - // 根据找到的列车触发信号机,根据列车类型判定是否触发 - signalTrainMap.forEach((signal, train) -> { -// log.debug(String.format("ATS为列车[%s]触发信号机[%s(%s)]的进路", -// train.getGroupNumber(), signal.getName(), signal.getCode())); - Route route = null; - if (train.isPlanTrain()) { // 计划车 - route = this.queryPlanTrainNeedRoute(simulation, train, signal, trainList); - } else if (train.isHeadCodeTrain()) { // 头码车 - route = this.queryHeadTrainNeedRoute(simulation, train, signal, trainList); - } - if (Objects.nonNull(route)) { - waitSettingList.add(new TrainRoute(train, trainPositionMap.get(train.getGroupNumber()), - systemTime, route)); - } - }); - return waitSettingList; - } - - private Route queryHeadTrainNeedRoute(Simulation simulation, TrainInfo train, - Signal signal, List trainList) { - if (!train.isHeadCodeTrain()) { // 不是头码车,返回 - return null; - } - Boolean right = train.getRight(); - SimulationDataRepository repository = simulation.getRepository(); - Section headSection = repository.getByCode(train.getPhysicalSection(), Section.class); // 列车所在区段 - // 查询目的地定义 - DestinationCodeDefinition destDefinition = repository.findDestinationCodeDefinition(train.getDestinationCode()); - Signal neededSignal = signal; - List routePaths = new ArrayList<>(); - if (Objects.nonNull(destDefinition)) { - // 如果已经到达目的地,返回 - if (Objects.equals(headSection, destDefinition.getSection())) { - return null; - } - if (!CollectionUtils.isEmpty(destDefinition.getRoutes())) { - return destDefinition.queryNextRoute(headSection); - } else if (train.getEstimatedArriveStandTrack() != null) { - Section targetSection = repository.getByCode(train.getEstimatedArriveStandTrack(), Section.class); - routePaths = repository.queryRoutePathsByEndAndContainsSection(targetSection, headSection); -// routePaths = CalculateService.queryRoutePathsOnDirection(headSection, targetSection, true, 10); -// if (CollectionUtils.isEmpty(routePaths)) { -// routePaths = CalculateService.queryRoutePathsOnDirection(headSection, targetSection, false, 10); -// } - } else { - // 目的地定义存在,根据目的地定义查询路径,办理进路 - // 判断是否终点站折返办理 - if (train.isParking() && destDefinition.isLoop()) { // 列车停车,且目的地为环路运营 - Section section = destDefinition.getEndStationParkSection(right); - if (Objects.equals(section, headSection)) { // 列车停靠终点站对应站台 - Station station = destDefinition.getStationOf(right); - if (destDefinition.isFrontTurnBack(station)) { // 站前折返 - neededSignal = section.getSignalOf(!right); - List paths = repository.queryRoutePathsByStart(section); - for (RoutePath path : paths) { // 筛选方向一致且终端区段是站台轨的 - if (Objects.equals(path.isRight(), !right) && path.getEnd().isNormalStandTrack()) { - routePaths.add(path); - } - } - } else { // 站后折返 - train.startTurnBack(null); - StationTurnBackStrategyOption strategy = null; - if (Objects.nonNull(station.getTbStrategyId())) { - strategy = station.getCurrentTurnBackStrategy(); - } - return this.selectTbRouteByStrategy(simulation, train, signal, null, strategy, trainList); - } - } - } - // 非终点折返办理,根据列车预计到站查询 - if (Objects.nonNull(train.getEstimatedArriveStandTrack())) { - // 查询到达预计到站的路径 - Section eaStandSection = repository.getByCode(train.getEstimatedArriveStandTrack(), Section.class); - List paths = repository.queryRoutePathsByEnd(eaStandSection); - for (RoutePath path : paths) { - if (path.containsSection(headSection) && path.isRight() == right) { - routePaths.add(path); - } - } - // 如果预计到达不是终点,尝试查询从预计到达开始的路径 - if (!destDefinition.isEndSection(eaStandSection, right)) { - List pathList = repository.queryRoutePathsByStart(eaStandSection); - for (RoutePath routePath : pathList) { - if (destDefinition.containsSection(routePath.getEnd(), right)) { - routePaths.add(routePath); - } - } - } - } - } - } else { - // 按交路查询办理,如果找到才办理 - Routing routing = train.getRouting(); - if (Objects.isNull(routing)) { - routing = this.queryHeadTrainRouting(repository, train); - if (Objects.nonNull(routing)) { - train.setRouting(routing); - } - } - if (Objects.nonNull(routing)) { - List
viaSectionList = routing.getViaSectionList(); - for (int i = 1; i < viaSectionList.size(); i++) { - Section start = viaSectionList.get(i - 1); - Section end = viaSectionList.get(i); - List paths = repository.getRoutePaths(start, end); - for (RoutePath path : paths) { - if (path.containsSignal(neededSignal)) { - routePaths.add(path); - } else if (train.isStop() && headSection.isFunctionTrack() && - path.isStartSection(headSection) && !Objects.equals(right, path.isRight())) { - // 列车在折返轨停车准备折返情况判断 - neededSignal = headSection.getSignalOf(path.isRight()); - routePaths.add(path); - } - } - if (routePaths.size() > 0) { - break; - } - } - } - } - // 根据路径和信号机查询需要办理的进路 - Route route = this.selectRouteOfPaths(simulation, train, neededSignal, routePaths, trainList); - return route; - } - - private Routing queryHeadTrainRouting(SimulationDataRepository repository, TrainInfo train) { - Section headSection = repository.getByCode(train.getPhysicalSection(), Section.class); - List routingList = repository.queryRoutingByDestCode(train.getDestinationCode()); - for (Routing routing1 : routingList) { - List
viaSectionList = routing1.getViaSectionList(); - for (int i = 1; i < viaSectionList.size(); i++) { - Section start = viaSectionList.get(i - 1); - Section end = viaSectionList.get(i); - if (Objects.equals(start, end)) { - continue; - } - List routePaths = repository.getRoutePaths(start, end); - for (RoutePath routePath : routePaths) { - if (routePath.containsSection(headSection)) { - return routing1; - } - } - } - } - return null; - } - - private Route queryPlanTrainNeedRoute(Simulation simulation, TrainInfo train, - Signal signal, List trainList) { - if (!train.isPlanTrain()) { - return null; - } - LocalDateTime systemTime = simulation.getSystemTime(); - SimulationDataRepository repository = simulation.getRepository(); - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - // 特殊情况检查处理 - if (tripPlan.isBehindTurnBack() && tripPlan.isLastPlanStationSection(signal.getSection())) { - // 列车到达终点站台准备折返 - if (signal.getSection().isNormalStandTrack() && - train.isParkingStand(signal.getSection().getStandList().get(0))) { - // 折返计划的正常站后折返起始信号机,根据折返策略查询进路 - Station station = signal.getSection().getStation(); - StationTurnBackStrategyOption strategy = null; - if (Objects.nonNull(station.getTbStrategyId())) { - strategy = station.getCurrentTurnBackStrategy(); - } - return this.selectTbRouteByStrategy(simulation, train, signal, tripPlan.getEndSection(), strategy, trainList); - } - } - // 根据列车下一计划到达查询进路路径和需要开放的信号机 - Signal neededSignal = signal; - List routePaths = new ArrayList<>(); - Section trainSection = repository.getByCode(train.getPhysicalSection(), Section.class); - if (Objects.nonNull(train.getPlanStandTrack())) { - Section nextSection = repository.getByCode(train.getPlanStandTrack(), Section.class); - if (!Objects.equals(trainSection, nextSection)) { - if (train.isParking() && !trainSection.isTransferTrack()) { // 列车在站台停车 - // 处理站前折返的进路排列 - routePaths = repository.queryRoutePaths(trainSection, nextSection); - } else if (train.isStop() && !trainSection.isNormalStandTrack() && (trainSection.isTurnBackTrack())) { // 折返轨停车 - // 判断从当前时间到折返后下一计划到站时间中是否存在冲突计划 - if (this.containsConflictPlan(simulation, tripPlan)) { - log.debug(String.format("折返与出库冲突,不排列")); - return null; - } - List routePaths1 = repository.queryRoutePaths(trainSection, nextSection); - if (!CollectionUtils.isEmpty(routePaths1)) { - routePaths = routePaths1; - } - } - } - if (!CollectionUtils.isEmpty(routePaths)) { - boolean right = routePaths.get(0).isRight(); - if (!Objects.equals(right, signal.isRight())) { // 方向相反,需要折返 - neededSignal = trainSection.getSignalOf(right); // 实际需要排列的信号机 - if (neededSignal.isNormalOpen()) { // 站前折返进路已经排列,返回 - return null; - } - } - } else { - routePaths = new ArrayList<>(); - // 正常情况处理 - List planList = tripPlan.getPlanList(); - for (int i = 1; i < planList.size(); i++) { - StationPlan plan1 = planList.get(i - 1); - StationPlan plan2 = planList.get(i); - if (tripPlan.isOutbound() && tripPlan.isFirstPlan(plan1)) { - if (plan1.getLeaveTime().isAfter(systemTime.toLocalTime())) { - log.debug(String.format("出库列车未到发车时间,不排列进路")); - return null; - } - } - List paths = repository.getRoutePaths(plan1.getSection(), plan2.getSection()); - boolean find = false; - for (RoutePath path : paths) { - if (path.containsSignal(neededSignal)) { - routePaths.add(path); - find = true; - } - } - if (find) { - break; - } - } - } - } - // 根据路径和信号机查询需要办理的进路 - Route route = this.selectRouteOfPaths(simulation, train, neededSignal, routePaths, trainList); - return route; - } - - /** - * 判断当前时间到折返计划的下一计划第一站到站时间之间是否存在冲突的出库计划 - * - * @param simulation - * @param tripPlan - * @return - */ - private boolean containsConflictPlan(Simulation simulation, TripPlan tripPlan) { - LocalTime current = simulation.getSystemTime().toLocalTime(); - LocalTime arriveTime = tripPlan.getFirstStationPlan().getArriveTime(); - SimulationDataRepository repository = simulation.getRepository(); - // 判断是否有冲突的出库计划 - if (tripPlan.isTurnBack()) { - if (current.isAfter(arriveTime)) { - return false; - } - List planList = repository.queryOutBoundTripPlanBetween(current, arriveTime); - if (!planList.isEmpty()) { - for (TripPlan plan : planList) { - if (Objects.equals(plan.getSecondStationPlan().getStation(), tripPlan.getFirstStationPlan().getStation())) { - return true; - } - } - } - } - // 判断是否有冲突的运行计划 - StationPlan stationPlan = repository.queryStationPlanBetween(tripPlan.getPlanList().get(0).getStation(), current, arriveTime); - return stationPlan != null; - } - - private Route selectRouteOfPaths(Simulation simulation, TrainInfo train, - Signal signal, List routePaths, List trainList) { - if (CollectionUtils.isEmpty(routePaths)) { - return null; - } - List signalRouteList = signal.getRouteList(); - if (CollectionUtils.isEmpty(signalRouteList)) { - return null; - } - if (!CollectionUtils.isEmpty(signalRouteList)) { - // 筛选首选列车路径 - SimulationDataRepository repository = simulation.getRepository(); - RoutePath firstChoiceRoutePath = repository.selectFirstChoiceRoutePath(routePaths); - // 根据首选列车进路尝试排列 - List triggerList = new ArrayList<>(); - for (Route route : signalRouteList) { - if (firstChoiceRoutePath.isPathContainRouteLastSection(route)) { - // 折返进路在列车计划到达存在且不是折返轨时不触发办理 - if (route.isTurnBack() && firstChoiceRoutePath.getEnd().isNormalStandTrack()) { - continue; - } - triggerList.add(route); - } - } - if (CollectionUtils.isEmpty(triggerList)) { - // 未找到,可以根据信号机变通策略再次在所有列车路径中筛选, - } else if (triggerList.size() > 1) { - if (this.hasSameEnd(triggerList)) { - // 多延续保护进路,取延续保护是定位的那条 - // todo 暂时逻辑定为只触发定位保护进路,后需完善为根据接下来的运行目的选择延续保护 - Route alternative = null; - for (Route route : triggerList) { - if (this.isAtsTrigger(simulation, train, route, false, trainList)) { - if (this.isOverlapStraight(route)) { - return route; - } else { - alternative = route; - } - } - } - if (alternative != null) - return alternative; - } else { - for (Route route : triggerList) { - if (this.isAtsTrigger(simulation, train, route, false, trainList)) { - return route; - } - } - } - } else { - if (this.isAtsTrigger(simulation, train, triggerList.get(0), false, trainList)) { - return triggerList.get(0); - } - } - } - return null; - } - - private boolean isOverlapStraight(Route route) { - RouteOverlap overlap = route.getOverlap(); - if (Objects.nonNull(overlap)) { - List pathList = overlap.getPathList(); - if (CollectionUtils.isEmpty(pathList)) { - throw new SimulationException(SimulationExceptionType.System_Fault); - } - if (pathList.size() > 1) { - return true; - } else { - if (pathList.get(0).isStraight()) { - return true; - } else { - return false; - } - } - } - return true; - } - - private boolean hasSameEnd(List routeList) { - if (!CollectionUtils.isEmpty(routeList) && routeList.size() > 1) { - Signal end = routeList.get(0).getDestination(); - for (Route route : routeList) { - if (!Objects.equals(route.getDestination(), end)) { - return false; - } - } - return true; - } - return false; - } - - /** - * 根据折返策略选择进路 - * - * @param simulation - * @param train - * @param signal - * @param defaultTb 默认折返轨,可能为null - * @param strategy - * @return - */ - private Route selectTbRouteByStrategy(Simulation simulation, TrainInfo train, Signal signal, - Section defaultTb, StationTurnBackStrategyOption strategy, - List trainList) { - Route tbRoute = null; - List
tbSectionList = new ArrayList<>(); - if (Objects.isNull(strategy)) { - if (Objects.nonNull(defaultTb)) { - tbSectionList.add(defaultTb); - } - } else { - switch (strategy.getType()) { - // 按计划 - case NONE: { - if (Objects.nonNull(defaultTb)) { - tbSectionList.add(defaultTb); - } - break; - } - // 仅某个折返轨 - case ONLY: { - tbSectionList.add(strategy.getSectionList().get(0)); - break; - } - // 折返轨等价 - case FIRST: - case EQUAL: { - tbSectionList.addAll(strategy.getSectionList()); - break; - } - } - } - SimulationDataRepository repository = simulation.getRepository(); - List routeList = signal.getRouteList(); - List tbRouteList = new ArrayList<>(); - for (Route route : routeList) { - if (route.isTurnBack()) { - tbRouteList.add(route); - tbSectionList.add(route.getSectionList().get(route.getSectionList().size() - 1)); - } - } - if (!tbRouteList.isEmpty()) { - routeList = tbRouteList; - } - for (Section section : tbSectionList) { - for (Route route : routeList) { - if (route.isRouteSection(section)) { - if (this.isAtsTrigger(simulation, train, route, true, trainList)) { - tbRoute = route; - break; - } - } - } - } - return tbRoute; - } - - private boolean isAtsTrigger(Simulation simulation, TrainInfo train, Route route, - boolean turnBack, List trainList) { - if (route.isAtsControl() && !route.isCiControl() && !route.isOpen()) { - if (route.hasSwitchFault()) { // 进路内存在故障道岔,不触发办理 - return false; - } - MapConfig config = simulation.getRepository().getConfig(); - // 进路分为ATP/地面/引导,根据列车类型和进路类型判断 - if (config.isRouteLikeHa1()) { - if ((DriveMode.AM.equals(train.getDriveMode()) || DriveMode.CM.equals(train.getDriveMode())) && !route.isAtp()) { // 通信车 - return false; - } else if ((DriveMode.RM.equals(train.getDriveMode()) || DriveMode.NRM.equals(train.getDriveMode())) && !route.isGround()) { - return false; - } - } - // 列车停站后才能办理出站进路开放出站信号 - if (!train.isJump()) { - if (config.isSignalOpenAfterParking()) { - Section section = route.getStart().getSection(); - if (section.isNormalStandTrack()) { // 站台轨出站进路 - boolean trigger = false; - for (TrainInfo trainInfo : trainList) { - if (trainInfo.isParking() && - section.isSamePhysical(trainInfo.getPhysicalSection()) -// && trainInfo.isDoorOpen() - ) {// 列车站台停靠且开门 - trigger = true; - break; - } - } - if (!trigger) { - return false; - } - } - } - } - // 判断进路中是否存在折返列车 - for (TrainInfo trainInfo : trainList) { - if (route.isRouteSection(trainInfo.getPhysicalSection())) { - if (turnBack) { // 折返进路,进路内有车,不排列 - return false; - } - if (trainInfo.hasPositionAndDirection() && !Objects.equals(route.getStart().isRight(), trainInfo.getRight())) { - return false; - } - // 判断是否折返列车 - if (this.isTurningBackTrain(simulation, trainInfo)) { - return false; - } - } - } - return true; - } - return false; - } - - /** - * 列车是否正在折返中的列车 - * - * @param simulation - * @param train - * @return - */ - private boolean isTurningBackTrain(Simulation simulation, TrainInfo train) { - if (!train.hasPositionAndDirection()) { - return false; - } - SimulationDataRepository repository = simulation.getRepository(); - Section trainSection = repository.getByCode(train.getPhysicalSection(), Section.class); - if (trainSection.isRouteLock() && !Objects.equals(trainSection.isLockRight(), train.getRight())) { - // 列车所在区段锁闭且列车和区段方向相反,认为折返 - return true; - } - // 暂时只判断计划车 - if (train.isPlanTrain() && train.hasPositionAndDirection()) { - TripPlan tripPlan = repository.getTripPlan(train.getServiceNumber(), train.getTripNumber()); - if (tripPlan.isTurnBack() || tripPlan.isBackup()) {// 折返计划/备用计划 - if (tripPlan.isFrontTurnBack() || tripPlan.isFrontBackup()) { // 站前折返/备用 - List routePaths = repository.getRoutePaths(tripPlan.getNextToLastStationPlan().getSection(), - tripPlan.getLastStationPlan().getSection()); - for (RoutePath routePath : routePaths) { - if (!routePath.isPathSection(trainSection)) { - continue; - } - List signalList = routePath.getSignalList(); - for (Signal signal : signalList) { - if (CollectionUtils.isEmpty(signal.getRouteList())) { - continue; - } - for (Route route : signal.getRouteList()) { - if (!route.isRouteSection(routePath.getEnd())) { - continue; - } - if (route.isRouteSection(trainSection)) { - return true; - } - } - } - } - } else { // 站后折返/备用 - // 判断列车终点站台到任意折返轨路径中,列车是否在路径中 - List routePaths = repository.getRoutePaths(tripPlan.getLastStationPlan().getSection(), tripPlan.getEndSection()); - boolean right = routePaths.get(0).isRight(); - List routePaths1 = repository.queryRoutePathsByStart(tripPlan.getLastStationPlan().getSection()); - for (RoutePath routePath : routePaths1) { - if (Objects.equals(routePath.isRight(), right) && - routePath.getEnd().isTurnBackTrack() && - !routePath.getEnd().isNormalStandTrack() && - (routePath.isPathSection(trainSection) || - routePath.getEnd().isSamePhysical(trainSection.getCode()))) { - return true; - } - } - } - } - // 判断是否已经在折返轨更换车次列车 - if (Objects.equals(trainSection, tripPlan.getStartSection())) { - return true; - } -// List routePaths = new ArrayList<>(); -// if (!tripPlan.isOutbound()) { // 对于非出库 -// if (!Objects.equals(tripPlan.getStartSection(), tripPlan.getFirstStationPlan().getSection())) { // 起始非站前折返 -// routePaths.addAll(repository.getRoutePaths(tripPlan.getStartSection(), tripPlan.getFirstStationPlan().getSection())); -// } else { // 起始站前折返 -// routePaths.addAll(repository.getRoutePaths(tripPlan.getFirstStationPlan().getSection(), tripPlan.getSecondStationPlan().getSection())); -// } -// } -// if (!tripPlan.isInbound()) { // 对于非回库 -// if (tripPlan.isFrontTurnBack()) { // 站前折返 -// routePaths.addAll(repository.getRoutePaths(tripPlan.getNextToLastStationPlan().getSection(), -// tripPlan.getLastStationPlan().getSection())); -// } else { // 站后折返 -// routePaths.addAll(repository.getRoutePaths(tripPlan.getLastStationPlan().getSection(), -// tripPlan.getEndSection())); -// } -// } -// for (RoutePath routePath : routePaths) { -// if (routePath.isPathSection(trainSection)) { // 在折返路径中 -// -// return true; -// } -// } - } else if (train.isHeadCodeTrain()) { - if (train.isTurnBack()) { - return true; - } - } else { // 人工车,默认是折返 -// return true; - } - return false; - } - - private Map getAllTrainPosition(Simulation simulation, List trainList) { - Map map = new HashMap<>(); - SimulationDataRepository repository = simulation.getRepository(); - for (TrainInfo trainInfo : trainList) { - if (trainInfo.hasPositionAndDirection()) { - map.put(trainInfo.getGroupNumber(), repository.buildHeadPositionOfTrainInfo(trainInfo)); - } - } - return map; - } - - /** - * 获取需要排列进路的列车位置及对应信号机 - * - * @return - */ - private Map getTrain2SignalOfSetRoute(Simulation simulation, List trainList, - Map trainPositionMap) { - Map signalTrainMap = new HashMap<>(); - for (TrainInfo train : trainList) { - if (!train.hasPositionAndDirection()) { - continue; - } - Boolean right = train.getRight(); - SectionPosition trainPosition = trainPositionMap.get(train.getGroupNumber()); - if (Objects.isNull(trainPosition)) { - continue; - } - Section section = trainPosition.getSection(); - Signal signal = this.queryApproachCloseSignal(simulation, train, section, right); - if (Objects.isNull(signal)) { -// log.warn(String.format("列车[%s]未找到前方关闭信号机", train.getGroupNumber())); - continue; - } - TrainInfo other = signalTrainMap.get(signal); - if (Objects.isNull(other)) { - signalTrainMap.put(signal, train); - } else { - SectionPosition otherPosition = trainPositionMap.get(other.getGroupNumber()); - if (trainPosition.isAheadOf(otherPosition, right)) { - signalTrainMap.put(signal, train); - } else { - continue; - } - } -// //如果扣车且信号机属于预计到达站台轨,取消列车的排列信号机 -// if (train.isHold() && Objects.equals(signal.getSection().getCode(), train.getEstimatedArriveStandTrack())) { -// signalTrainMap.remove(signal); -// } - //如果信号机所属集中站ats故障,取消进路排列 - if (signal.getDeviceStation() != null && Station.Fault.ATS_FAULT.equals(signal.getDeviceStation().getFault())) { - signalTrainMap.remove(signal); - } - } - return signalTrainMap; - } - - /** - * 查询到达接近区段且未开放的信号机 - * - * @param simulation - * @param train - * @param section - * @param right - * @return - */ - private Signal queryApproachCloseSignal(Simulation simulation, TrainInfo train, Section section, Boolean right) { - SimulationDataRepository repository = simulation.getRepository(); - Section base = section; - Signal target = null; - Section logic = repository.getByCode(train.getSection(), Section.class); - int count = 0; - while (Objects.nonNull(base) && count < 10) { - ++count; - Signal signal = base.getSignalOf(right); - if (Objects.isNull(signal) || !signal.isClose() || - (!train.isCommunicable() && signal.isVirtual())) { - // 指定方向信号机不存在,或已经开放,或非通信车碰到虚拟信号机,继续往下找 - base = base.getNextRunningSectionOf(right); - continue; - } - // 信号机存在,判定信号关闭、接近区段 - if (signal.isApproachSection(train.getPhysicalSection())) { - target = signal; - } - break; - } - return target; - } - - private void filterConflictTrainRouteByPlanTime(Simulation simulation, List waitSetList) { - List removeList = new ArrayList<>(); - // 先过滤排列条件不足无法排列的进路 - for (int i = 0; i < waitSetList.size(); i++) { - TrainRoute trainRoute1 = waitSetList.get(i); - if (removeList.contains(trainRoute1)) { - continue; - } - TrainInfo train1 = trainRoute1.getTrain(); - Route route1 = trainRoute1.getRoute(); - for (int j = i + 1; j < waitSetList.size(); j++) { - TrainRoute trainRoute2 = waitSetList.get(j); - if (removeList.contains(trainRoute2)) { - continue; - } - TrainInfo train2 = trainRoute2.getTrain(); - Route route2 = trainRoute2.getRoute(); - boolean conflict = false; - if (Objects.equals(route1, route2)) { - conflict = true; - } else if (route1.isConflictWith(route2)) { - conflict = true; - log.debug(String.format("列车[%s-%s|%s|%s]计划进路[%s(%s)]" + - "与列车[%s-%s|%s|%s]计划进路[%s(%s)]冲突", - train1.getGroupNumber(), train1.getServiceNumber(), - train1.getTripNumber(), train1.getDestinationCode(), - route1.getName(), route1.getCode(), - train2.getGroupNumber(), train2.getServiceNumber(), - train2.getTripNumber(), train2.getDestinationCode(), - route2.getName(), route2.getCode())); - } else if (route1.containConflictSwitch(route2.getSwitchList())) { - conflict = true; - } - if (conflict) { - if (!Objects.equals(train2.isPriorityRouteSet(), train1.isPriorityRouteSet())) { - if (train2.isPriorityRouteSet()) { - removeList.add(trainRoute1); - break; - } else { - removeList.add(trainRoute2); - continue; - } - } else if (route1.getSwitchList().size() > route2.getSwitchList().size()) { - //包含道岔多的先不排 - removeList.add(trainRoute1); - break; - } else if (trainRoute1.getTime().isAfter(trainRoute2.getTime())) { - removeList.add(trainRoute1); - break; - } else { - removeList.add(trainRoute2); - } - } - } - } - waitSetList.removeAll(removeList); - } - - @Getter - private static class TrainRoute { - private TrainInfo train; - - private SectionPosition headPosition; - - private LocalDateTime time; - - private Route route; - - public TrainRoute(TrainInfo train, SectionPosition headPosition, LocalDateTime time, Route route) { - this.train = train; - this.headPosition = headPosition; - this.time = time; - this.route = route; - } - - } - -} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStationService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStationService.java index b93f4f0e7..95766c069 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStationService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsStationService.java @@ -25,7 +25,6 @@ import java.time.LocalTime; import java.util.*; import java.util.stream.Collectors; - @Component @Slf4j public class AtsStationService { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsSwitchService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsSwitchService.java deleted file mode 100644 index b67ae568d..000000000 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsSwitchService.java +++ /dev/null @@ -1,13 +0,0 @@ -package club.joylink.rtss.simulation.cbtc.ATS.service; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -/** - * ATS道岔服务 - */ -@Component -@Slf4j -public class AtsSwitchService { - -} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Section.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Section.java index 03f90c562..6814f36d6 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Section.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Section.java @@ -434,15 +434,14 @@ public class Section extends MayOutOfOrderDevice { * 区段列车出清 */ public void clearOccupy() { - synchronized (this) { - setCtOccupied(false); - this.setNctOccupied(false); - if (!CollectionUtils.isEmpty(this.logicList)) { - for (Section section : this.logicList) { - section.ctOccupied = false; - section.setNctOccupied(false); - } + setCtOccupied(false); + this.setNctOccupied(false); + if (!CollectionUtils.isEmpty(this.logicList)) { + for (Section section : this.logicList) { + section.ctOccupied = false; + section.setNctOccupied(false); } + } // if (this.isAxleCounter()) { // // 是计轴区段 // if (this.getVirtualAxleCounter().isOccupy()) { @@ -461,7 +460,6 @@ public class Section extends MayOutOfOrderDevice { // logic.setNctOccupied(false); // }); // } - } } /** @@ -776,7 +774,7 @@ public class Section extends MayOutOfOrderDevice { this.trainRight = right; this.setNctOccupied(true); // this.nctOccupied = true; - setCtOccupied(false); +// setCtOccupied(false); } public boolean isOccupiedOn(boolean right) {