Compare commits

...

6 Commits

13 changed files with 901 additions and 270 deletions

View File

@ -48,7 +48,9 @@ import club.joylink.rtss.vo.client.project.sr.SrSectionConfigVO;
import club.joylink.rtss.vo.client.project.sr.SrSignalConfigVO; import club.joylink.rtss.vo.client.project.sr.SrSignalConfigVO;
import club.joylink.rtss.vo.client.project.sr.SrSwitchConfigVO; import club.joylink.rtss.vo.client.project.sr.SrSwitchConfigVO;
import club.joylink.rtss.vo.client.project.sr.SrTrainConfigVO; import club.joylink.rtss.vo.client.project.sr.SrTrainConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandRunPlanConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandSectionConfigVO; import club.joylink.rtss.vo.client.project.thailand.ThailandSectionConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandSignalConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandSwitchConfigVO; import club.joylink.rtss.vo.client.project.thailand.ThailandSwitchConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandTrainConfigVO; import club.joylink.rtss.vo.client.project.thailand.ThailandTrainConfigVO;
import club.joylink.rtss.vo.client.project.xty.XtyPsdConfigVO; import club.joylink.rtss.vo.client.project.xty.XtyPsdConfigVO;
@ -62,6 +64,7 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -932,7 +935,7 @@ public class DeviceServiceImpl implements DeviceService {
signal.setType(ProjectDeviceType.SIGNAL.name()); signal.setType(ProjectDeviceType.SIGNAL.name());
signal.setCreator(accountVO.getId()); signal.setCreator(accountVO.getId());
signal.setCreateTime(now); signal.setCreateTime(now);
SrSignalConfigVO configVO = new SrSignalConfigVO(mapSignalNewVO.getCode(), ThailandSignalConfigVO configVO = new ThailandSignalConfigVO(mapSignalNewVO.getCode(),
mapSignalNewVO.getSrCode()); mapSignalNewVO.getSrCode());
signal.setConfig(JsonUtils.writeValueAsString(configVO)); signal.setConfig(JsonUtils.writeValueAsString(configVO));
list.add(signal); list.add(signal);
@ -983,100 +986,120 @@ public class DeviceServiceImpl implements DeviceService {
train.setConfig(JsonUtils.writeValueAsString(configVO)); train.setConfig(JsonUtils.writeValueAsString(configVO));
list.add(train); list.add(train);
} }
//运行计划
ProjectDevice runPlan = new ProjectDevice();
runPlan.setProjectCode(projectCode);
runPlan.setCode("runPlan");
runPlan.setType(ProjectDeviceType.SAND_TABLE_RUN_PLAN.name());
runPlan.setCreator(accountVO.getId());
runPlan.setCreateTime(now);
ThailandRunPlanConfigVO configVO = new ThailandRunPlanConfigVO();
configVO.setStandTrackList(Arrays.asList("T85", "T158", "T205", "T247", "T206", "T157", "T84"));
runPlan.setConfig(JsonUtils.writeValueAsString(configVO));
list.add(runPlan);
return list; return list;
} }
private void thailandFillTime(ThailandSectionConfigVO configVO, MapSectionNewVO section) { private void thailandFillTime(ThailandSectionConfigVO configVO, MapSectionNewVO section) {
Float headDelayTime = null; Float headDelayTime = null;
Float tailDelayTime = null; Float tailDelayTime = null;
if (section.isStandTrack()) { //站台轨默认为车尾出清立即停车 if (section.isStandTrack()) {
headDelayTime = 5f; headDelayTime = 5f;
} }
switch (section.getCode()) { switch (section.getCode()) {
case "T233": //X302G case "T1": //X301G
headDelayTime = 7.3f; headDelayTime = 5f;
break; break;
case "T232": //X303G case "T2": //X302G
headDelayTime = 6.5f; headDelayTime = 7.9f;
break; break;
case "T231": //X304G case "T3": //X303G
headDelayTime = 5.5f; headDelayTime = 6.7f;
break; break;
case "T178": //4站-3G case "T4": //X304G
headDelayTime = 5.2f; headDelayTime = 5.6f;
break; break;
case "T177": //4站-IG case "T5": //X305G
headDelayTime = 5.4f;
break;
case "T175": //4站-IIG
headDelayTime = 6.6f;
break;
case "T174": //4站-4G
headDelayTime = 6.2f;
break;
case "T176": //4站-6G
headDelayTime = 5.9f;
break;
case "T179": //4站-5G
headDelayTime = 6.5f;
break;
case "T123": //3站-3G
headDelayTime = 5.2f;
break;
case "T122": //3站-IG
headDelayTime = 5.3f; headDelayTime = 5.3f;
break; break;
case "T121": //3站-IIG case "T6": //X306G
headDelayTime = 5.9f;
break;
case "T120": //3站-4G
headDelayTime = 5.5f; headDelayTime = 5.5f;
break; break;
case "T78": //2站-IG case "T7": //X307G
headDelayTime = 5.3f;
break;
case "T8": //X308G
headDelayTime = 5.5f; headDelayTime = 5.5f;
break; break;
case "T77": //2站-IIG case "T89": //1站6G
headDelayTime = 5.9f; headDelayTime = 5.9f;
break; break;
case "T76": //2站-4G case "T90": //1站4G
headDelayTime = 6.4f;
break;
case "T85": //1站2G
headDelayTime = 6.8f;
break;
case "T84": //1站1G
headDelayTime = 5.9f;
break;
case "T94": //1站3G
headDelayTime = 5.2f;
break;
case "T95": //1站5G
headDelayTime = 6.5f;
break;
case "T160": //2站4G
headDelayTime = 5.5f;
break;
case "T158": //2站2G
headDelayTime = 5.2f;
break;
case "T157": //2站1G
headDelayTime = 5.8f;
break;
case "T159": //2站3G
headDelayTime = 5.2f;
break;
case "T204": //3站4G
headDelayTime = 5.7f; headDelayTime = 5.7f;
break; break;
case "T22": //1站-1G case "T205": //3站2G
headDelayTime = 5.6f;
break;
case "T206": //3站1G
headDelayTime = 6f;
break;
case "T207": //3站3G
headDelayTime = 5.3f; headDelayTime = 5.3f;
break; break;
case "T23": //1站-IIG case "T242": //4站6G
headDelayTime = 5.1f; headDelayTime = 5.1f;
break; break;
case "T27": //1站-5G case "T243": //3站4G
headDelayTime = 4.9f; headDelayTime = 5.1f;
break; break;
case "T26": //1站-3G case "T244": //3站2G
headDelayTime = 5.1f;
break;
case "T247": //3站1G
headDelayTime = 5.5f;
break;
case "T254": //3站3G
headDelayTime = 4.8f; headDelayTime = 4.8f;
break; break;
case "T24": //1站-4G case "T255": //3站5G
headDelayTime = 5.1f; headDelayTime = 4.9f;
break; break;
case "T25": //1站-6G case "T142": //QX04
headDelayTime = 5.1f;
break;
case "T145": //QX04
headDelayTime = 1.9f; headDelayTime = 1.9f;
break; break;
case "T146": //QS04 case "T141": //其它QX/QS区段
headDelayTime = 2.7f; case "T183":
break; case "T184":
case "T104": //QX03 case "T223":
case "T224":
tailDelayTime = 0f; tailDelayTime = 0f;
break;
case "T105": //QS03
tailDelayTime = 0f;
break;
case "T62": //QX02
tailDelayTime = 0f;
break;
case "T63": //QS02
tailDelayTime = 0f;
break;
} }
configVO.setHeadDelayTime(headDelayTime); configVO.setHeadDelayTime(headDelayTime);
configVO.setTailDelayTime(tailDelayTime); configVO.setTailDelayTime(tailDelayTime);

View File

@ -3,12 +3,11 @@ package club.joylink.rtss.simulation.cbtc.data.support;
import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants; import club.joylink.rtss.simulation.cbtc.constant.SimulationConstants;
import club.joylink.rtss.simulation.cbtc.data.CalculateService; import club.joylink.rtss.simulation.cbtc.data.CalculateService;
import club.joylink.rtss.simulation.cbtc.data.map.Section; import club.joylink.rtss.simulation.cbtc.data.map.Section;
import lombok.Getter;
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.util.Objects; import java.util.Objects;
import lombok.Getter;
import org.springframework.util.CollectionUtils;
/** /**
* 地图位置坐标 * 地图位置坐标
@ -16,117 +15,131 @@ import java.util.Objects;
@Getter @Getter
public class SectionPosition { public class SectionPosition {
private Section section; private Section section;
private float offset; private float offset;
public SectionPosition(Section section, float offset) { public SectionPosition(Section section, float offset) {
this.section = section; this.section = section;
this.offset = offset; this.offset = offset;
}
public boolean isSameSection(Section section) {
return this.section.getCode().equals(section.getCode());
}
/**
* 当前位置是否在other前面更right的位置视为更前
*
* @param other
* @param right
* @return
*/
public boolean isAheadOf(SectionPosition other, boolean right) {
return isAheadOf(other.getSection(), other.getOffset(), right);
}
private boolean isAheadOf(Section section, float offset, boolean right) {
if (Objects.equals(this.getSection(), section)) {
// 同一根区段
if (right && this.getOffset() > offset) {
// 右向此偏移量大于另一个偏移量
return true;
} else if (!right && this.getOffset() < offset) {
// 左向此偏移量小于另一个偏移量
return true;
}
} else { // 不是同一区段查询另一区段在指定方向上是否能找到此位置区段如果找到则在前
if (CalculateService.isTargetSectionOnDirectionExist(section, right,
this.getSection())) {
return true;
}
} }
return false;
}
public boolean isSameSection(Section section) { @Override
return this.section.getCode().equals(section.getCode()); public String toString() {
} return "{" +
String.format("%s(%s)", section.getName(), section.getCode()) +
String.format(", %s", offset) +
'}';
}
public boolean isAheadOf(SectionPosition other, boolean right) { public Section getLogicSection() {
return isAheadOf(other.getSection(), other.getOffset(), right); if (!CollectionUtils.isEmpty(section.getLogicList())) {
} for (Section logic : section.getLogicList()) {
if (this.offset > logic.getMinOffset() && this.offset <= logic.getMaxOffset()) {
public boolean isAheadOf(Section section, float offset, boolean right) { return logic;
if (Objects.equals(this.getSection(), section)) {
// 同一根区段
if (right && this.getOffset() > offset) {
// 右向此偏移量大于另一个偏移量
return true;
} else if (!right && this.getOffset() < offset) {
// 左向此偏移量小于另一个偏移量
return true;
}
} else { // 不是同一区段查询另一区段在指定方向上是否能找到此位置区段如果找到则在前
if (CalculateService.isTargetSectionOnDirectionExist(section, right, this.getSection())) {
return true;
}
} }
return false; }
} }
return this.section;
}
@Override @Override
public String toString() { public int hashCode() {
return "{" + return section.hashCode() + Float.hashCode(offset);
String.format("%s(%s)", section.getName(), section.getCode()) + }
String.format(", %s", offset) +
'}';
}
public Section getLogicSection() { @Override
if (!CollectionUtils.isEmpty(section.getLogicList())) { public boolean equals(Object obj) {
for (Section logic : section.getLogicList()) { if (this == obj) {
if (this.offset > logic.getMinOffset() && this.offset <= logic.getMaxOffset()) { return true;
return logic;
}
}
}
return this.section;
} }
if (obj == null || getClass() != obj.getClass()) {
return false;
}
SectionPosition sp = (SectionPosition) obj;
return Objects.equals(this.getSection(), sp.getSection()) && offset == sp.getOffset();
}
@Override public float getPercent() {
public int hashCode() { return BigDecimal.valueOf(this.offset / this.getSection().getLen())
return section.hashCode() + Float.hashCode(offset); .setScale(4, RoundingMode.HALF_UP).floatValue();
} }
@Override public boolean isAvailable() {
public boolean equals(Object obj) { return !(this.offset < 0 || this.offset > this.section.getLen());
if (this == obj) return true; }
if (obj == null || getClass() != obj.getClass()) return false;
SectionPosition sp = (SectionPosition) obj;
return Objects.equals(this.getSection(), sp.getSection()) && offset == sp.getOffset();
}
public float getPercent() { public boolean isBetween(SectionPosition one, SectionPosition two) {
return BigDecimal.valueOf(this.offset / this.getSection().getLen()) if (one.equals(this) || two.equals(this)) {
.setScale(4, RoundingMode.HALF_UP).floatValue(); return true;
} }
if (one.isAheadOf(this, true)) {
return this.isAheadOf(two, true);
} else if (this.isAheadOf(one, true)) {
return two.isAheadOf(this, true);
} else {
return false;
}
}
public boolean isAvailable() { public SectionPosition convert2PhysicalSectionPosition() {
return !(this.offset < 0 || this.offset > this.section.getLen()); Section section = this.getSection();
if (section.isPhysical()) {
return this;
} else {
Section parent = section.getParent();
if (parent != null) {
return new SectionPosition(parent, section.getMinOffset() + this.getOffset());
} else {
return null;
}
} }
}
public boolean isBetween(SectionPosition one, SectionPosition two) { /**
if (one.equals(this) || two.equals(this)) * 该位置位于对应区段的停车范围内
return true; */
if (one.isAheadOf(this, true)) { public boolean isWithinParkingRange(boolean right) {
return this.isAheadOf(two, true); SectionPosition targetPosition = new SectionPosition(this.section,
} else if (this.isAheadOf(one, true)) { this.section.getStopPointByDirection(right));
return two.isAheadOf(this, true); SectionPosition max = CalculateService.calculateNextPositionByStartAndLen(targetPosition,
} else { right, SimulationConstants.PARK_POINT_MAX_OFFSET, false);
return false; SectionPosition min = CalculateService.calculateNextPositionByStartAndLen(targetPosition,
} !right, SimulationConstants.PARK_POINT_MAX_OFFSET, false);
} return this.isAheadOf(min, right) && max.isAheadOf(this, right);
}
public SectionPosition convert2PhysicalSectionPosition() {
Section section = this.getSection();
if (section.isPhysical()) {
return this;
} else {
Section parent = section.getParent();
if (parent != null) {
return new SectionPosition(parent, section.getMinOffset() + this.getOffset());
} else {
return null;
}
}
}
/**
* 该位置位于对应区段的停车范围内
*/
public boolean isWithinParkingRange(boolean right) {
SectionPosition targetPosition = new SectionPosition(this.section, this.section.getStopPointByDirection(right));
SectionPosition max = CalculateService.calculateNextPositionByStartAndLen(targetPosition,
right, SimulationConstants.PARK_POINT_MAX_OFFSET, false);
SectionPosition min = CalculateService.calculateNextPositionByStartAndLen(targetPosition,
!right, SimulationConstants.PARK_POINT_MAX_OFFSET, false);
return this.isAheadOf(min, right) && max.isAheadOf(this, right);
}
} }

