大铁CTC接发车功能

This commit is contained in:
joylink_zhangsai 2022-06-16 18:54:27 +08:00
parent 44a1d09e20
commit 2991922eb7
17 changed files with 228 additions and 95 deletions

View File

@ -10,6 +10,8 @@ import org.springframework.util.ReflectionUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
@Getter
@ -73,6 +75,9 @@ public class OperateMethod {
}
} else if (Map.class.isAssignableFrom(parameter.getType())) {
args[i] = JsonUtils.read(JsonUtils.writeValueAsString(param.get(parameter.getName())), parameter.getType());
} else if (LocalTime.class.equals(parameter.getType()) || LocalDateTime.class.equals(parameter.getType())) {
Object value = param.get(parameter.getName());
args[i] = parameter.getType().cast(value);
} else {
Object value = param.get(parameter.getName());
if (value != null) {

View File

@ -888,6 +888,9 @@ public class Operation {
*/
CTC_AUTO_TRIGGER,
//------ 站场图操作 ------
CTC_SET_ROUTE,
//------ 行车日志操作 ------
/**
* 发送预告发车预告

View File

@ -31,11 +31,11 @@ public class SignalOperateHandler {
*/
@OperateHandlerMapping(type = Operation.Type.Signal_Set_Route)
public void settingRoute(Simulation simulation, String routeCode) {
Route.CheckFailMessage checkResult = this.ciApiService.routeSettingCheck(simulation, routeCode);
if (checkResult != null)
log.info(checkResult.debugStr());
BusinessExceptionAssertEnum.SIMULATION_EXCEPTION_FOR_SHOW.assertNull(checkResult, "进路排列失败,被联锁逻辑取消");
this.ciApiService.settingRoute(simulation, routeCode);
// Route.CheckFailMessage checkResult = this.ciApiService.routeSettingCheck(simulation, routeCode);
// if (checkResult != null)
// log.info(checkResult.debugStr());
Route.CheckFailMessage checkFailMessage = this.ciApiService.settingRoute(simulation, routeCode);
BusinessExceptionAssertEnum.SIMULATION_EXCEPTION_FOR_SHOW.assertNull(checkFailMessage, "进路排列失败,被联锁逻辑取消");
}
/**
@ -147,9 +147,10 @@ public class SignalOperateHandler {
/**
* 冲突进路办理确认
*
* @param simulation
* @param routeCode
* @param way 处理方式1-按计划执行2-执行冲突进路
* @param way 处理方式1-按计划执行2-执行冲突进路
*/
@OperateHandlerMapping(type = Operation.Type.Signal_Conflict_Route_Set_Confirm)
public void conflictRouteSetConfirm(Simulation simulation, SimulationMember member, String routeCode, int way) {

View File

@ -129,8 +129,9 @@ public interface CiApiService {
* 排列进路
* @param simulation
* @param routeCode
* @return
*/
void settingRoute(Simulation simulation, String routeCode);
Route.CheckFailMessage settingRoute(Simulation simulation, String routeCode);
/**
* 解锁进路/取消进路无法取消进路接近区段占用的进路

View File

@ -175,9 +175,9 @@ public class CiApiServiceImpl2 implements CiApiService {
}
@Override
public void settingRoute(Simulation simulation, String routeCode) {
public Route.CheckFailMessage settingRoute(Simulation simulation, String routeCode) {
Route route = simulation.getRepository().getByCode(routeCode, Route.class);
this.routeService.setRoute(simulation, route);
return this.routeService.setRoute(simulation, route);
}
@Override

View File

@ -17,6 +17,7 @@ import club.joylink.rtss.websocket.StompMessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.*;
@ -33,7 +34,7 @@ public class CTCLogicLoop {
private StompMessageService stompMessageService;
@Autowired
private CiApiService ciApiService;
private CTCService ctcService;
public void run(Simulation simulation) {
updateRunPlanLog(simulation);
@ -66,16 +67,22 @@ public class CTCLogicLoop {
LocalDateTime correctSystemTime = simulation.getCorrectSystemTime();
for (RouteSequence routeSequence : simulation.getCtcRepository().getRouteSequenceMap().values()) {
for (RouteSequence.Line line : routeSequence.getLines()) {
if (line.isTriggered())
continue;
Route route = line.getRoute();
if (!line.isTriggered()) {
if (route != null) {
if (route.isLock()) {
line.setTriggered(true);
} else if (line.isAutoTrigger() && correctSystemTime.toLocalTime().isAfter(line.getPlanTime())) {
ciApiService.settingRoute(simulation, line.getRouteCode());
}
if (line.getTripNumber().equals(route.getTripNumber())) {
if (route.isLock()) {
line.setTriggered(true);
continue;
} else {
line.setSetting(route.isSetting());
if (line.isSetting())
continue;
}
}
if (line.isAutoTrigger() && correctSystemTime.toLocalTime().isAfter(line.getPlanTime())) {
ctcService.setRoute(simulation, line.getRouteCode(), line.getTripNumber(), false, null);
}
}
}
}
@ -85,27 +92,54 @@ public class CTCLogicLoop {
*/
private void updateRunPlanLog(Simulation simulation) {
SimulationDataRepository repository = simulation.getRepository();
String actualTime = simulation.getCorrectSystemTime().toLocalTime().toString();
actualTime = actualTime.substring(0, actualTime.indexOf("."));
CtcRepository ctcRepository = simulation.getCtcRepository();
for (TrainInfo trainInfo : repository.getSuperviseTrainList()) {
VirtualRealityTrain train = repository.getOnlineTrainBy(trainInfo.getGroupNumber());
SectionPosition headPosition = train.getHeadPosition();
Section headSection = headPosition.getSection();
Station station = headSection.getStation();
CtcStationRunPlanLog runPlan = ctcRepository.getRunPlan(station.getCode(), trainInfo.getServiceNumber());
Station station = headSection.getDeviceStation();
String tripNumber = trainInfo.getTripNumber();
CtcStationRunPlanLog runPlan = ctcRepository.findRunPlan(station.getCode(), tripNumber);
if (runPlan == null)
continue;
// 接车计划到点列车占压区段与接车计划股道一致且列车是停站状态
CtcStationRunPlanLog.RunPlanItem arriveRunPlan = runPlan.getArriveRunPlan();
if (arriveRunPlan != null) {
if (headSection.equals(arriveRunPlan.getTrackSection()) && train.isParkingAt()) {
arriveRunPlan.setActualTime(simulation.getCorrectSystemTime().toLocalTime().toString());
if (!StringUtils.hasText(arriveRunPlan.getActualTime())) {
if (headSection.equals(arriveRunPlan.getTrackSection()) && train.isParkingAt()) {
arriveRunPlan.setActualTime(actualTime);
//设置上一站的邻站到达
Station previousStation = runPlan.findPreviousStation();
if (previousStation != null) {
CtcStationRunPlanLog previousRunPlan = ctcRepository.findRunPlan(previousStation.getCode(), tripNumber);
if (previousRunPlan != null) {
CtcStationRunPlanLog.RunPlanItem nextArriveRunPlan = previousRunPlan.getArriveRunPlan();
nextArriveRunPlan.setAdjacentDepart(actualTime);
}
}
}
}
}
// 发车计划发点列车位置越过出站信号机
CtcStationRunPlanLog.RunPlanItem departRunPlan = runPlan.getDepartRunPlan();
if (departRunPlan != null) {
Section trackSection = departRunPlan.getTrackSection();
Signal signal = trackSection.getSignalOf(departRunPlan.isRight());
if (headPosition.isAheadOf(signal.getPosition(), departRunPlan.isRight())) {
departRunPlan.setActualTime(simulation.getCorrectSystemTime().toLocalTime().toString());
if (!StringUtils.hasText(departRunPlan.getActualTime())) { //实际发车时间没填
Section trackSection = departRunPlan.getTrackSection();
Signal signal = trackSection.getSignalOf(departRunPlan.isRight());
if (headPosition.isAheadOf(signal.getPosition(), departRunPlan.isRight())) { //车头越过出站信号机
departRunPlan.setActualTime(actualTime);
//设置下一站的<邻站出发>
Station nextStation = runPlan.findNextStation();
if (nextStation != null) {
CtcStationRunPlanLog nextRunPlan = ctcRepository.findRunPlan(nextStation.getCode(), tripNumber);
if (nextRunPlan != null) {
CtcStationRunPlanLog.RunPlanItem nextArriveRunPlan = nextRunPlan.getArriveRunPlan();
nextArriveRunPlan.setAdjacentDepart(actualTime);
}
}
}
}
}
}
@ -182,7 +216,7 @@ public class CTCLogicLoop {
} else {
for (RouteSequence routeSequence : routeSequenceMap.values()) {
RouteSequenceVO vo = routeSequenceVOMap.get(routeSequence.getStationCode());
Map<String, Object> changed = vo.updateAndReturnChanged(routeSequence);
RouteSequenceVO changed = vo.updateAndReturnChanged(routeSequence);
if (changed != null) {
Map<String, Object> map = new HashMap<>();
map.put("stationCode", vo.getStationCode());

View File

@ -1,20 +1,25 @@
package club.joylink.rtss.simulation.cbtc.CTC;
import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
import club.joylink.rtss.simulation.cbtc.CI.CiApiService;
import club.joylink.rtss.simulation.cbtc.CTC.data.CtcRepository;
import club.joylink.rtss.simulation.cbtc.CTC.data.CtcStationRunPlanLog;
import club.joylink.rtss.simulation.cbtc.CTC.data.RouteSequence;
import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.map.Route;
import club.joylink.rtss.simulation.cbtc.data.map.Station;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.time.LocalTime;
import java.util.Objects;
@Component
public class CTCService {
@Autowired
private CTCLogicLoop ctcLogicLoop;
private CiApiService ciApiService;
public void runPlanItemUpdate(Simulation simulation, Station station, CtcStationRunPlanLog.RunPlanItem item, boolean departure) {
CtcRepository ctcRepository = simulation.getCtcRepository();
@ -106,6 +111,16 @@ public class CTCService {
line.setAutoTrigger(trigger);
}
public String setRoute(Simulation simulation, String routeCode, String tripNumber, Boolean force, Integer duration) {
// if (StringUtils.hasText(tripNumber) && !Objects.equals(true, force)) {
// // CTC卡控功能
// }
Route.CheckFailMessage checkFailMessage = ciApiService.settingRoute(simulation, routeCode);
if (checkFailMessage != null)
throw BusinessExceptionAssertEnum.OPERATION_FAIL.exception(checkFailMessage.debugStr());
return null;
}
private CtcStationRunPlanLog getCtcStationRunPlan(Simulation simulation, String stationCode, String tripNumber) {
CtcRepository ctcRepository = simulation.getCtcRepository();
return ctcRepository.getRunPlan(stationCode, tripNumber);

View File

@ -180,12 +180,16 @@ public class CtcRepository {
* 根据车次获取运行计划
*/
public CtcStationRunPlanLog getRunPlan(String stationCode, String tripNumber) {
// 先从仿真数据中寻找
CtcStationRunPlanLog planLog = this.allRunPlanList.stream()
CtcStationRunPlanLog planLog = findRunPlan(stationCode, tripNumber);
BusinessExceptionAssertEnum.DATA_NOT_EXIST.assertNotNull(planLog,
String.format("车站[%s]车次[%s]的数据不存在", stationCode, tripNumber));
return planLog;
}
public CtcStationRunPlanLog findRunPlan(String stationCode, String tripNumber) {
return this.allRunPlanList.stream()
.filter(r -> r.getStation().getCode().equals(stationCode) && r.getTripNumber().equals(tripNumber))
.findFirst().orElse(null);
BusinessExceptionAssertEnum.DATA_NOT_EXIST.assertNotNull(planLog);
return planLog;
}
public RouteSequence.Line getRouteSequenceLine(String stationCode, String tripNumber, boolean departure) {

View File

@ -190,14 +190,24 @@ public class CtcStationRunPlanLog {
departRunPlan.setAdjacentMessage(RunPlanItem.WAIT);
}
public Station findNextStation() {
return departRunPlan == null ? null : departRunPlan.getStation();
}
public Station getNextStation() {
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(departRunPlan);
return departRunPlan.getStation();
Station station = findNextStation();
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(station);
return station;
}
public Station findPreviousStation() {
return arriveRunPlan == null ? null : arriveRunPlan.getStation();
}
public Station getPreviousStation() {
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(arriveRunPlan);
return arriveRunPlan.getStation();
Station station = findPreviousStation();
BusinessExceptionAssertEnum.SYSTEM_EXCEPTION.assertNotNull(station);
return station;
}
/**
@ -362,6 +372,10 @@ public class CtcStationRunPlanLog {
this.accessName = String.format("%s%s(%s)", "甲丙", this.right ? "上行" : "下行", this.station.getName());
}
}
public String getPlanTimeStr() {
return planTime == null ? null : planTime.toString();
}
}
/**

View File

@ -189,6 +189,12 @@ public class RouteSequence {
private LocalTime planTime;
private Route route;
/**
* 进路是否正在触发
*/
@Setter
private boolean setting;
/**
* 进路是否已经触发过
*/
@ -204,15 +210,16 @@ public class RouteSequence {
this.route = route;
}
public String getTripNumber() {
return this.item.getTripNumber();
public String getAccessName() {
return item.getAccessName();
}
public boolean isSetting() {
if (route != null) {
return route.isSetting();
}
return false;
public String getDirection() {
return departure ? "-->" + item.getStation().getName() : item.getStation().getName() + "-->";
}
public String getTripNumber() {
return this.item.getTripNumber();
}
public Section getTrack() {

View File

@ -20,13 +20,18 @@ public class RouteSequenceVO {
/**
* 进路序列模式可修改/只读
*/
private boolean readOnly;
private Boolean readOnly;
private final List<LineVO> lines;
@JsonIgnore
private final Map<String, LineVO> idLineMap;
public RouteSequenceVO() {
lines = new ArrayList<>();
idLineMap = new HashMap<>();
}
public RouteSequenceVO(RouteSequence routeSequence) {
this.stationCode = routeSequence.getStationCode();
this.readOnly = routeSequence.isReadOnly();
@ -45,13 +50,15 @@ public class RouteSequenceVO {
this.idLineMap.put(line.getId(), line);
}
public Map<String, Object> updateAndReturnChanged(RouteSequence routeSequence) {
Map<String, Object> map = new HashMap<>();
public RouteSequenceVO updateAndReturnChanged(RouteSequence routeSequence) {
RouteSequenceVO routeSequenceVO = new RouteSequenceVO();
boolean change = false;
if (!Objects.equals(readOnly, routeSequence.isReadOnly())) {
change = true;
readOnly = routeSequence.isReadOnly();
map.put("readOnly", readOnly);
routeSequenceVO.setReadOnly(readOnly);
}
List<Object> properties = new ArrayList<>();
List<LineVO> lines = routeSequenceVO.getLines();
for (RouteSequence.Line line : routeSequence.getLines()) {
// TODO: 2022/6/14 需要考虑修改股道之后对应的进路序列删除的逻辑
if (line.getRoute() == null) {
@ -59,22 +66,19 @@ public class RouteSequenceVO {
}
LineVO vo = findLine(line.getId());
if (vo == null) {
change = true;
vo = new LineVO(line);
addLine(vo);
properties.add(vo);
lines.add(vo);
} else {
Map<String, Object> changed = vo.updateAndReturnChanged(line);
if (changed != null)
properties.add(changed);
LineVO changed = vo.updateAndReturnChanged(line);
if (changed != null) {
change = true;
lines.add(changed);
}
}
}
if (!CollectionUtils.isEmpty(properties)) {
map.put("lines", properties);
}
if (!CollectionUtils.isEmpty(map)) {
return map;
}
return null;
return change ? routeSequenceVO : null;
}
@Getter
@ -102,10 +106,10 @@ public class RouteSequenceVO {
*/
private boolean departure;
// /**
// * 方向
// */
// private String direction;
/**
* 方向
*/
private String direction;
/**
* 开始时间
@ -130,12 +134,17 @@ public class RouteSequenceVO {
private static final String SETTING = "1";
private static final String TRIGGERED = "2";
public LineVO(String id) {
this.id = id;
}
public LineVO(RouteSequence.Line line) {
id = line.getId();
tripNumber = line.getTripNumber();
trackName = line.getTrackName();
autoTrigger = line.isAutoTrigger();
departure = line.isDeparture();
direction = line.getDirection();
startTime = line.getStartTime();
planTime = line.getPlanTime();
routeCode = line.getRouteCode();
@ -151,36 +160,50 @@ public class RouteSequenceVO {
/**
* 更新并返回有变化的字段值
*/
public Map<String, Object> updateAndReturnChanged(RouteSequence.Line line) {
Map<String, Object> map = new HashMap<>();
public LineVO updateAndReturnChanged(RouteSequence.Line line) {
LineVO vo = new LineVO(line.getId());
boolean change = false;
if (!Objects.equals(tripNumber, line.getTripNumber())) {
change = true;
tripNumber = line.getTripNumber();
map.put("tripNumber", tripNumber);
vo.setTripNumber(tripNumber);
}
if (!Objects.equals(trackName, line.getTrackName())) {
change = true;
trackName = line.getTrackName();
map.put("trackName", trackName);
vo.setTrackName(trackName);
}
if (!Objects.equals(autoTrigger, line.isAutoTrigger())) {
change = true;
autoTrigger = line.isAutoTrigger();
map.put("autoTrigger", autoTrigger);
vo.setAutoTrigger(autoTrigger);
}
if (!Objects.equals(departure, line.isDeparture())) {
change = true;
departure = line.isDeparture();
map.put("departure", departure);
vo.setDeparture(departure);
}
if (!Objects.equals(direction, line.getDirection())) {
change = true;
direction = line.getDirection();
vo.setDirection(direction);
}
if (!Objects.equals(startTime, line.getStartTime())) {
change = true;
startTime = line.getStartTime();
map.put("startTime", startTime);
vo.setStartTime(startTime);
}
if (!Objects.equals(planTime, line.getPlanTime())) {
change = true;
planTime = line.getPlanTime();
map.put("planTime", planTime);
vo.setPlanTime(planTime);
}
if (!Objects.equals(routeCode, line.getRouteCode())) {
change = true;
routeCode = line.getRouteCode();
map.put("routeCode", line.getRouteCode());
vo.setRouteCode(routeCode);
}
String status;
if (line.isTriggered()) {
status = TRIGGERED;
} else if (line.isSetting()) {
@ -188,13 +211,13 @@ public class RouteSequenceVO {
} else {
status = WAIT;
}
if (!CollectionUtils.isEmpty(map)) {
map.put("id", id);
map.put("status", status);
return map;
if (!Objects.equals(this.status, status)) {
change = true;
this.status = status;
vo.setStatus(status);
}
return null;
return change ? vo : null;
}
}
}

View File

@ -139,6 +139,18 @@ public class TrackView {
return null;
}
public String getReceivingDirection() {
if (receivingRoute != null)
return receivingRoute.getAccessName() + "->";
return "-->";
}
public String getDepartureDirection() {
if (departureRoute != null)
return departureRoute.getAccessName();
return "-";
}
public boolean isReceivingRouteLock() {
if (receivingRoute != null) {
return receivingRoute.isTriggered();
@ -264,26 +276,26 @@ public class TrackView {
public String getArriveTime() {
if (receivingRoute ==null)
return null;
return "始发";
return receivingRoute.getItem().getActualTime();
}
public LocalTime getPlanArriveTime() {
public String getPlanArriveTime() {
if (receivingRoute == null)
return null;
return receivingRoute.getItem().getPlanTime();
return "图定始发";
return receivingRoute.getItem().getPlanTimeStr();
}
public String getDepartureTime() {
if (departureRoute ==null)
return null;
return "终到";
return departureRoute.getItem().getActualTime();
}
public LocalTime getPlanDepartureTime() {
public String getPlanDepartureTime() {
if (departureRoute == null)
return null;
return departureRoute.getItem().getPlanTime();
return "图定终到";
return departureRoute.getItem().getPlanTimeStr();
}
}

View File

@ -76,12 +76,12 @@ public class TrackViewVO {
/**
* 计划到达时间
*/
private LocalTime planArriveTime;
private String planArriveTime;
/**
* 计划发车时间
*/
private LocalTime planDepartureTime;
private String planDepartureTime;
public LineVO(String id) {
this.id = id;
@ -197,13 +197,13 @@ public class TrackViewVO {
this.departureTime = departureTime;
vo.setDepartureTime(departureTime);
}
LocalTime planArriveTime = line.getPlanArriveTime();
String planArriveTime = line.getPlanArriveTime();
if (!Objects.equals(this.planArriveTime, planArriveTime)) {
change = true;
this.planArriveTime = planArriveTime;
vo.setPlanArriveTime(planArriveTime);
}
LocalTime planDepartureTime = line.getPlanDepartureTime();
String planDepartureTime = line.getPlanDepartureTime();
if (!Objects.equals(this.planArriveTime, planArriveTime)) {
change = true;
this.planDepartureTime = planDepartureTime;

View File

@ -88,4 +88,15 @@ public class RailCtcOperateHandler {
public void routeAutoTrigger(Simulation simulation, String stationCode, String tripNumber, String routeCode, boolean trigger) {
ctcService.routeAutoTrigger(simulation, stationCode, tripNumber, routeCode, trigger);
}
/**
* CTC办理进路
* @param tripNumber 办理列车进路时可以输入车次号
* @param force 是否强制办理
* @param duration 办理调车进路时可以输入时间
*/
@OperateHandlerMapping(type = Operation.Type.CTC_SET_ROUTE)
public void setRoute(Simulation simulation, String routeCode, String tripNumber, Boolean force, Integer duration) {
ctcService.setRoute(simulation, routeCode, tripNumber, force, duration);
}
}

View File

@ -154,6 +154,9 @@ public class Route extends MapNamedElement {
/** 执行冲突进路 */
public static final int Conflict_Handle_Way_2 = 2;
/** 大铁进路排列车次信息 */
private String tripNumber;
@Override
public void reset() {
this.atsControl = true;

View File

@ -74,10 +74,6 @@ public enum WebSocketMessageType {
* 仿真-设备状态消息
*/
Simulation_DeviceStatus,
/**
* 大铁CTC系统的状态消息
*/
Simulation_RailCtcStatus,
/**
* 仿真-IBP状态消息
*/
@ -353,6 +349,10 @@ public enum WebSocketMessageType {
/** ------------ CTC消息信息 ------- */
/**
* 大铁CTC系统的状态消息
*/
Simulation_RailCtcStatus,
/**
* 仿真CTC运行计划初始化
**/

View File

@ -85,7 +85,6 @@ public class SocketMessageFactory {
case Simulation_User:
case Simulation_Member:
case Simulation_DeviceStatus:
case Simulation_RailCtcStatus:
case Simulation_IbpStatus:
case Simulation_PslStatus:
case Simulation_AutoFault_Trigger:
@ -108,7 +107,8 @@ public class SocketMessageFactory {
}
case SIMULATION_CTC_RUN_PLAN_INIT:
case SIMULATION_CTC_RUN_PLAN_CHANGE:
case SIMULATION_CTC_RUN_PLAN_REMOVE: {
case SIMULATION_CTC_RUN_PLAN_REMOVE:
case Simulation_RailCtcStatus: {
topicList.add(SimulationSubscribeTopic.Ctc.buildDestination(group));
break;
}