diff --git a/src/main/java/club/joylink/rtss/simulation/Simulation.java b/src/main/java/club/joylink/rtss/simulation/Simulation.java index 83884d683..edb44b51e 100644 --- a/src/main/java/club/joylink/rtss/simulation/Simulation.java +++ b/src/main/java/club/joylink/rtss/simulation/Simulation.java @@ -281,13 +281,13 @@ public abstract class Simulation onlineTrainList, - Map> trainAtpSectionMap) { - Set signalAcrossSet = new HashSet<>(); // 跨压信号机 - List signalList = simulation.getRepository().getSignalList(); - signalList.forEach(signal -> { -// if (!signal.isNormalOpen()) { -// return; -// } - Route lockedRoute = signal.getLockedRoute(); - if (Objects.isNull(lockedRoute)) { - return; - } - Section section = signal.getSection(); - Section nextSection = section.getNextRunningSectionOf(signal.isRight()); - boolean across = false; - for (VirtualRealityTrain train : onlineTrainList) { - List
atpSectionList = trainAtpSectionMap.get(train.getGroupNumber()); - List
physicalList = this.convert2PhysicalSectionList(atpSectionList); - if (physicalList.contains(section) && physicalList.contains(nextSection)) { - across = true; - break; - } - } - String signalCode = signal.getCode(); - if (across) { - signalAcrossSet.add(signalCode); - } - }); - this.ciApiService.handleSignalAcrossMessage(simulation, signalAcrossSet); - } - /** * 收集列车接近信号机消息并发送给CI */ 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 e9a31f99c..9332941bf 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 @@ -56,8 +56,7 @@ public class GroundAtpApiServiceImpl implements GroundAtpApiService { // 更新保存新的占用 repository.addOrUpdateTrainOccupyAtpSectionList(train.getGroupNumber(), trainAtpSectionList); } - // 信号机接近信息、停稳信息、跨压信息通知 - this.atpSectionService.collectAndSendSignalAcrossMessage2CI(simulation, onlineTrainList, trainAtpSectionMap); + // 信号机接近信息、停稳信息通知 Map nctApproachSignalMap = this.atpSectionService.collectAndSendSignalApproachMessage2CI(simulation, onlineTrainList, trainAtpSectionMap); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/MaService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/MaService.java new file mode 100644 index 000000000..6626abd56 --- /dev/null +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/MaService.java @@ -0,0 +1,428 @@ +package club.joylink.rtss.simulation.cbtc.ATP.ground; + +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.*; +import club.joylink.rtss.simulation.cbtc.data.support.SectionPosition; +import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain; +import club.joylink.rtss.simulation.cbtc.onboard.ATO.SpeedCurve; +import club.joylink.rtss.simulation.cbtc.onboard.ATP.OnboardAtpApiService; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 移动授权计算服务 + */ +@Component +public class MaService { + + @Autowired + private OnboardAtpApiService onboardAtpApiService; + + @Getter + public static class Ma { + public static final int Safety_Margin = 10; // 安全余量,单位m + public static final int EB_Trigger = 20; // 紧急制动触发点偏移量,单位m + public static final int Safety_Distance = 30; // 安全距离, 单位m + + VirtualRealityTrain train; // 列车 + MapNamedElement device; // 终端设备 + MaType type; // 类型 + SectionPosition eoaPosition; // 授权终端位置 + + float distanceToEoa; // 距离 + /** + * 紧急制动触发曲线 + */ + private SpeedCurve ebTriggerCurve; + /** + * ATO停车曲线 + */ + private SpeedCurve atoStopCurve; + /** + * 移动的累计距离 + */ + private float moveLen; + + public Ma(VirtualRealityTrain train, MapNamedElement device, MaType type) { + this.train = train; + this.device = device; + this.type = type; + this.getEoaPosition(); + this.calculateDistanceToEoa(); + } + + public void addMoveLen(float len) { + this.moveLen += len; + } + + public void setEbTriggerCurve(SpeedCurve ebTriggerCurve) { + this.ebTriggerCurve = ebTriggerCurve; + } + + public void setAtoStopCurve(SpeedCurve atoStopCurve) { + this.atoStopCurve = atoStopCurve; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Ma ma = (Ma) o; + return Objects.equals(eoaPosition, ma.eoaPosition); + } + + @Override + public int hashCode() { + return Objects.hash(eoaPosition); + } + + public String debugStr() { + return String.format("列车[%s]位置:%s, 移动授权为:{类型: %s, 设备: %s, EOA: %s, distance: %s}", + this.train.getGroupNumber(), + this.train.getHeadPosition().toString(), + this.type, + this.device.debugStr(), + this.eoaPosition.toString(), + this.distanceToEoa); + } + + public float calculateDistanceToEoa() { + SectionPosition headPosition = train.getHeadPosition(); + boolean right = train.isRight(); + Float distance = CalculateService.calculateDistance(headPosition, this.eoaPosition, right); + if (distance == null) { + distance = CalculateService.calculateDistance(headPosition, this.eoaPosition, !right); + if (distance > 0) { + distance = -distance; + } + } + this.distanceToEoa = distance; + return distanceToEoa; + } + + public float calculateDistanceOfEbTriggerEnd() { + if (this.type.equals(MaType.Limit_Signal_Without_Overlap)) { + Signal signal = (Signal) this.device; + if (signal.getSection().isJustTurnBackTrack()) { + return this.calculateDistanceToEoa(); + } + } + return this.calculateDistanceToEoa() - EB_Trigger; + } + + public float calculateDistanceOfAtoEnd() { + if (this.type.equals(MaType.Limit_Signal_With_Overlap) || + this.type.equals(MaType.Limit_Signal_Without_Overlap)) { + Signal signal = (Signal) this.device; + SectionPosition sectionPosition = new SectionPosition(signal.getSection(), signal.getOffset()); + boolean right = this.train.isRight(); + SectionPosition end = CalculateService.calculateNextPositionByStartAndLen(sectionPosition, !right, 2); + return CalculateService.calculateDistance(this.train.getHeadPosition(), end, right); + } else { + return this.calculateDistanceOfEbTriggerEnd() - Safety_Distance; + } + } + + public SectionPosition getEoaPosition() { + SectionPosition eoa; + boolean right = this.train.isRight(); + switch (this.type) { + case Front_Train: { + VirtualRealityTrain frontTrain = (VirtualRealityTrain) this.device; + if (frontTrain.isRight() == right) { + eoa = frontTrain.getTailPosition(); + } else { + eoa = frontTrain.getHeadPosition(); + } + eoa = CalculateService.calculateNextPositionByStartAndLen(eoa, !right, 10 + frontTrain.getSpeed() * 2); + break; + } + case Axle_Occupy_Section:{ + Section aoSection = (Section) this.device; + eoa = this.buildEoaOfAxleOccupySection(aoSection, right); + break; + } + case Limit_Signal_Without_Overlap:{ + Signal signal = (Signal) this.device; + Section section = signal.getSection(); + eoa = new SectionPosition(section, right ? section.getLen() : 0); + break; + } + case Limit_Signal_With_Overlap:{ + Signal signal = (Signal) this.device; + eoa = this.buildEoaOfLimitSignalWithOverlap(signal, right); + break; + } + case ZC_Boundary:{ + Section section = (Section) this.device; + eoa = new SectionPosition(section, right ? section.getLen() : 0); + break; + } + case Closed_Section: + case Fault_Route:{ + Section section = (Section) this.device; + eoa = new SectionPosition(section, right ? 0 : section.getLen()); + break; + } + case Stand:{ + Section section = (Section) this.device; + float offset = section.getStopPointByDirection(right); + offset = right ? offset - train.getLen() - 20 : offset + train.getLen() + 20; + eoa = CalculateService.getAvailableSectionPositionOf(new SectionPosition(section, offset)); + break; + } + default: + throw new IllegalStateException("未知的移动授权类型: " + this.type); + } + this.eoaPosition = eoa; + return eoa; + } + + private SectionPosition buildEoaOfAxleOccupySection(Section aoSection, boolean right) { + SectionPosition headPosition = this.train.getHeadPosition(); + if (aoSection.equals(headPosition.getSection())) { // 车头区段 + return new SectionPosition(aoSection, right ? 0 : aoSection.getLen()); + } + // 非车头区段,取非通信车占用区段与列车运行方向反向区段 + // 如果此区段是道岔区段且不是车头区段,则从列车车头区段往前找,直到第一个和此道岔区段为同一道岔计轴的道岔区段 + Section front = aoSection.getSectionOf(!right); + if (front.isSwitchTrack() && !front.equals(headPosition.getSection())) { + Section section = headPosition.getSection(); + for (int i = 0; i < 20; i++) { + Section fs = section.getNextRunningSectionOf(right); + if (fs.isSameAxle(front.getCode())) { + front = fs; + break; + } + } + } + return new SectionPosition(front, right ? 0 : front.getLen()); + } + + private SectionPosition buildEoaOfLimitSignalWithOverlap(Signal signal, boolean right) { + List overlapList = signal.getOverlapList(); + Section section = this.queryOverlapLockedEndSection(overlapList); + if (section == null) { + section = signal.getSection(); + } + return new SectionPosition(section, right ? section.getLen() : 0); + } + + private Section queryOverlapLockedEndSection(List overlapList) { + for (RouteOverlap routeOverlap : overlapList) { + if (routeOverlap.isLock()) { + List pathList = routeOverlap.getPathList(); + for (SectionPath sectionPath : pathList) { + List
sectionList = sectionPath.getSectionList(); + if (sectionList.get(sectionList.size() - 1).isOverlapLock()) { + return sectionList.get(sectionList.size() - 1); + } + } + } + } + return null; + } + } + + /** + * 移动授权类型 + */ + public enum MaType { + /** 前方列车 */ + Front_Train, + /** 计轴占用区段 */ + Axle_Occupy_Section, + /** 没有延续保护的限制信号机 */ + Limit_Signal_Without_Overlap, + /** 有延续保护的限制信号机 */ + Limit_Signal_With_Overlap, + /** ZC边界 */ + ZC_Boundary, + /** 关闭的区段 */ + Closed_Section, + /** 进路联锁条件不再完备 */ + Fault_Route, + /** 站台问题(屏蔽门或紧急停车) */ + Stand, + } + + public void calculateMaOfCtcTrains(Simulation simulation) { + SimulationDataRepository repository = simulation.getRepository(); + Map usedTrainMap = repository.getUsedTrainMap(); + usedTrainMap.values().forEach(vrTrain -> vrTrain.calculateTailPosition()); + for (VirtualRealityTrain vrTrain : usedTrainMap.values()) { + if (vrTrain.isRMMode() || vrTrain.isNRMMode()) { + continue; + } + if (vrTrain.isCommunication()) { + Ma ma = this.calculateCtcMa(simulation, vrTrain, usedTrainMap); + if (ma.distanceToEoa <= 0) { + System.out.println(ma.debugStr()); + } + this.onboardAtpApiService.updateCtcMa(simulation, ma); + } + } + } + + public Ma calculateCtcMa(Simulation simulation, VirtualRealityTrain train, Map trainMap) { + SectionPosition headPosition = train.getHeadPosition(); + SectionPosition tailPosition = train.getTailPosition(); + boolean right = train.isRight(); + Ma ma = null; + boolean front = false; + Section section = tailPosition.getSection(); + for (int i = 0; i < 20 && section != null; i++) { + if (section.equals(headPosition.getSection())) { + front = true; + } + Route route = section.getRoute(); + if (route != null) { + if (this.isSwitchFaultRoute(route, section)) { + // 道岔故障进路,直接构建返回 + ma = new Ma(train, section, MaType.Fault_Route); + break; + } + } + if (section.isClosed()) { + // 关闭的区段 + ma = this.checkAndUpdateMa(ma, new Ma(train, section, MaType.Closed_Section)); + } + if (this.isFaultStand(section)) { + // 故障/紧急停车站台 + ma = this.checkAndUpdateMa(ma, new Ma(train, section, MaType.Stand)); + } + Section next = section.getNextRunningSectionOf(right); + if (front) { // 车头前方 + Signal signal = section.getSignalOf(right); + if (signal != null && !signal.isNormalOpen()) { + // 限制信号机 + if (signal.isOverlapLock()) { + ma = this.checkAndUpdateMa(ma, new Ma(train, signal, MaType.Limit_Signal_With_Overlap)); + } else { + ma = this.checkAndUpdateMa(ma, new Ma(train, signal, MaType.Limit_Signal_Without_Overlap)); + } + } + if (section.hasNctOccupy()) { + // 计轴占用区段 + ma = this.checkAndUpdateMa(ma, new Ma(train, section, MaType.Axle_Occupy_Section)); + } else if (section.isOnlyCtcOccupy()) { + // 前方列车 + Ma ma2 = this.queryFrontTrain(train, section, trainMap); + ma = this.checkAndUpdateMa(ma, ma2); + } + // ZC(尽头区段)边界 + if (!section.anyZcWorking() && + (headPosition.getSection().equals(section) || + !headPosition.isAheadOf(new SectionPosition(section, right ? section.getLen() : 0), right))) { + ma = this.checkAndUpdateMa(ma, new Ma(train, section, MaType.ZC_Boundary)); + } + if (next == null || (!next.anyZcWorking())) { + ma = this.checkAndUpdateMa(ma, new Ma(train, section, MaType.ZC_Boundary)); + } + } + section = next; + if (ma != null) { + break; + } + } + return ma; + } + + private boolean isFaultStand(Section section) { + if (section.isNormalStandTrack()) { + for (Stand stand : section.getStandList()) { + if (stand.isEmergencyClosed()) { + return true; + } + PSD psd = stand.getPsd(); + if (psd != null && (!psd.isCloseAndLock() && !psd.isInterlockRelease()) && !stand.isTrainParking()) { + return true; + } + } + } + return false; + } + + private boolean isSwitchFaultRoute(Route route, Section section) { + boolean switchFault = false; + List
sectionList = route.getSectionList(); + Set handledSwitchSet = new HashSet<>(); + boolean flag = false; + for (Section routeSection : sectionList) { + if (routeSection.equals(section)) { + flag = true; + } + if (flag) { + if (routeSection.isSwitchTrack()) { + if (handledSwitchSet.contains(routeSection.getRelSwitch().getCode())) { + continue; + } + SwitchElement switchElement = route.getRouteSwitchElement(routeSection.getRelSwitch()); + Switch aSwitch = switchElement.getASwitch(); + handledSwitchSet.add(aSwitch.getCode()); + if (route.equals(aSwitch.getRoute())) { + if (aSwitch.isLoss()) { + switchFault = true; + break; + } else { + RouteFls fls = route.getRouteFlsOfSwitch(aSwitch); + if (fls != null) { + List level1List = fls.getLevel1List(); + for (RouteFls.FlsElement flsElement : level1List) { + SwitchElement pSwitch = flsElement.getPSwitch(); + if (pSwitch != null && pSwitch.getASwitch().isLoss()) { + switchFault = true; + break; + } + } + } + } + } + if (switchFault) { + break; + } + } + } + } + return switchFault; + } + + private Ma checkAndUpdateMa(Ma ma, Ma ma2) { + if (ma == null) { + return ma2; + } else if(ma2 != null) { + if (ma.distanceToEoa > ma2.distanceToEoa) { + return ma2; + } + } + return ma; + } + + private Ma queryFrontTrain(VirtualRealityTrain train, Section section, + Map trainMap) { + List trainList = trainMap.values().stream() + .filter(vrTrain -> !vrTrain.equals(train) && + (vrTrain.getHeadPosition().getSection().equals(section) || + vrTrain.getTailPosition().getSection().equals(section))) + .collect(Collectors.toList()); + Ma ma = null; + if (trainList.isEmpty()) { + return null; + } else { + for (VirtualRealityTrain vrTrain : trainList) { + Ma ma1 = new Ma(train, vrTrain, MaType.Front_Train); + if (ma1.getDistanceToEoa() > 0) { + ma = this.checkAndUpdateMa(ma, ma1); + } + } + } + return ma; + } + +} diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/ZCLogicLoop.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/ZCLogicLoop.java index 07804d62f..06f6bca74 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/ZCLogicLoop.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATP/ground/ZCLogicLoop.java @@ -5,8 +5,6 @@ import club.joylink.rtss.simulation.cbtc.ATS.AtsApiService; import club.joylink.rtss.simulation.cbtc.CI.service.RouteService; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.constant.RunLevel; -import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants; -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.*; @@ -39,6 +37,9 @@ public class ZCLogicLoop { @Autowired private AtsApiService atsApiService; + @Autowired + private MaService maService; + @Autowired private RouteService routeService; @@ -680,6 +681,7 @@ public class ZCLogicLoop { } public void addJobs(Simulation simulation) { - simulation.addJob(SimulationModule.ZC.name(), () -> this.run(simulation), SimulationConstants.ZC_LOOP_RATE); +// simulation.addJob(SimulationModule.ZC.name(), () -> this.run(simulation), SimulationConstants.ZC_LOOP_RATE); + simulation.addJob("MaCal", () -> this.maService.calculateMaOfCtcTrains(simulation), 1000); } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSMessageCollectAndDispatcher.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSMessageCollectAndDispatcher.java index 55500e5a0..1920cf138 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSMessageCollectAndDispatcher.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/ATSMessageCollectAndDispatcher.java @@ -349,7 +349,7 @@ public class ATSMessageCollectAndDispatcher { } public void addJobs(Simulation simulation) { - simulation.addFixedRateJob(SimulationModule.SYNC_TIME.name(), () -> this.syncTime(simulation), SimulationConstants.SYNC_TIME_RATE); + simulation.addJob(SimulationModule.SYNC_TIME.name(), () -> this.syncTime(simulation), SimulationConstants.SYNC_TIME_RATE); simulation.addFixedRateJob(SimulationModule.MESSAGE.name(), () -> this.run(simulation), SimulationConstants.SEND_CLIENT_RATE); } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainLoadService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainLoadService.java index 33efda296..f718bc480 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainLoadService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainLoadService.java @@ -2,6 +2,7 @@ package club.joylink.rtss.simulation.cbtc.ATS.service; import club.joylink.rtss.exception.BusinessExceptionAssertEnum; import club.joylink.rtss.simulation.cbtc.ATP.ground.GroundAtpApiService; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.ATP.ground.ZCLogicLoop; import club.joylink.rtss.simulation.cbtc.CI.CILogicLoop; import club.joylink.rtss.simulation.cbtc.CI.service.RouteService; @@ -65,6 +66,8 @@ public class AtsTrainLoadService { @Autowired private ZCLogicLoop zcLogicLoop; + @Autowired + private MaService maService; @Autowired private VRTrainRunningService vrTrainRunningService; @@ -405,6 +408,7 @@ public class AtsTrainLoadService { // 列车上线并构建ATS监控列车信息 this.trainOnlineAndBuildSupervise(simulation, loadedList); // 计算移动授权 +// this.maService.calculateMaOfCtcTrains(simulation); this.zcLogicLoop.run(simulation); // // 更新列车速度 // this.updateTrainSpeed(simulation); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainMonitorService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainMonitorService.java index 11fb5a61a..9982e148a 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainMonitorService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/AtsTrainMonitorService.java @@ -109,9 +109,7 @@ public class AtsTrainMonitorService { if (dlen < SimulationConstants.PARK_POINT_MAX_OFFSET) { // 停车点停车 this.trainParking(simulation, trainInfo, headSection.getStation(), headSection); - if (headSection.isNormalStandTrack()) { - this.onboardAtpApiService.parkingInPlace(simulation, vrTrain); - } + this.onboardAtpApiService.parkingInPlace(simulation, vrTrain, headSection); } } } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsTriggerRouteService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsTriggerRouteService.java index a5604d824..c51e76b4a 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsTriggerRouteService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/ATS/service/ars/AtsTriggerRouteService.java @@ -147,6 +147,10 @@ public class AtsTriggerRouteService { if (!trainInfo.isPlanTrain()) { return false; } + // 计划车没有下一个计划,可能是加载折返情况 + if (trainInfo.getPlanStandTrack() == null) { + return true; + } SimulationDataRepository repository = simulation.getRepository(); TripPlan tripPlan = repository.getTripPlan(trainInfo.getServiceNumber(), trainInfo.getTripNumber()); List planList = tripPlan.getPlanList(); @@ -155,12 +159,6 @@ public class AtsTriggerRouteService { StationPlan stationPlan = planList.get(i - 1); StationPlan next = planList.get(i); List currentPaths = repository.getRoutePaths(stationPlan.getSection(), next.getSection()); - if (i == 1 && stationPlan.getSection().getCode().equals(trainInfo.getPlanStandTrack())) { - if (currentPaths.get(0).isRight() != route.isRight()) { - return true; - } - break; - } boolean opposite = false; if (next.getSection().getCode().equals(trainInfo.getPlanStandTrack())) { if (i + 1 < size) { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiService.java index 024918ded..a703c8a58 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiService.java @@ -9,7 +9,6 @@ import club.joylink.rtss.simulation.cbtc.data.support.SignalApproachMessage; import club.joylink.rtss.simulation.cbtc.data.support.TrainStopMessage; import java.util.List; -import java.util.Set; /** * CI子系统接口 @@ -275,13 +274,6 @@ public interface CiApiService { */ void handleSignalApproachMessage(Simulation simulation, List approachMessageList); - /** - * 接收处理信号机跨压信息 - * @param simulation - * @param acrossMessageList - */ - void handleSignalAcrossMessage(Simulation simulation, Set acrossMessageList); - /** * 接收处理列车停稳信息 * @param simulation diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiServiceImpl.java index a7825cf18..73d8b40dc 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/CiApiServiceImpl.java @@ -7,7 +7,6 @@ import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository; import club.joylink.rtss.simulation.cbtc.data.map.*; import club.joylink.rtss.simulation.cbtc.data.support.SignalApproachMessage; import club.joylink.rtss.simulation.cbtc.data.support.TrainStopMessage; -import club.joylink.rtss.simulation.cbtc.data.vo.TrainInfo; import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealitySectionAxleCounter; import club.joylink.rtss.simulation.cbtc.exception.SimulationException; import club.joylink.rtss.simulation.cbtc.exception.SimulationExceptionType; @@ -21,7 +20,6 @@ import java.time.LocalTime; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; /** @@ -394,20 +392,6 @@ public class CiApiServiceImpl implements CiApiService { }); } - @Override - public void handleSignalAcrossMessage(Simulation simulation, - Set acrossMessageList) { - acrossMessageList.forEach(signalCode -> { - Signal signal = simulation.getRepository().getByCode(signalCode, Signal.class); -// if (signal.isNormalOpen()) { - Route lockedRoute = signal.getLockedRoute(); - if (Objects.nonNull(lockedRoute)) { - this.routeService.normalUnlockStart(simulation, lockedRoute); - } -// } - }); - } - @Override public void handleTrainStopMessage(Simulation simulation, TrainStopMessage stopMessage) { this.routeService.handleTrainStopMessage(simulation, stopMessage); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/service/RouteService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/service/RouteService.java index 6bc078e6e..9b2bf6bcf 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/CI/service/RouteService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/CI/service/RouteService.java @@ -1336,6 +1336,12 @@ public class RouteService { } } if (route.isOpen()) { + // 判断是否列车进入正常解锁 + Section firstLogicSection = route.getFirstLogicSection(); + if (firstLogicSection.isCtOccupied() || firstLogicSection.isNctOccupied()) { + this.normalUnlockStart(simulation, route); + return; + } boolean interlocked = this.isInterlocked(route); if (!interlocked) { // 进路信号开放,联锁逻辑不满足,需关闭信号 diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/build/InterlockBuilder2.java b/src/main/java/club/joylink/rtss/simulation/cbtc/build/InterlockBuilder2.java index cc9560eee..269accf67 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/build/InterlockBuilder2.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/build/InterlockBuilder2.java @@ -52,8 +52,10 @@ public class InterlockBuilder2 { Signal signal = unlockSection.getSignalOf(mapOverlapVO.isRight()); if (Objects.isNull(signal)) { errMsgList.add(String.format("延续保护[%s(%s)]数据异常,无法找到防护起始信号机", routeOverlap.getName(), routeOverlap.getCode())); + } else { + routeOverlap.setSignal(signal); + signal.getOverlapList().add(routeOverlap); } - routeOverlap.setSignal(signal); } if (Objects.isNull(mapOverlapVO.getUnlockTime()) || mapOverlapVO.getUnlockTime() <= 0) { errMsgList.add(String.format("延续保护[%s(%s)]的解锁时间未设置或小于0", routeOverlap.getName(), routeOverlap.getCode())); 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 2a3597b9c..beea4a79e 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 @@ -522,16 +522,35 @@ public class Section extends MayOutOfOrderDevice { } /** - * 有非通信车占用 + * 是否有非通信车占用 * * @return */ public boolean hasNctOccupy() { + boolean nct = this.nctOccupied && !this.ctOccupied; if (!CollectionUtils.isEmpty(this.logicList)) { - return this.logicList.stream().anyMatch(section -> !section.isCtOccupied() && section.isNctOccupied()); - } else { - return !this.isCtOccupied() && this.isNctOccupied(); + nct = false; + for (Section logic : this.logicList) { + if (!logic.isCtOccupied() && logic.isNctOccupied()) { + nct = true; + break; + } + } } + return nct; + } + + public boolean isOnlyCtcOccupy() { + boolean ctOccupied = this.ctOccupied; + if (!CollectionUtils.isEmpty(this.logicList)) { + for (Section logic : this.logicList) { + if (logic.isCtOccupied()) { + ctOccupied = true; + break; + } + } + } + return ctOccupied; } public List
getAtpSectionListBy(float offset1, float offset2) { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Signal.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Signal.java index 50ee9a1b7..8d6f13e7a 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Signal.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/map/Signal.java @@ -24,6 +24,7 @@ public class Signal extends MayOutOfOrderDevice { super(code, name, DeviceType.SIGNAL); this.redOpen = true; this.routeList = new ArrayList<>(); + this.overlapList = new ArrayList<>(); } // ------------------固有属性/关联关系--------------------- @@ -119,6 +120,10 @@ public class Signal extends MayOutOfOrderDevice { * 进路列表 */ private List routeList; + /** + * 延续保护列表 + */ + private List overlapList; /** * 接近锁闭解锁时间 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 a468accbd..d708658c3 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 @@ -50,8 +50,8 @@ public class SectionPosition { @Override public String toString() { return "{" + - String.format("section:[%s(%s)]", section.getName(), section.getCode()) + - String.format(", offset:[%s]", offset) + + String.format("%s(%s)", section.getName(), section.getCode()) + + String.format(", %s", offset) + '}'; } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/data/vr/VirtualRealityTrain.java b/src/main/java/club/joylink/rtss/simulation/cbtc/data/vr/VirtualRealityTrain.java index 5ca17193a..90e4f42f1 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/data/vr/VirtualRealityTrain.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/data/vr/VirtualRealityTrain.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.data.vr; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.constant.*; import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.map.*; @@ -31,6 +32,11 @@ public class VirtualRealityTrain extends VirtualRealityDevice { */ public static final float Emergency_Acceleration = -1.5f; + /** + * 紧急制动触发曲线计算加速度(矢量) + */ + public static final float Emergency_Trigger_Acceleration = -1.1f; + /** * 非紧急制动状态下最大制动加速度(标量) */ @@ -176,6 +182,10 @@ public class VirtualRealityTrain extends VirtualRealityDevice { * 车头位置 */ private SectionPosition headPosition; + /** + * 车尾位置 + */ + private SectionPosition tailPosition; /** * 是否向右行驶 @@ -191,6 +201,10 @@ public class VirtualRealityTrain extends VirtualRealityDevice { * 移动授权 */ private MovementAuthority ma; + /** + * 移动授权 + */ + private MaService.Ma ma2; /** * 未收到CBTC级别ma的时长,单位毫秒 @@ -461,6 +475,7 @@ public class VirtualRealityTrain extends VirtualRealityDevice { super(code, groupNumber, DeviceType.TRAIN); this.groupNumber = groupNumber; this.gear = ControlGear.Drive; + this.communication = true; this.atpOn = true; this.door1 = new Door("1"); this.door2 = new Door("2"); @@ -489,6 +504,7 @@ public class VirtualRealityTrain extends VirtualRealityDevice { this.terminalStation = null; this.headPosition = null; this.robotTargetPosition = null; + this.ma2 = null; this.ma = null; this.cbtcMaMissDuration = 0; this.itcMaMissDuration = 0; @@ -613,9 +629,16 @@ public class VirtualRealityTrain extends VirtualRealityDevice { this.autoOpenATO = this.atoOn; } + public void setTarget(Section target) { + this.target = target; +// if (this.ma2 != null) { +// SpeedCurve.calculateAtoStopCurveAndUpdate(this, this.ma2); +// } + } + public synchronized void updateNextStationPlan(Station nextStation, Section targetSection, boolean nextParking) { this.nextStation = nextStation; - this.target = targetSection; + this.setTarget(targetSection); this.nextParking = nextParking; // log.info(String.format("列车[%s]更新下一站[%s] 轨道[%s], [%s]", @@ -625,7 +648,7 @@ public class VirtualRealityTrain extends VirtualRealityDevice { } public void startTurnBack(Section tbSection) { - this.target = tbSection; + this.setTarget(tbSection); this.dtro = true; this.nextStation = null; this.terminalStation = null; @@ -688,6 +711,7 @@ public class VirtualRealityTrain extends VirtualRealityDevice { public void clearMa() { this.ma = null; + this.ma2 = null; } /** @@ -715,7 +739,8 @@ public class VirtualRealityTrain extends VirtualRealityDevice { } public SectionPosition calculateTailPosition() { - return CalculateService.calculateNextPositionByStartAndLen(this.headPosition, !right, this.getLen()); + this.tailPosition = CalculateService.calculateNextPositionByStartAndLen(this.headPosition, !right, this.getLen()); + return this.tailPosition; } /** @@ -986,9 +1011,12 @@ public class VirtualRealityTrain extends VirtualRealityDevice { } } - public synchronized void parkingAt() { -// this.parkRemainTime = 30 * 1000; - this.standParkedTrainActivity = StandParkedTrainActivity.PARK; + public synchronized void parkingAt(Section headSection) { + if (headSection.isNormalStandTrack()) { + this.standParkedTrainActivity = StandParkedTrainActivity.PARK; + } else { + this.standParkedTrainActivity = StandParkedTrainActivity.START; + } } public boolean isParkingAt() { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/SpeedCurve.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/SpeedCurve.java index ac4a80e5c..0b9b61b88 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/SpeedCurve.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/SpeedCurve.java @@ -1,5 +1,7 @@ package club.joylink.rtss.simulation.cbtc.onboard.ATO; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; +import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.map.Section; import club.joylink.rtss.simulation.cbtc.data.storage.ato.StorageSpeedCalculator; @@ -14,6 +16,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -44,6 +47,51 @@ public class SpeedCurve { this.speedCalculators = speedCalculators; } + public static void calculateAndUpdate(Simulation simulation, VirtualRealityTrain train, MaService.Ma ma) { + if (ma.equals(train.getMa2())) { + return; + } +// log.debug(String.format("系统时间:[%s],更新——%s", +// simulation.getSystemTime(), +// ma.debugStr())); + train.setMa2(ma); + SpeedCurve.calculateEbTriggerCurveAndUpdate(train, ma); + SpeedCurve.calculateAtoStopCurveAndUpdate(train, ma); + } + + public static void calculateEbTriggerCurveAndUpdate(VirtualRealityTrain train, MaService.Ma ma) { + float distance = ma.calculateDistanceOfEbTriggerEnd(); + SpeedCurve ebTriggerCurve = SpeedCurve.generateProtectSpeedCurve(distance, train.getAtpSpeedMax()); + ma.setEbTriggerCurve(ebTriggerCurve); + } + + public static SpeedCurve calculateAtoStopCurveAndUpdate(VirtualRealityTrain train, MaService.Ma ma) { + SectionPosition headPosition = train.getHeadPosition(); + SectionPosition tailPosition = train.getTailPosition(); + boolean right = train.isRight(); + float stopDistance = ma.calculateDistanceOfAtoEnd(); + float speedMax = Math.min(train.getAtoSpeedMax(), train.getSpeedLimit() * 0.9f); + SpeedCurve atoStopCurve; + if (train.isNextParking() || train.isHold()) { // 列车下一站停车 + Section target = train.getTarget(); + if (target != null) { + float stopPoint = target.getStopPointByDirection(right); + SectionPosition stopPosition = new SectionPosition(target, stopPoint); + Float stopPointDistance = CalculateService.calculateDistance(headPosition, stopPosition, right); + float ebTriggerDistance = ma.calculateDistanceOfEbTriggerEnd(); + if (stopPointDistance != null && stopPointDistance.floatValue() <= ebTriggerDistance) { + // 列车停车位置距离小于eb触发距离 + stopDistance = stopPointDistance; + } + } + } + atoStopCurve = SpeedCurve + .buildTargetSpeedCurve(headPosition, tailPosition, right, + stopDistance, train.getSpeed(), speedMax); +// ma.setAtoStopCurve(atoStopCurve); + return atoStopCurve; + } + @Override public String toString() { return "SpeedCurve{" + @@ -127,12 +175,12 @@ public class SpeedCurve { // 先不考虑区段临时限速 // 减速段 // 减速段总距离 - float dvDistance = (float) (Math.pow(speedMax, 2) / (2 * VirtualRealityTrain.Emergency_Acceleration)); + float dvDistance = (float) (Math.pow(speedMax, 2) / (2 * VirtualRealityTrain.Emergency_Trigger_Acceleration)); if (dvDistance >= safeDistance) { // 只有减速段 // 计算减速段开始位置防护初速度 - float v0 = (float) Math.sqrt(2 * VirtualRealityTrain.Emergency_Acceleration * safeDistance); + float v0 = (float) Math.sqrt(2 * VirtualRealityTrain.Emergency_Trigger_Acceleration * safeDistance); speedCurve.addCalculator(new SpeedCalculator(0, safeDistance, - v0, 0, VirtualRealityTrain.Emergency_Acceleration, + v0, 0, VirtualRealityTrain.Emergency_Trigger_Acceleration, VSFormula.UNIFORM_DECELERATION)); } else { // 有匀速,有减速 // 匀速-减速分界位置 @@ -141,12 +189,58 @@ public class SpeedCurve { speedMax, speedMax, 0, VSFormula.UNIFORM)); speedCurve.addCalculator(new SpeedCalculator(point, safeDistance, - speedMax, 0, VirtualRealityTrain.Emergency_Acceleration, + speedMax, 0, VirtualRealityTrain.Emergency_Trigger_Acceleration, VSFormula.UNIFORM_DECELERATION)); } return speedCurve; } + public static SpeedCurve buildAtoCurve(SectionPosition headPosition, boolean right, + float v0, float speedMax, float totalDistance) { + if (totalDistance <= 0) { + return SpeedCurve.ZERO; + } + SpeedCurve speedCurve = new SpeedCurve(totalDistance); + List limitSpeedList = new ArrayList<>(); + float start = 0; + float end = 0; + float limitSpeed = speedMax; + Section logicSection = headPosition.getLogicSection(); + Section base = headPosition.getSection(); + if (logicSection.equals(headPosition.getSection())) { // 非逻辑区段 + end += right ? logicSection.getLen() - headPosition.getOffset() : headPosition.getOffset(); + } else { // 逻辑区段 + end += right ? logicSection.getMaxOffset() - headPosition.getOffset() : headPosition.getOffset() - logicSection.getMinOffset(); + } + if (base.getNeedLimitSpeed() != null && base.getNeedLimitSpeed().floatValue() < limitSpeed) { + limitSpeed = base.getNeedLimitSpeed(); + } + if (end >= totalDistance) { + end = totalDistance; + + } + for (int i = 0; i < 15 && base != null; i++) { + Float needLimitSpeed = base.getNeedLimitSpeed(); + if (needLimitSpeed != null) { + end += base.getLen(); + } else if (!CollectionUtils.isEmpty(base.getLogicList())) { + List
logicList; + if (!right) { + logicList = new ArrayList<>(base.getLogicList()); + Collections.reverse(logicList); + } else { + logicList = base.getLogicList(); + } + for (Section logic : logicList) { + if (headPosition.getLogicSection().equals(logic)) { + + } + } + } + } + return speedCurve; + } + public static SpeedCurve buildTargetSpeedCurve(SectionPosition headPosition, SectionPosition tailPosition, boolean right, float totalLen, float v0, float recommendedSpeedMax) { diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/service/ATOService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/service/ATOService.java index 867bf2542..e94d5aa06 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/service/ATOService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATO/service/ATOService.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.onboard.ATO.service; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.CI.CiApiService; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.communication.vo.TrainDoorSwitch; @@ -71,6 +72,37 @@ public class ATOService { } } + public void ato2(VirtualRealityTrain train) { + if (!train.isPowerOn() || !train.isAtoOn()) { + return; + } + //下令停车 + if (train.isOrderStop()) { + this.doBreakMax(train); + return; + } + // 是否EB + if (train.isEB()) { // EB中,不控制 + return; + } + // 是否停车制动 + if (train.isBreaking()) { // 站台持续停车制动中,输出持续制动 + doBreakMax(train); + return; + } + if (train.getTarget() == null) + return; + MaService.Ma ma = train.getMa2(); + if (ma == null) { + return; + } + SpeedCurve speedCurve = SpeedCurve.calculateAtoStopCurveAndUpdate(train, ma); + // 更新目标距离和建议速度 + train.setTargetDistance(speedCurve.getTotalDistance()); + train.setAtoSpeed(speedCurve.getSpeedOf(speedCurve.getTotalDistance())); + this.doControlBySpeedCurve(train, speedCurve, speedCurve.getTotalDistance()); + } + /** * ATO运算逻辑 */ diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPLogicLoop.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPLogicLoop.java index 9e7d2ca5e..ddbfc82ff 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPLogicLoop.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPLogicLoop.java @@ -1,15 +1,15 @@ package club.joylink.rtss.simulation.cbtc.onboard.ATP; import club.joylink.rtss.simulation.cbtc.ATP.ground.GroundAtpApiService; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.ATS.AtsApiService; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.constant.*; import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.map.MapConfig; import club.joylink.rtss.simulation.cbtc.data.map.Section; +import club.joylink.rtss.simulation.cbtc.data.map.Signal; 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.MovementAuthority; import club.joylink.rtss.simulation.cbtc.data.support.SectionPosition; import club.joylink.rtss.simulation.cbtc.data.support.TrainStopMessage; import club.joylink.rtss.simulation.cbtc.data.vr.StandParkedTrainActivity; @@ -130,25 +130,10 @@ public class ATPLogicLoop { // 施加常规制动,防止倒溜 this.atoService.openBreaking(train); } - if (!train.isParkingAt()) { - this.checkParkingAndSend2Ats(simulation, train); - } else { // 列车站台停靠 + if (train.isParkingAt()) {// 列车站台停靠 this.handleStandParkedTrain(simulation, train); } -// if (train.isEB()) { // 列车EB -// if (train.isAtpOn()) { // 尝试自动缓解信号EB -// if (train.getLeverPosition() == 0 && train.getMa() != null) { -// Float distance = CalculateService.calculateDistance(headPosition, train.getMa().getEnd().getEndPosition(), right); -// if (distance != null && distance > 100) { -// atpService.cancelSignalEB(train); -// applicationContext.publishEvent(new SimulationATPAutoCancelEBEvent(this, simulation, train)); -// log.info(String.format("列车[%s]移动授权距离超过100m,EB自动缓解", train.getGroupNumber())); -// } -// } -// } -// } - /* 缓解EB检查 */ if (train.isEB()) { if (train.isRMMode() && train.getControlLeaver() <= 0) { //停车、RM模式、操纵杆非牵引位 @@ -183,65 +168,81 @@ public class ATPLogicLoop { } // 列车ATO自动驾驶逻辑运行 - this.atoService.ATO(train); +// this.atoService.ATO(train); + this.atoService.ato2(train); } private void calculateSpeedCurve(Simulation simulation, VirtualRealityTrain train) { if (!train.isAtpOn()) return; DriveMode driveMode = train.getDriveMode(); - float atpSpeed = 0; - float atoSpeed = 0; switch (driveMode) { case RM: { - atpSpeed = simulation.getRepository().getConfig().getRmAtpSpeed(); - atoSpeed = train.getAtpSpeed() - 5 / 3.6f; + float atpSpeed = simulation.getRepository().getConfig().getRmAtpSpeed(); + float atoSpeed = train.getAtpSpeed() - 5 / 3.6f; + train.setAtpSpeed(atpSpeed); + train.setAtoSpeed(atoSpeed); break; } case AM: case CM: { - // 驾驶故障导致EB - MovementAuthority ma = train.getMa(); - if (Objects.nonNull(ma)) { // 通信正常,移动授权可用 - // 获取移动授权剩余距离 - Float remainDistance = atpService.calculateProtectDistance(train, ma.getEnd()); - if (Objects.isNull(remainDistance) || remainDistance <= 0) { - log.warn(String.format("列车[%s]无法计算出移动防护剩余距离或防护距离不为正", train.getGroupNumber())); - atpSpeed = 0; - atoSpeed = 0; - } else { -// // 获取防护速度 -// SpeedCurve protectSpeedCurve = ma.getProtectSpeedCurve(); -// float protectSpeed = protectSpeedCurve.getSpeedOf(remainDistance); -// atpSpeed = protectSpeed; - // 计算推荐速度曲线 - float targetDistance = ATOService.calculateTargetRemainDistance(train, ma); - SectionPosition headPosition = train.getHeadPosition(); - SectionPosition tailPosition = train.calculateTailPosition(); - boolean right = train.isRight(); - float speedMax = Math.min(train.getAtoSpeedMax(), train.getSpeedLimit() * 0.9f); - SpeedCurve speedCurve = SpeedCurve - .buildTargetSpeedCurve(headPosition, tailPosition, right, - targetDistance, train.getSpeed(), speedMax); - // 更新目标距离和建议速度 - train.setTargetDistance(targetDistance); - train.setSpeedCurve(speedCurve); - atoSpeed = speedCurve.getSpeedOf(speedCurve.getTotalDistance()); - atpSpeed = atoSpeed + 5 / 3.6f; + MaService.Ma ma = train.getMa2(); + if (ma != null) { + // EB 触发速度计算 + SpeedCurve ebTriggerCurve = ma.getEbTriggerCurve(); + float ebTriggerRemain = ma.calculateDistanceOfEbTriggerEnd(); + float totalDistance = ebTriggerCurve.getTotalDistance(); + if (totalDistance < ebTriggerRemain) { + ebTriggerRemain = totalDistance; } - } else { - atpSpeed = 0; - atoSpeed = 0; - log.warn(String.format("列车[%s-%s|%s|%s]速度[%s]没有移动授权,触发EB", - train.getGroupNumber(), train.getServiceNumber(), - train.getTripNumber(), train.getDestinationCode(), - train.getSpeed())); + float atpSpeed = ebTriggerCurve.getSpeedOf(ebTriggerRemain); + // ATO推荐速度计算 + SpeedCurve speedCurve = SpeedCurve.calculateAtoStopCurveAndUpdate(train, ma); + float atoSpeed = speedCurve.getSpeedOf(speedCurve.getTotalDistance()); + train.setAtpSpeed(atpSpeed); + train.setAtoSpeed(atoSpeed); } - break; +// // 驾驶故障导致EB +// MovementAuthority ma = train.getMa(); +// if (Objects.nonNull(ma)) { // 通信正常,移动授权可用 +// // 获取移动授权剩余距离 +// Float remainDistance = atpService.calculateProtectDistance(train, ma.getEnd()); +// if (Objects.isNull(remainDistance) || remainDistance <= 0) { +// log.warn(String.format("列车[%s]无法计算出移动防护剩余距离或防护距离不为正", train.getGroupNumber())); +// atpSpeed = 0; +// atoSpeed = 0; +// } else { +//// // 获取防护速度 +//// SpeedCurve protectSpeedCurve = ma.getProtectSpeedCurve(); +//// float protectSpeed = protectSpeedCurve.getSpeedOf(remainDistance); +//// atpSpeed = protectSpeed; +// // 计算推荐速度曲线 +// float targetDistance = ATOService.calculateTargetRemainDistance(train, ma); +// SectionPosition headPosition = train.getHeadPosition(); +// SectionPosition tailPosition = train.calculateTailPosition(); +// boolean right = train.isRight(); +// float speedMax = Math.min(train.getAtoSpeedMax(), train.getSpeedLimit() * 0.9f); +// SpeedCurve speedCurve = SpeedCurve +// .buildTargetSpeedCurve(headPosition, tailPosition, right, +// targetDistance, train.getSpeed(), speedMax); +// // 更新目标距离和建议速度 +// train.setTargetDistance(targetDistance); +// train.setSpeedCurve(speedCurve); +// atoSpeed = speedCurve.getSpeedOf(speedCurve.getTotalDistance()); +// atpSpeed = atoSpeed + 5 / 3.6f; +// } +// } else { +// atpSpeed = 0; +// atoSpeed = 0; +// log.warn(String.format("列车[%s-%s|%s|%s]速度[%s]没有移动授权,触发EB", +// train.getGroupNumber(), train.getServiceNumber(), +// train.getTripNumber(), train.getDestinationCode(), +// train.getSpeed())); +// } +// break; } } - train.setAtpSpeed(atpSpeed); - train.setAtoSpeed(atoSpeed); + } private void driveModeControl(Simulation simulation, VirtualRealityTrain train) { @@ -415,14 +416,21 @@ public class ATPLogicLoop { } public boolean checkConditionToMove2(Simulation simulation, VirtualRealityTrain train) { - if (train.isParkingAt() && !train.isStandReadyStart()) { // 站台停靠未完成 - return false; + if (train.isParkingAt()) { + if (!train.isStandReadyStart()) { // 站台停靠未完成 + return false; + } + SectionPosition headPosition = train.getHeadPosition(); + Signal signal = headPosition.getSection().getSignalOf(train.isRight()); + if (signal != null && !signal.isNormalOpen() && !(train.isRMMode() || train.isNRMMode())) {// 站台前方信号未开放 + return false; + } } if (train.isHold()) { return false; } if (simulation.getRepository().getConfig().isAdjustOperationAutomatically()) { - if (train.getDelayTime() == 0) { + if (train.getDelayTime() <= 0) { train.setDelayTime(SimulationConstants.ATO_TRAIN_GET_SIGNAL_TO_START_DELAY); return false; } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPService.java index 0c6f31b12..3b3fd15e8 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/ATPService.java @@ -99,7 +99,7 @@ public class ATPService { * @param train */ public void speedProtect(Simulation simulation, VirtualRealityTrain train) { - if (train.isEB() || !train.isAtpOn()) { + if (train.isEB() || !train.isAtpOn() || (!train.isRMMode() && train.getMa2() == null)) { return; } DriveMode driveMode = train.getDriveMode(); diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiService.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiService.java index 1c3009490..dd0c16684 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiService.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiService.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.onboard.ATP; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.data.map.Section; import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; @@ -11,6 +12,8 @@ import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain; */ public interface OnboardAtpApiService { + void updateCtcMa(Simulation simulation, MaService.Ma ma); + /** * 更新CBTC移动授权 */ @@ -116,5 +119,5 @@ public interface OnboardAtpApiService { void checkAndChangeDirection(Simulation simulation, VirtualRealityTrain train, Section targetSection); - void parkingInPlace(Simulation simulation, VirtualRealityTrain vrTrain); + void parkingInPlace(Simulation simulation, VirtualRealityTrain vrTrain, Section headSection); } diff --git a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiServiceImpl.java b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiServiceImpl.java index f7ff792c8..19c73af07 100644 --- a/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiServiceImpl.java +++ b/src/main/java/club/joylink/rtss/simulation/cbtc/onboard/ATP/OnboardAtpApiServiceImpl.java @@ -1,5 +1,6 @@ package club.joylink.rtss.simulation.cbtc.onboard.ATP; +import club.joylink.rtss.simulation.cbtc.ATP.ground.MaService; import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.constant.DriveMode; import club.joylink.rtss.simulation.cbtc.constant.RunLevel; @@ -11,6 +12,7 @@ import club.joylink.rtss.simulation.cbtc.data.plan.TripPlan; import club.joylink.rtss.simulation.cbtc.data.support.MovementAuthority; import club.joylink.rtss.simulation.cbtc.data.support.RoutePath; import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain; +import club.joylink.rtss.simulation.cbtc.onboard.ATO.SpeedCurve; import club.joylink.rtss.simulation.cbtc.onboard.ATO.service.ATOService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -30,6 +32,13 @@ public class OnboardAtpApiServiceImpl implements OnboardAtpApiService { @Autowired private ATOService ATOService; + @Override + public void updateCtcMa(Simulation simulation, MaService.Ma ma) { + VirtualRealityTrain train = ma.getTrain(); + SpeedCurve.calculateAndUpdate(simulation, train, ma); + train.setCbtcMaMissDuration(0); + } + @Override public void updateMA4CBTC(VirtualRealityTrain train, MovementAuthority ma) { if (ma == null) @@ -275,7 +284,7 @@ public class OnboardAtpApiServiceImpl implements OnboardAtpApiService { } @Override - public void parkingInPlace(Simulation simulation, VirtualRealityTrain vrTrain) { - vrTrain.parkingAt(); + public void parkingInPlace(Simulation simulation, VirtualRealityTrain vrTrain, Section headSection) { + vrTrain.parkingAt(headSection); } } diff --git a/src/main/java/club/joylink/rtss/simulation/vo/SimulationInfoVO.java b/src/main/java/club/joylink/rtss/simulation/vo/SimulationInfoVO.java index 3abc6e9f0..3d84106e7 100644 --- a/src/main/java/club/joylink/rtss/simulation/vo/SimulationInfoVO.java +++ b/src/main/java/club/joylink/rtss/simulation/vo/SimulationInfoVO.java @@ -12,12 +12,6 @@ public class SimulationInfoVO { String name; Integer speed; LocalDateTime systemTime; -// Integer year; -// Integer month; -// Integer day; -// Integer hour; -// Integer minute; -// Integer second; Integer state; List userList; @@ -27,13 +21,6 @@ public class SimulationInfoVO { this.speed = simulation.getSpeed(); this.state = simulation.getState(); this.systemTime = simulation.getSystemTime(); -// LocalDateTime systemTime = simulation.getSystemTime(); -// this.year = systemTime.getYear(); -// this.month = systemTime.getMonthValue(); -// this.day = systemTime.getDayOfMonth(); -// this.hour = systemTime.getHour(); -// this.minute = systemTime.getMinute(); -// this.second = systemTime.getSecond(); } public void setUserList(List userList) {