View File

@ -1,21 +1,43 @@
package club.joylink.rtss.simulation.cbtc.device.real.udp; package club.joylink.rtss.simulation.cbtc.device.real.udp;
import club.joylink.rtss.constants.ProjectCode; import club.joylink.rtss.constants.ProjectCode;
import club.joylink.rtss.constants.ProjectDeviceType;
import club.joylink.rtss.dao.MapDataDAO; import club.joylink.rtss.dao.MapDataDAO;
import club.joylink.rtss.entity.MapDataExample; import club.joylink.rtss.entity.MapDataExample;
import club.joylink.rtss.entity.MapDataWithBLOBs; import club.joylink.rtss.entity.MapDataWithBLOBs;
import club.joylink.rtss.simulation.cbtc.GroupSimulationService; import club.joylink.rtss.simulation.cbtc.GroupSimulationService;
import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.map.Section;
import club.joylink.rtss.simulation.cbtc.device.real.modbustcp.device.RealDeviceConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.service.SrTrainServiceImpl; import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.service.SrTrainServiceImpl;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandRunPlanConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.service.ThailandRunPlanServiceImpl;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.service.ThailandTrainServiceImpl; import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.service.ThailandTrainServiceImpl;
import club.joylink.rtss.util.JsonUtils; import club.joylink.rtss.util.JsonUtils;
import club.joylink.rtss.vo.map.MapLogicDataNewVO; import club.joylink.rtss.vo.client.project.thailand.ThailandRunPlanConfigVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandRunPlanConfigVO.Param;
import club.joylink.rtss.vo.map.MapGraphDataNewVO;
import club.joylink.rtss.vo.map.graph.MapSectionNewVO;
import club.joylink.rtss.vo.map.graph.MapSignalNewVO;
import club.joylink.rtss.vo.map.graph.MapSwitchVO;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
/**
* 沙盘项目接口
*/
@RestController @RestController
@RequestMapping("/api/sandTable") @RequestMapping("/api/sandTable")
public class SandTableTrainController { public class SandTableTrainController {
@ -25,6 +47,8 @@ public class SandTableTrainController {
@Autowired @Autowired
private ThailandTrainServiceImpl thailandTrainService; private ThailandTrainServiceImpl thailandTrainService;
@Autowired @Autowired
private ThailandRunPlanServiceImpl thailandRunPlanService;
@Autowired
private SrTrainServiceImpl srTrainService; private SrTrainServiceImpl srTrainService;
@PutMapping("/{simulationId}/control") @PutMapping("/{simulationId}/control")
@ -42,29 +66,89 @@ public class SandTableTrainController {
} }
} }
/**
* 按计划运行
*/
@PutMapping("/{simulationId}/runAsPlan")
public void runAsPlan(@PathVariable String simulationId,
@RequestBody @Validated List<Param> paramList) {
Simulation simulation = groupSimulationService.getSimulationByGroup(simulationId);
switch (simulation.getProject()) {
case ProjectCode.THAILAND_SANDBOX:
thailandRunPlanService.runAsPlan(simulation, paramList);
break;
default:
}
}
/**
* 获取运行计划详情
*/
@GetMapping("/{simulationId}/runPlanDetail")
public List<ThailandRunPlanConfigVO.Detail> getRunPlanDetail(@PathVariable String simulationId) {
Simulation simulation = groupSimulationService.getSimulationByGroup(simulationId);
List<RealDeviceConfig> runPlanConfigList = simulation.getRealDeviceByType(
ProjectDeviceType.SAND_TABLE_RUN_PLAN);
if (CollectionUtils.isEmpty(runPlanConfigList)) {
return new ArrayList<>();
}
ThailandRunPlanConfig config = (ThailandRunPlanConfig) runPlanConfigList.get(0);
List<ThailandRunPlanConfigVO.Detail> list = new ArrayList<>();
Integer parkingDuration = config.getParkingDuration();
config.getDetailMap().forEach((train, detailList) -> {
String groupNumber = train.getGroupNumber();
List<ThailandRunPlanConfigVO.Detail> detailVOList = detailList.stream().map(detail -> {
String stationName = detail.getTrack().getStation().getName();
String trackName = detail.getTrack().getName();
boolean finished = detail.isFinished();
return new ThailandRunPlanConfigVO.Detail(groupNumber, stationName, trackName,
parkingDuration, finished);
}).collect(Collectors.toList());
detailVOList.get(0).setParkingDuration(0);
detailVOList.get(detailVOList.size() - 1).setParkingDuration(0);
list.addAll(detailVOList);
});
return list;
}
@PutMapping("/{simulationId}/occupy")
public void setOccupy(@PathVariable String simulationId, @RequestParam List<String> sectionCodes,
boolean occupy) {
Simulation simulation = groupSimulationService.getSimulationByGroup(simulationId);
for (String sectionCode : sectionCodes) {
Section section = simulation.getRepository().getByCode(sectionCode, Section.class);
section.findAxle().setOccupy(occupy);
}
}
@Autowired @Autowired
private MapDataDAO mapDataDAO; private MapDataDAO mapDataDAO;
@PutMapping("/handle") @PutMapping("/handle")
public void handle() { public void handle() {
MapDataExample example = new MapDataExample(); MapDataExample example = new MapDataExample();
example.createCriteria().andMapIdEqualTo(183L);
example.setOrderByClause("id desc"); example.setOrderByClause("id desc");
example.setLimit(1); example.setLimit(1);
MapDataWithBLOBs data = mapDataDAO.selectByExampleWithBLOBs(example).get(0); MapDataWithBLOBs data = mapDataDAO.selectByExampleWithBLOBs(example).get(0);
MapLogicDataNewVO logicDataNewVO = JsonUtils.read(data.getLogicData(), MapLogicDataNewVO.class); MapGraphDataNewVO graphDataNewVO = JsonUtils.read(data.getGraphData(), MapGraphDataNewVO.class);
logicDataNewVO.getRouteList().stream()
.filter(route -> route.getStartSignalCode().equals("X542") || route.getStartSignalCode() String errSections = graphDataNewVO.getSectionList().stream()
.equals("X11357") .filter(section -> section.getType().equals("04"))
|| route.getStartSignalCode().equals("X47820") || route.getStartSignalCode() .filter(section -> !StringUtils.hasText(section.getSrCode()))
.equals("X4371") .map(MapSectionNewVO::getCode)
|| route.getStartSignalCode().equals("X21134") || route.getStartSignalCode() .collect(Collectors.joining(","));
.equals("X99402") String errSignals = graphDataNewVO.getSignalList().stream()
|| route.getStartSignalCode().equals("X2670") || route.getStartSignalCode() .filter(signal -> !StringUtils.hasText(signal.getSrCode()))
.equals("X49087")) .map(MapSignalNewVO::getCode)
.forEach(route -> { .collect(Collectors.joining(","));
route.setSignalAspect(3); String errSwitches = graphDataNewVO.getSwitchList().stream()
}); .filter(aSwitch -> !StringUtils.hasText(aSwitch.getSrCode()) || aSwitch.getThroat() == null)
data.setLogicData(JsonUtils.writeValueAsString(logicDataNewVO)); .map(MapSwitchVO::getCode)
mapDataDAO.updateByPrimaryKeyWithBLOBs(data); .collect(Collectors.joining(","));
System.out.println("异常区段:" + errSections);
System.out.println("异常信号机:" + errSignals);
System.out.println("异常区段:" + errSwitches);
} }
} }

View File

@ -11,7 +11,6 @@ import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -60,16 +59,15 @@ public class UDPClient implements ApplicationRunner {
public void write(InetSocketAddress addr, byte[] msg) { public void write(InetSocketAddress addr, byte[] msg) {
if (msg != null) { if (msg != null) {
msgQueue.add(new Msg(addr, msg)); msgQueue.add(new Msg(addr, msg));
System.out.println(LocalDateTime.now());
} }
} }
@Scheduled(fixedRate = 10) @Scheduled(fixedRate = 10)
public void send() { public void send() {
Msg msg = msgQueue.poll(); Msg msg = msgQueue.poll();
if (msg == null) { if (msg == null) {
return; return;
} }
InetSocketAddress addr = msg.getAddr(); InetSocketAddress addr = msg.getAddr();
byte[] data = msg.getData(); byte[] data = msg.getData();
if (channel != null && channel.isWritable() && addr != null && data != null) { if (channel != null && channel.isWritable() && addr != null && data != null) {

View File

@ -72,7 +72,7 @@ public class UDPRealDeviceThread {
for (RealDeviceConfig config : realDeviceList) { for (RealDeviceConfig config : realDeviceList) {
for (UDPRealDeviceService service : serviceList) { for (UDPRealDeviceService service : serviceList) {
if (service.isMatch(udpLowConfig) && service.isMatch(config) if (service.isMatch(udpLowConfig) && service.isMatch(config)
&& config.getMapElement() != null) { /*&& config.getMapElement() != null*/) {
service.run(simulation, udpLowConfig, config); service.run(simulation, udpLowConfig, config);
} }
} }

View File

@ -0,0 +1,90 @@
package club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config;
import club.joylink.rtss.simulation.cbtc.data.map.Route;
import club.joylink.rtss.simulation.cbtc.data.map.Section;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain;
import club.joylink.rtss.simulation.cbtc.device.real.modbustcp.device.RealDeviceConfig;
import club.joylink.rtss.util.JsonUtils;
import club.joylink.rtss.vo.client.project.ProjectDeviceVO;
import club.joylink.rtss.vo.client.project.thailand.ThailandRunPlanConfigVO;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ThailandRunPlanConfig extends RealDeviceConfig {
private ThailandRunPlanConfigVO configVO;
/**
* 设置计划后生成的实际计划
*/
private LinkedHashMap<VirtualRealityTrain, List<Detail>> detailMap = new LinkedHashMap<>();
/**
* key - 始终区段code连接 value - 始终区段间的进路
*/
private Map<String, Route[]> routeMap = new HashMap<>();
/**
* 表示是否按计划运行中
*/
private boolean running;
public ThailandRunPlanConfig(ProjectDeviceVO projectDevice) {
super(projectDevice);
if (Objects.nonNull(projectDevice.getConfig())) {
this.configVO = JsonUtils.read(projectDevice.getConfig(), ThailandRunPlanConfigVO.class);
}
}
@Override
public String findDeviceCode() {
return null;
}
public int getParkingDuration() {
return configVO.getParkingDuration();
}
public Integer getDepartureDuration() {
return configVO.getDepartureDuration();
}
public void addRoutes(String fromSectionCode, String toSectionCode, Route[] routes) {
routeMap.put(fromSectionCode + toSectionCode, routes);
}
public Route[] getRoutes(String fromSectionCode, String toSectionCode) {
return routeMap.get(fromSectionCode + toSectionCode);
}
@Data
public static class Detail {
private VirtualRealityTrain train;
private Section track;
private LocalDateTime departureTime;
/**
* 计划是否完成
*/
private boolean finished;
/**
* 进路是否已经办理
*/
private boolean routeSet;
public Detail(VirtualRealityTrain train, Section track, LocalDateTime departureTime) {
this.train = train;
this.track = track;
this.departureTime = departureTime;
}
}
}

View File

@ -0,0 +1,293 @@
package club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.service;
import static club.joylink.rtss.constants.ProjectDeviceType.SAND_TABLE_RUN_PLAN;
import club.joylink.rtss.constants.ProjectDeviceType;
import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
import club.joylink.rtss.simulation.cbtc.ATS.service.AtsTrainLoadService;
import club.joylink.rtss.simulation.cbtc.CI.CiApiService;
import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.SimulationDataRepository;
import club.joylink.rtss.simulation.cbtc.data.map.Route;
import club.joylink.rtss.simulation.cbtc.data.map.Section;
import club.joylink.rtss.simulation.cbtc.data.map.Signal;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain;
import club.joylink.rtss.simulation.cbtc.device.real.modbustcp.device.RealDeviceConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPLowConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPRealDeviceService;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandRunPlanConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandRunPlanConfig.Detail;
import club.joylink.rtss.simulation.cbtc.onboard.ATP.ATPService;
import club.joylink.rtss.vo.client.project.thailand.ThailandRunPlanConfigVO.Param;
import io.netty.buffer.ByteBuf;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Slf4j
@Service
public class ThailandRunPlanServiceImpl implements UDPRealDeviceService {
private AtsTrainLoadService atsTrainLoadService;
private CiApiService ciApiService;
private ATPService atpService;
public ThailandRunPlanServiceImpl(AtsTrainLoadService atsTrainLoadService,
CiApiService ciApiService, ATPService atpService) {
this.atsTrainLoadService = atsTrainLoadService;
this.ciApiService = ciApiService;
this.atpService = atpService;
}
@Override
public boolean isMatch(UDPLowConfig udpLowConfig) {
return true;
}
@Override
public boolean isMatch(RealDeviceConfig realDevice) {
return SAND_TABLE_RUN_PLAN.equals(realDevice.getDeviceType());
}
@Override
public void run(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
ThailandRunPlanConfig config = (ThailandRunPlanConfig) realDevice;
if (!config.isRunning()) {
return;
}
SimulationDataRepository repository = simulation.getRepository();
LocalDateTime systemTime = simulation.getSystemTime();
List<Detail> previousTrainPlanDetails = new ArrayList<>();
boolean planCompleted = true;
for (Entry<VirtualRealityTrain, List<Detail>> entry : config.getDetailMap().entrySet()) {
VirtualRealityTrain train = entry.getKey();
if (!repository.isVrTrainOnline(train.getGroupNumber())) { //列车上线
Section firstTrack = entry.getValue().get(0).getTrack();
Section secondTrack = entry.getValue().get(1).getTrack();
Route[] routes = config.getRoutes(firstTrack.getCode(), secondTrack.getCode());
atsTrainLoadService.loadSpareTrain(simulation, train.getGroupNumber(),
firstTrack.getCode(), routes[0].isRight());
}
Section lastArriveTrack = null;
for (Detail detail : entry.getValue()) {
if (detail.isFinished()) {
lastArriveTrack = detail.getTrack();
continue;
}
Section track = detail.getTrack();
if (/*train.isStop() && */Objects.equals(train.getHeadPosition().getSection(), track)) {
lastArriveTrack = detail.getTrack();
detail.setFinished(true);
continue;
}
planCompleted = false;
if (track.isOccupied()) {
break;
}
if (systemTime.isBefore(detail.getDepartureTime())) {
break;
}
Route[] routes = config.getRoutes(lastArriveTrack.getCode(), track.getCode());
if (routes == null) {
log.error("缺少[{}-{}]的进路路径", lastArriveTrack.getCode(), track.getCode());
break;
}
if (detail.isRouteSet()) {
if (routes[0].isRight() != train.isRight()) {
atpService.turnDirectionImmediately(train);
}
break;
}
if (previousTrainPlanDetails.stream()
.filter(pd -> pd.getTrack().equals(track))
.anyMatch(pd -> !pd.isFinished())) { //前一辆车到该股道的计划尚未完成
break;
}
boolean routeLocked = true;
for (int i = routes.length - 1; i >= 0; i--) {
Route route = routes[i];
if (!route.isLock()) {
routeLocked = false;
ciApiService.settingRoute(simulation, route.getCode());
break;
}
}
if (routeLocked) {
detail.setRouteSet(true);
}
break;
}
previousTrainPlanDetails = entry.getValue();
}
if (planCompleted) {
planRunning(simulation, config, false);
}
}
@Override
public void init(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
ThailandRunPlanConfig config = (ThailandRunPlanConfig) realDevice;
planRunning(simulation, config, false);
config.getDetailMap().clear();
SimulationDataRepository repository = simulation.getRepository();
repository.getStandList()
.forEach(stand -> {
if (stand.getSection().isParkingTrack()) {
stand.setParkingTime(0);
} else {
stand.setParkingTime(config.getParkingDuration());
}
stand.setParkingAlwaysValid(true);
});
Map<String, Route[]> routeMap = config.getRouteMap();
if (CollectionUtils.isEmpty(routeMap)) {
List<Section> trackList = config.getConfigVO().getStandTrackList().stream()
.map(code -> repository.getByCode(code, Section.class))
.collect(Collectors.toList());
List<Section> potSections = repository.getSectionList().stream()
.filter(Section::isParkingTrack)
.collect(Collectors.toList()); //筛选出的都是停车场站台轨
boolean right = true;
for (Section potSection : potSections) {
right = fillRoutes(config, potSection, trackList.get(0), right);
}
for (int i = 0, trackListSize = trackList.size(); i < trackListSize - 1; i++) {
right = fillRoutes(config, trackList.get(i), trackList.get(i + 1), right);
}
for (Section potSection : potSections) {
right = fillRoutes(config, trackList.get(trackList.size() - 1), potSection, right);
}
}
}
@Override
public void handle(Simulation simulation, ByteBuf msg) {
}
private boolean fillRoutes(ThailandRunPlanConfig config, Section from, Section to,
boolean right) {
Route[] routes = findRoutes(from, to, right);
if (routes != null) {
config.addRoutes(from.getCode(), to.getCode(), routes);
return right;
}
routes = findRoutes(from, to, !right);
if (routes == null) {
log.error("[{}-{}]的进路找不到", from.debugStr(), to.debugStr());
}
config.addRoutes(from.getCode(), to.getCode(), routes);
return !right;
}
private Route[] findRoutes(Section from, Section to, boolean right) {
for (int i = 0; i < 10; i++) {
Signal departureSignal = from.getSignalOf(right);
if (departureSignal == null) {
return null;
}
List<Route> departureRoutes = departureSignal.getRouteList();
if (CollectionUtils.isEmpty(departureRoutes)) {
return null;
}
Route departureRoute = departureRoutes.stream()
.filter(Route::isTrainRoute)
.filter(route -> {
Signal destination = route.getDestination();
return destination.isDepartureSignal();
}).findFirst().orElse(null);
if (departureRoute == null) {
return null;
}
Section interStationSection = departureRoute.getDestination().getSection();
Signal receiveSignal = interStationSection.getSignalOf(right);
if (receiveSignal == null) {
return null;
}
List<Route> receiveRoutes = receiveSignal.getRouteList();
if (CollectionUtils.isEmpty(receiveRoutes)) {
return null;
}
Optional<Route> receiveRouteOptional = receiveRoutes.stream()
.filter(Route::isTrainRoute)
.filter(route -> route.getLastRouteSection().equals(to))
.findFirst();
return receiveRouteOptional.map(route -> new Route[]{departureRoute, route}).orElse(null);
}
return null;
}
public void runAsPlan(Simulation simulation, List<Param> paramList) {
SimulationDataRepository repository = simulation.getRepository();
List<String> errMsgList = new ArrayList<>();
Set<String> set = new HashSet<>();
for (Param param : paramList) {
Section section = repository.getByCode(param.getTrackCode(), Section.class);
if (!section.isOccupied()) {
errMsgList.add(String.format("The track named [%s] is unoccupied", section.getName()));
}
VirtualRealityTrain train = repository.queryOnlineTrainBy(param.getGroupNumber());
if (train != null && !train.getHeadPosition().getSection().equals(section)) {
errMsgList.add(String.format(
"The train with number [%s] is already in use and is not on the selected section.",
param.getGroupNumber()));
}
if (!set.add(param.getGroupNumber())) {
errMsgList.add(String.format("[%s] train number is duplicated", param.getGroupNumber()));
}
if (!set.add(param.getTrackCode())) {
errMsgList.add(String.format("[%s] section is duplicated", section.getName()));
}
}
BusinessExceptionAssertEnum.ARGUMENT_ILLEGAL.assertCollectionEmpty(errMsgList,
String.join("\n", errMsgList));
List<RealDeviceConfig> runPlanList = simulation.getRealDeviceByType(
ProjectDeviceType.SAND_TABLE_RUN_PLAN);
BusinessExceptionAssertEnum.OPERATION_NOT_SUPPORTED.assertCollectionNotEmpty(runPlanList,
"Configuration is missing, unable to set the running plan");
ThailandRunPlanConfig runPlanConfig = (ThailandRunPlanConfig) runPlanList.get(0);
List<Section> standTrackList = runPlanConfig.getConfigVO().getStandTrackList().stream()
.map(code -> repository.getByCode(code, Section.class))
.collect(Collectors.toList());
LocalDateTime now = simulation.getSystemTime();
Integer departureDuration = runPlanConfig.getDepartureDuration();
for (int i = 0, paramListSize = paramList.size(); i < paramListSize; i++) {
Param param = paramList.get(i);
VirtualRealityTrain train = repository.getVRByCode(param.getGroupNumber(),
VirtualRealityTrain.class);
Section section = repository.getByCode(param.getTrackCode(), Section.class);
LocalDateTime departureTime =
departureDuration == null ? now : now.plusSeconds(departureDuration * i);
List<Detail> detailList = new ArrayList<>();
detailList.add(new Detail(train, section, departureTime));
standTrackList.forEach(track -> {
Detail detail = new Detail(train, track, departureTime);
detailList.add(detail);
});
detailList.add(new Detail(train, section, departureTime));
runPlanConfig.getDetailMap().put(train, detailList);
}
planRunning(simulation, runPlanConfig, true);
}
private void planRunning(Simulation simulation, ThailandRunPlanConfig config, boolean run) {
config.setRunning(run);
simulation.getRepository().getStationList().forEach(station -> station.setPlanControl(run));
}
}

View File

@ -4,12 +4,15 @@ import club.joylink.rtss.constants.ProjectCode;
import club.joylink.rtss.constants.ProjectDeviceType; import club.joylink.rtss.constants.ProjectDeviceType;
import club.joylink.rtss.simulation.cbtc.Simulation; import club.joylink.rtss.simulation.cbtc.Simulation;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealitySectionAxleCounter; import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealitySectionAxleCounter;
import club.joylink.rtss.simulation.cbtc.data.vr.VirtualRealityTrain;
import club.joylink.rtss.simulation.cbtc.device.real.modbustcp.device.RealDeviceConfig; import club.joylink.rtss.simulation.cbtc.device.real.modbustcp.device.RealDeviceConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPClientConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPClientConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPLowConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPLowConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPRealDeviceService; import club.joylink.rtss.simulation.cbtc.device.real.udp.UDPRealDeviceService;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSectionConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSectionConfig;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -20,21 +23,41 @@ import org.springframework.stereotype.Service;
@Service @Service
public class ThailandSectionServiceImpl implements UDPRealDeviceService { public class ThailandSectionServiceImpl implements UDPRealDeviceService {
private Map<VirtualRealityTrain, LocalDateTime> map = new HashMap<>();
@Override @Override
public boolean isMatch(RealDeviceConfig realDevice) { public boolean isMatch(RealDeviceConfig realDevice) {
return realDevice instanceof UDPClientConfig && ProjectCode.THAILAND_SANDBOX return (realDevice instanceof UDPClientConfig || realDevice instanceof ThailandSectionConfig)
.equals(realDevice.getProject()); && ProjectCode.THAILAND_SANDBOX.equals(realDevice.getProject());
} }
@Override @Override
public void run(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) { public void run(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
//本地测试模拟计轴占用
// List<VirtualRealityTrain> trainList = simulation.getRepository().getOnlineTrainList();
// LocalDateTime now = LocalDateTime.now();
// for (VirtualRealityTrain train : trainList) {
// LocalDateTime time = map.computeIfAbsent(train, K -> now.plusSeconds(7));
// Section section = train.getHeadPosition().getSection();
// VirtualRealitySectionAxleCounter currentAxle = section.findAxle();
// if (!train.isStop() && !now.isBefore(time)) {
// Section nextSection = section.getNextRunningSectionOf(train.isRight());
// if (nextSection == null) {
// continue;
// }
// VirtualRealitySectionAxleCounter nextAxle = nextSection.findAxle();
// if (nextAxle != currentAxle) {
// nextAxle.occupied(train.isRight());
// currentAxle.setOccupy(false);
// }
// map.put(train, time.plusSeconds(7));
// }
// }
} }
@Override @Override
public void init(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) { public void init(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
//沙盘不需要停太长时间 map.clear();
simulation.getRepository().getStandList().forEach(stand -> stand.setParkingTime(15));
} }
@Override @Override

View File

@ -87,6 +87,23 @@ public class ThailandTrainServiceImpl implements UDPRealDeviceService {
} }
} }
@Override
public void init(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
ThailandTrainConfig config = (ThailandTrainConfig) realDevice;
//控制vr设备
VirtualRealityTrain train = (VirtualRealityTrain) config.getMapElement();
//控制沙盘设备
byte[] data = buildData(train, config);
udpClient.write(udpLowConfig.getAddr(), data);
//清除状态
config.updateHeadSection(null);
}
@Override
public void handle(Simulation simulation, ByteBuf msg) {
}
/** /**
* 更新列车位置 * 更新列车位置
* <p> * <p>
@ -103,16 +120,22 @@ public class ThailandTrainServiceImpl implements UDPRealDeviceService {
ThailandSectionConfig sectionConfig = (ThailandSectionConfig) headSection.findAxle() ThailandSectionConfig sectionConfig = (ThailandSectionConfig) headSection.findAxle()
.getRealDevice(); //车头区段的配置 .getRealDevice(); //车头区段的配置
if (nextSection != null) { if (nextSection != null) {
VirtualRealitySectionAxleCounter nextAxle = nextSection.findAxle(); boolean notSwitchTrack = !nextSection.isSwitchTrack();
if (nextAxle.isOccupy()) { boolean isSwitchRelSections =
train.setHeadPosition( nextSection.isSwitchTrack() && nextSection.getRelSwitch().getSectionsByPosition()
new SectionPosition(nextSection, right ? 0 : nextSection.getMaxOffset())); .contains(nextSection);
config.setTimeOfArriveStopPoint(null); if (notSwitchTrack || isSwitchRelSections) {
sectionConfig = (ThailandSectionConfig) nextAxle.getRealDevice(); VirtualRealitySectionAxleCounter nextAxle = nextSection.findAxle();
//更新变量 if (nextAxle.isOccupy()) {
previousSection = headSection; train.setHeadPosition(
headSection = train.getHeadPosition().getSection(); new SectionPosition(nextSection, right ? 0 : nextSection.getMaxOffset()));
nextSection = null; config.setTimeOfArriveStopPoint(null);
sectionConfig = (ThailandSectionConfig) nextAxle.getRealDevice();
//更新变量
previousSection = headSection;
headSection = train.getHeadPosition().getSection();
nextSection = null;
}
} }
} }
//如果列车车头在进路首个区段进路开始解锁 //如果列车车头在进路首个区段进路开始解锁
@ -162,6 +185,12 @@ public class ThailandTrainServiceImpl implements UDPRealDeviceService {
if (config.getManualGear() != null) { if (config.getManualGear() != null) {
return; return;
} }
if (train.isStop() && headSection.isStandTrack()) {
if (headSection.getStandList().stream().anyMatch(stand -> stand.getRemainTime() > 0)) {
//此处自行控制停站时间以确保用户的感知和设置的一样
return;
}
}
if (stopPosition != null) { if (stopPosition != null) {
Float distance = CalculateService.calculateDistance(train.getHeadPosition(), stopPosition, Float distance = CalculateService.calculateDistance(train.getHeadPosition(), stopPosition,
right, false); right, false);
@ -215,23 +244,6 @@ public class ThailandTrainServiceImpl implements UDPRealDeviceService {
} }
} }
@Override
public void init(Simulation simulation, UDPLowConfig udpLowConfig, RealDeviceConfig realDevice) {
ThailandTrainConfig config = (ThailandTrainConfig) realDevice;
//控制vr设备
VirtualRealityTrain train = (VirtualRealityTrain) config.getMapElement();
//控制沙盘设备
byte[] data = buildData(train, config);
udpClient.write(udpLowConfig.getAddr(), data);
//清除状态
config.updateHeadSection(null);
}
@Override
public void handle(Simulation simulation, ByteBuf msg) {
}
private byte[] buildData(VirtualRealityTrain train, ThailandTrainConfig config) { private byte[] buildData(VirtualRealityTrain train, ThailandTrainConfig config) {
int gear; int gear;
if (train.isStop()) { if (train.isStop()) {

View File

@ -196,11 +196,11 @@ public class SimulationRobotService {
doBreakMax(simulation, train); doBreakMax(simulation, train);
} }
} else if (train.isRobotNeedRun() && (train.isRMMode() } else if (train.isRobotNeedRun() && (train.isRMMode()
|| train.isNRMMode())) { //CM应当根据推荐速度驾驶待实现 || train.isNRMMode())) { //CM应当根据推荐速度驾驶待实现
VirtualRealityTrain linkTrain = train.getLinkTrain(); VirtualRealityTrain linkTrain = train.getLinkTrain();
if (linkTrain == null) { if (linkTrain == null) {
Optional<SectionPosition> targetPositionOptional = calculateTargetPosition(simulation, Optional<SectionPosition> targetPositionOptional = calculateTargetPosition(simulation,
train); train);
targetPositionOptional.ifPresent(tp -> robotDrive(simulation, driver, train, tp)); targetPositionOptional.ifPresent(tp -> robotDrive(simulation, driver, train, tp));
} else { } else {
robotDrive(simulation, driver, train, train.getRobotTargetPosition()); robotDrive(simulation, driver, train, train.getRobotTargetPosition());
@ -255,8 +255,10 @@ public class SimulationRobotService {
Signal throughSignal = robotDriveParam.getThroughSignal(); Signal throughSignal = robotDriveParam.getThroughSignal();
SignalAspect throughAspect = robotDriveParam.getThroughSignalAspect(); SignalAspect throughAspect = robotDriveParam.getThroughSignalAspect();
Section section = headPosition.getSection(); Section section = headPosition.getSection();
boolean parking = train.isParkingAt();
if (throughSignal != null && !Objects.equals(section, throughSignal.getSection())) { //当车头与要越过的信号机不在同一区段 if (throughSignal != null && !Objects.equals(section,
throughSignal.getSection())) { //当车头与要越过的信号机不在同一区段
throughSignal = null; throughSignal = null;
throughAspect = null; throughAspect = null;
robotDriveParam.setThrough(DriveParamVO.NO); robotDriveParam.setThrough(DriveParamVO.NO);
@ -268,37 +270,25 @@ public class SimulationRobotService {
// 车头在正常的站台上 // 车头在正常的站台上
if (section.isNormalStandTrack() && !section.equals(train.getParkSection())) { //正常站台轨且未在此处停过车 if (section.isNormalStandTrack() && !section.equals(train.getParkSection())) { //正常站台轨且未在此处停过车
// 如果计划停车区段为空或者计划停车区段中有包含当前区段 // 如果计划停车区段为空或者计划停车区段中有包含当前区段
if (CollectionUtils.isEmpty(plannedParkingSections) || plannedParkingSections.contains(section)) { if (CollectionUtils.isEmpty(plannedParkingSections) || plannedParkingSections.contains(
section)) {
SectionPosition stopPosition = section.buildStopPointPosition(right); SectionPosition stopPosition = section.buildStopPointPosition(right);
if (targetPosition == null || stopPosition.isAheadOf(targetPosition, right)) { if (targetPosition == null || stopPosition.isAheadOf(targetPosition, !right)) {
// 如果头部位置偏移小于停车点位置偏移目标点为当前停车点 // 如果头部位置偏移小于停车点位置偏移目标点为当前停车点
if (headPosition.getOffset() < (section.getStopPointByDirection(right) + SimulationConstants.PARK_POINT_MAX_OFFSET)) { //防止意外开过站后无法发车 if (headPosition.getOffset() < (section.getStopPointByDirection(right)
+ SimulationConstants.PARK_POINT_MAX_OFFSET)) { //防止意外开过站后无法发车
targetPosition = stopPosition; targetPosition = stopPosition;
} }
} }
} }
} }
// 取禁止信号前停车位置与当前目标停车位置中更近的一个 targetPosition = updateTargetPositionOfSignal(headPosition, right,
Signal signal = section.getSignalOf(right); throughSignal, throughAspect, section, i, targetPosition, train.isParkingAt());
if (signal != null && !signal.isShunting()) { // 信号机不为调车信号机
VirtualRealitySignal vrSignal = signal.getVirtualSignal();
SectionPosition signalPosition = signal.getPosition();
if (vrSignal != null && (i != 0 || signalPosition.isAheadOf(headPosition, right))) { //有实体信号机且列车未越过信号机
if (Objects.equals(vrSignal.getAspect(), signal.getDefaultAspect()) //禁止信号
|| Objects.equals(vrSignal.getAspect(), signal.getGuideAspect())) { //引导信号
if (!Objects.equals(signal, throughSignal) || !Objects.equals(vrSignal.getAspect(), throughAspect)) {
SectionPosition noPassPosition = CalculateService.calculateNextPositionByStartAndLen(signalPosition, !right, 2, true);
if (targetPosition == null || noPassPosition.isAheadOf(targetPosition, right)) {
targetPosition = noPassPosition;
}
}
}
}
}
if (targetPosition == null) { if (targetPosition == null) {
if (selectedPosition != null && section.equals(selectedPosition.getSection())) { //不会有比选定位置更靠前的停车点了 if (selectedPosition != null && section.equals(
selectedPosition.getSection())) { //不会有比选定位置更靠前的停车点了
targetPosition = selectedPosition; targetPosition = selectedPosition;
} else { } else {
Section tempSection = section.findNextRunningSectionBaseRealSwitch(right); Section tempSection = section.findNextRunningSectionBaseRealSwitch(right);
@ -310,13 +300,13 @@ public class SimulationRobotService {
} }
} else { } else {
if (selectedPosition != null) { if (selectedPosition != null) {
if (selectedPosition.isAheadOf(targetPosition, right)) { if (selectedPosition.isAheadOf(targetPosition, !right)) {
targetPosition = selectedPosition; targetPosition = selectedPosition;
} else { } else {
break; break;
} }
} }
if (selectedPosition != null && selectedPosition.isAheadOf(targetPosition, right)) { if (selectedPosition != null && selectedPosition.isAheadOf(targetPosition, !right)) {
targetPosition = selectedPosition; targetPosition = selectedPosition;
} else { } else {
@ -329,7 +319,50 @@ public class SimulationRobotService {
if (targetPosition == null) { //上方的区段遍历完后即没有找到目标区段也没有找到轨道尽头 if (targetPosition == null) { //上方的区段遍历完后即没有找到目标区段也没有找到轨道尽头
targetPosition = new SectionPosition(section, right ? 0 : section.getMaxOffset()); targetPosition = new SectionPosition(section, right ? 0 : section.getMaxOffset());
} }
return Optional.ofNullable(targetPosition); return Optional.of(targetPosition);
}
/**
* 获取以信号机为依据的目标位置
*
* @param i 区段的索引
* @return 如果计算出的targetPosition比参数中的更近则返回新的否则返回原来的
*/
private SectionPosition updateTargetPositionOfSignal(SectionPosition headPosition, boolean right,
Signal throughSignal, SignalAspect throughAspect, Section section, int i,
SectionPosition targetPosition, boolean parking) {
Signal signal = section.getSignalOf(right);
if (signal == null || signal.isShunting()) {
return targetPosition;
}
VirtualRealitySignal vrSignal = signal.getVirtualSignal();
SectionPosition signalPosition = signal.getPosition();
if (vrSignal == null || (i == 0 && headPosition.isAheadOf(signalPosition, right))) {
//解释下||后面的条件如果i!=0则车头位置一定不可能在信号机前方即i==0只是为了减少计算对结果没有影响
return targetPosition;
}
if (targetPosition != null && signalPosition.isAheadOf(targetPosition, right)) {
//只要目标位置没有越过信号机则没必要一定和信号机保持2m距离
return targetPosition;
}
if (!Objects.equals(vrSignal.getAspect(), signal.getDefaultAspect())
&& !Objects.equals(vrSignal.getAspect(), signal.getGuideAspect())) { //非禁止/引导信号
return targetPosition;
}
if (Objects.equals(signal, throughSignal)
&& Objects.equals(vrSignal.getAspect(), throughAspect)) {
return targetPosition;
}
if (parking && section.equals(headPosition.getSection())) { //避免停站结束后向前移动到信号机前
return headPosition;
}
SectionPosition newPosition = CalculateService.calculateNextPositionByStartAndLen(
signalPosition, !right, 2, true);
if (targetPosition == null || newPosition.isAheadOf(targetPosition, !right)) {
return newPosition;
} else {
return targetPosition;
}
} }
private void releaseEB(Simulation simulation, SimulationMember driver, private void releaseEB(Simulation simulation, SimulationMember driver,

View File

@ -34,6 +34,7 @@ import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrSectionConf
import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrSignalConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrSignalConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrSwitchConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrSwitchConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrTrainConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.sr.config.SrTrainConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandRunPlanConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSectionConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSectionConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSignalConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSignalConfig;
import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSwitchConfig; import club.joylink.rtss.simulation.cbtc.device.real.udp.thailand.config.ThailandSwitchConfig;
@ -176,6 +177,9 @@ public class ProjectDeviceVO {
case TRAIN: case TRAIN:
list.add(new ThailandTrainConfig(deviceVO)); list.add(new ThailandTrainConfig(deviceVO));
break; break;
case SAND_TABLE_RUN_PLAN:
list.add(new ThailandRunPlanConfig(deviceVO));
break;
} }
} }
return list; return list;

View File

@ -0,0 +1,59 @@
package club.joylink.rtss.vo.client.project.thailand;
import club.joylink.rtss.vo.client.project.RealConfigVO;
import java.util.List;
import javax.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class ThailandRunPlanConfigVO extends RealConfigVO {
private List<String> standTrackList;
/**
* 停站时长/s
*/
private int parkingDuration = 15;
/**
* 发车间隔/s多辆车从停车场发车的间隔时长
*/
private Integer departureDuration;
public ThailandRunPlanConfigVO() {
super(null, null);
}
public ThailandRunPlanConfigVO(Integer addr, Integer quantity) {
super(addr, quantity);
}
@Override
public String findDeviceCode() {
return null;
}
@Data
@AllArgsConstructor
public static class Detail {
private String groupNumber;
private String stationName;
private String trackName;
private Integer parkingDuration;
private boolean finished;
}
@Data
public static class Param {
@NotBlank(message = "groupNumber cannot be null")
private String groupNumber;
@NotBlank(message = "track cannot be null")
private String trackCode;
}
}

View File

@ -3,50 +3,49 @@ package club.joylink.rtss.websocket.interceptor;
import club.joylink.rtss.exception.BusinessExceptionAssertEnum; import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
import club.joylink.rtss.services.LoginSessionManager; import club.joylink.rtss.services.LoginSessionManager;
import club.joylink.rtss.vo.LoginUserInfoVO; import club.joylink.rtss.vo.LoginUserInfoVO;
import java.util.Map;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.HashMap;
import java.util.Map;
@Slf4j @Slf4j
public class SessionAuthHandshakeInterceptor extends BaseInterceptor { public class SessionAuthHandshakeInterceptor extends BaseInterceptor {
private static final String Token_Key = "token";
public static final String ATTR_USER_KEY = "user";
private LoginSessionManager loginSessionManager;
public SessionAuthHandshakeInterceptor(LoginSessionManager loginSessionManager) { private static final String Token_Key = "token";
this.loginSessionManager = loginSessionManager; public static final String ATTR_USER_KEY = "user";
private LoginSessionManager loginSessionManager;
public SessionAuthHandshakeInterceptor(LoginSessionManager loginSessionManager) {
this.loginSessionManager = loginSessionManager;
}
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
try {
LoginUserInfoVO loginUserInfoVO = getLoginUser(request);
attributes.put(ATTR_USER_KEY, loginUserInfoVO);
return true;
} catch (Throwable e) {
log.error("未登录或登陆已过期");
} }
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return false;
}
@Override private LoginUserInfoVO getLoginUser(ServerHttpRequest request) {
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
try {
LoginUserInfoVO loginUserInfoVO = getLoginUser(request);
attributes.put(ATTR_USER_KEY, loginUserInfoVO);
return true;
} catch (Throwable e) {
log.error("未登录或登陆已过期", e);
}
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return false;
}
private LoginUserInfoVO getLoginUser(ServerHttpRequest request) {
// String query = request.getURI().getQuery(); // String query = request.getURI().getQuery();
Map<String, String> map = this.findQueryParams(request); Map<String, String> map = this.findQueryParams(request);
log.info(String.format("webSocket handshake query: [%s]", map)); log.info(String.format("webSocket handshake query: [%s]", map));
BusinessExceptionAssertEnum.NOT_LOGIN.assertTrue(StringUtils.hasText(map.get(Token_Key))); BusinessExceptionAssertEnum.NOT_LOGIN.assertTrue(StringUtils.hasText(map.get(Token_Key)));
return this.loginSessionManager.getLoginInfoByToken(map.get(Token_Key)); return this.loginSessionManager.getLoginInfoByToken(map.get(Token_Key));
// AccountVO accountVO = loginInfoVO.getAccountVO(); // AccountVO accountVO = loginInfoVO.getAccountVO();
// BusinessExceptionAssertEnum.NOT_LOGIN.assertNotNull(accountVO); // BusinessExceptionAssertEnum.NOT_LOGIN.assertNotNull(accountVO);
// return accountVO; // return accountVO;
} }
} }