|
|
@@ -1,13 +1,15 @@
|
|
|
package top.haijunit.train.simulation.train;
|
|
|
|
|
|
+import cn.hutool.core.collection.CollUtil;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import top.haijunit.common.utils.ErrorUtil;
|
|
|
+import top.haijunit.common.utils.StreamUtils;
|
|
|
import top.haijunit.train.domain.constant.TrainConstant;
|
|
|
-import top.haijunit.train.domain.entity.SelectionEntity;
|
|
|
import top.haijunit.train.domain.entity.SignStopEntity;
|
|
|
import top.haijunit.train.service.SelectionService;
|
|
|
-import top.haijunit.train.simulation.domain.RouteItem;
|
|
|
+import top.haijunit.train.simulation.domain.SectionItem;
|
|
|
import top.haijunit.train.simulation.domain.TrainItem;
|
|
|
import top.haijunit.train.utils.CalculatorUtil;
|
|
|
import top.haijunit.train.utils.NumberUtil;
|
|
|
@@ -15,11 +17,8 @@ import top.haijunit.train.utils.NumberUtil;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.time.Duration;
|
|
|
import java.time.LocalTime;
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.LinkedList;
|
|
|
-import java.util.OptionalInt;
|
|
|
+import java.util.*;
|
|
|
import java.util.stream.Collectors;
|
|
|
-import java.util.stream.IntStream;
|
|
|
|
|
|
/**
|
|
|
* @author zhanghaijun
|
|
|
@@ -32,103 +31,131 @@ import java.util.stream.IntStream;
|
|
|
public class TrainComputerService {
|
|
|
private final SelectionService selectionService;
|
|
|
|
|
|
-
|
|
|
public void run(TrainItem trainItem) {
|
|
|
- TrainItem item = this.runComputer(trainItem);
|
|
|
- if (null != item) {
|
|
|
- TrainSimulationHolder.addTrainItem(item);
|
|
|
+ try {
|
|
|
+ TrainItem item = this.runComputer(trainItem);
|
|
|
+ if (null != item) {
|
|
|
+ log.info("列车{} 区段:{},偏移量:{},速度:{},加速度:{}", trainItem.getTrainNo(), trainItem.getBlockNo(), trainItem.getOffset(), trainItem.getSpeed(), trainItem.getAcceleration());
|
|
|
+ TrainSimulationHolder.addTrainItem(item);
|
|
|
+ }
|
|
|
+ } catch (Exception exception) {
|
|
|
+ log.error("列车行驶计算错误:{}", ErrorUtil.errorInfoToString(exception));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public TrainItem runComputer(TrainItem trainItem) {
|
|
|
- // 进路数据
|
|
|
- LinkedList<RouteItem> routeList = TrainSimulationHolder.getRouteList(trainItem.getTrainId());
|
|
|
- // 区段数据
|
|
|
- LinkedList<SelectionEntity> selectionAllList = new LinkedList<>();
|
|
|
- for (RouteItem item : routeList) {
|
|
|
- selectionAllList.addAll(item.getSelectionList());
|
|
|
- }
|
|
|
- // 停车标数据
|
|
|
- HashMap<Long, SignStopEntity> stopSignalMap = selectionService.getStopSignalMap(selectionAllList.stream().map(SelectionEntity::getId).collect(Collectors.toCollection(LinkedList::new)));
|
|
|
- // 当前的区段下标
|
|
|
- OptionalInt optionIndex = IntStream.range(0, selectionAllList.size()).filter(i -> {
|
|
|
- SelectionEntity entity = selectionAllList.get(i);
|
|
|
- return NumberUtil.isContain(trainItem.getHeadKilometer(), entity.getStartKilometer(), entity.getEndKilometer());
|
|
|
- }).findFirst();
|
|
|
- if (optionIndex.isEmpty()) {
|
|
|
- log.error("列车没有进路,列车位置: {},列车速度: {},列车方向: {}", trainItem.getHeadKilometer(), trainItem.getSpeed(), trainItem.getDirection());
|
|
|
+ public TrainItem runComputer(TrainItem train) {
|
|
|
+ // 进路区段数据
|
|
|
+ LinkedList<SectionItem> sectionList = TrainSimulationHolder.getRouteList(train.getTrainId());
|
|
|
+ if (CollUtil.isEmpty(sectionList)) {
|
|
|
+ log.error("没有列车的进路数据,请检查进路计算是否正常,列车name:{}", train.getTrainNo());
|
|
|
return null;
|
|
|
}
|
|
|
- // 计算前的区段下标
|
|
|
- int index = optionIndex.getAsInt();
|
|
|
- // 计算后的区段下标
|
|
|
- int currentIndex = selectionAllList.size() - 1;
|
|
|
// 获取当前的时间
|
|
|
- LocalTime now = LocalTime.now();
|
|
|
+ long nowMillis = System.currentTimeMillis();
|
|
|
// 1、距离上一个周期 列车运行的距离
|
|
|
- BigDecimal decimal = trainItem.getDistance(now);
|
|
|
- if (NumberUtil.isLessOrEqual(decimal, BigDecimal.ZERO) && Math.abs(Duration.between(trainItem.getUpdateTime(), now).getSeconds()) <= TrainConstant.STOP_TIME_SECOND) {
|
|
|
+ BigDecimal distance = train.getDistance(nowMillis);
|
|
|
+ if (NumberUtil.isLessOrEqual(distance, BigDecimal.ZERO) && Math.abs(nowMillis - train.getUpdateTimeMillis()) <= TrainConstant.STOP_TIME_SECOND * 1000) {
|
|
|
// 停车状态 时间不足30S
|
|
|
- return trainItem;
|
|
|
- } else if (NumberUtil.isGreater(decimal, BigDecimal.ZERO)) {
|
|
|
- // 向前行驶计算
|
|
|
- BigDecimal kilometer = trainItem.getHeadKilometer();
|
|
|
- for (int i = index; i < selectionAllList.size(); i++) {
|
|
|
- SelectionEntity entity = selectionAllList.get(i);
|
|
|
- // if (i == index) {
|
|
|
- // decimal = decimal.add(entity.getEndKilometer().subtract(kilometer));
|
|
|
- // }
|
|
|
- BigDecimal d = entity.getEndKilometer().subtract(kilometer);
|
|
|
- if (NumberUtil.isGreaterOrEqual(d, decimal)) {
|
|
|
- currentIndex = i;
|
|
|
- break;
|
|
|
- }
|
|
|
- kilometer = entity.getEndKilometer();
|
|
|
- decimal = decimal.subtract(d);
|
|
|
+ return train;
|
|
|
+ }
|
|
|
+ if (NumberUtil.isGreater(distance, BigDecimal.ZERO)) {
|
|
|
+ Optional<SectionItem> sectionOptional = StreamUtils.filter(sectionList, item -> item.getSectionId().equals(train.getBlockId())).stream().findFirst();
|
|
|
+ if (sectionOptional.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException(String.format("没有找到当前列车的所在的区段, 区段Name: %s, offset: %s", train.getBlockNo(), train.getOffset()));
|
|
|
}
|
|
|
- for (int i = index; i <= currentIndex; i++) {
|
|
|
- SelectionEntity entity = selectionAllList.get(i);
|
|
|
- if (stopSignalMap.containsKey(entity.getId())) {
|
|
|
- // 期间存在停车标 立即停车
|
|
|
- trainItem.setDirection(entity.getDirection());
|
|
|
- trainItem.setUpdateTime(now);
|
|
|
- trainItem.setSpeed(new BigDecimal("0.00"));
|
|
|
- trainItem.setAcceleration(new BigDecimal("0.00"));
|
|
|
- BigDecimal kilometerStop = entity.getStartKilometer().add(stopSignalMap.get(entity.getId()).getSelectionOff());
|
|
|
- trainItem.setKilometer(kilometerStop);
|
|
|
- return trainItem;
|
|
|
+ List<SectionItem> list = this.getSectionByDistance(sectionList, train.getBlockId(), train.getOffset(), distance);
|
|
|
+ log.info("列车预计行驶的区段:{}", list.stream().map(SectionItem::getSectionName).collect(Collectors.joining(",")));
|
|
|
+ // 停车标数据
|
|
|
+ HashMap<Long, SignStopEntity> stopSignalMap = selectionService.getStopSignalMap(list.stream().map(SectionItem::getSectionId).collect(Collectors.toList()));
|
|
|
+ SectionItem currentSection = sectionOptional.get();
|
|
|
+ BigDecimal currentSectionOffset = train.getOffset();
|
|
|
+ BigDecimal currentDistance = distance.abs();
|
|
|
+ for (SectionItem sectionItem : list) {
|
|
|
+ BigDecimal offsetDistance = NumberUtil.add(currentSectionOffset, NumberUtil.mul(sectionItem.getDirection().getFactor(), currentDistance));
|
|
|
+ SignStopEntity stopSign = stopSignalMap.get(sectionItem.getSectionId());
|
|
|
+ if (null != stopSign && NumberUtil.isIn(stopSign.getSelectionOff(), NumberUtil.min(currentSectionOffset, offsetDistance), NumberUtil.max(currentSectionOffset, offsetDistance))) {
|
|
|
+ // 存在停车标
|
|
|
+ train.setSpeed(BigDecimal.ZERO);
|
|
|
+ train.setAcceleration(BigDecimal.ZERO);
|
|
|
+ train.setBlockId(sectionItem.getSectionId());
|
|
|
+ train.setBlockNo(sectionItem.getSectionName());
|
|
|
+ train.setOffset(stopSign.getSelectionOff());
|
|
|
+ train.setDirection(sectionItem.getDirection());
|
|
|
+ train.setUpdateTimeMillis(nowMillis);
|
|
|
+ return train;
|
|
|
}
|
|
|
+ currentSection = sectionItem;
|
|
|
+ currentDistance = NumberUtil.add(currentSectionOffset, NumberUtil.mul(sectionItem.getDirection().getFactor(), currentDistance)).abs();
|
|
|
+ currentSectionOffset = BigDecimal.ZERO;
|
|
|
}
|
|
|
- SelectionEntity currentSelection = selectionAllList.get(currentIndex);
|
|
|
- trainItem.setHeadKilometer(now, currentSelection.getStartKilometer().add(decimal), currentSelection);
|
|
|
+ train.setSpeed(CalculatorUtil.velocity(train.getSpeed(), train.getAcceleration(), train.getUpdateTimeMillis(), nowMillis).abs());
|
|
|
+ train.setBlockId(currentSection.getSectionId());
|
|
|
+ train.setBlockNo(currentSection.getSectionName());
|
|
|
+ train.setOffset(currentDistance);
|
|
|
+ train.setDirection(currentSection.getDirection());
|
|
|
+ train.setUpdateTimeMillis(nowMillis);
|
|
|
}
|
|
|
// 计算MA
|
|
|
- BigDecimal distanceMa = new BigDecimal("0.00");
|
|
|
- for (int i = currentIndex; i < selectionAllList.size(); i++) {
|
|
|
- SelectionEntity entity = selectionAllList.get(i);
|
|
|
- if (!stopSignalMap.containsKey(entity.getId())) {
|
|
|
- distanceMa = distanceMa.add(entity.getSelectionLength());
|
|
|
- continue;
|
|
|
- }
|
|
|
- SignStopEntity signStop = stopSignalMap.get(entity.getId());
|
|
|
- distanceMa = distanceMa.add(signStop.getSelectionOff());
|
|
|
- break;
|
|
|
- }
|
|
|
- BigDecimal distance = CalculatorUtil.distanceStopLimit(trainItem.getSpeed());
|
|
|
+ BigDecimal distanceMa = this.getDistanceMa(sectionList, train.getBlockId(), train.getOffset());
|
|
|
+ BigDecimal distanceLimit = CalculatorUtil.distanceStopLimit(train.getSpeed());
|
|
|
// log.info("列车信息:列车:{},速度:{},加速度:{}, 滑行距离:{}", trainItem.getTrainId(), trainItem.getSpeed(), trainItem.getAcceleration(), distance);
|
|
|
- if (NumberUtil.isLessOrEqual(distanceMa, distance)) {
|
|
|
+ if (NumberUtil.isLessOrEqual(distanceMa, distanceLimit)) {
|
|
|
// 紧急制动
|
|
|
- trainItem.setAcceleration(TrainConstant.DECELERATION_LIMIT);
|
|
|
- } else if (NumberUtil.isGreaterOrEqual(trainItem.getSpeed(), TrainConstant.SPEED_LIMIT_EBI)) {
|
|
|
+ train.setAcceleration(TrainConstant.DECELERATION_LIMIT);
|
|
|
+ } else if (NumberUtil.isGreaterOrEqual(train.getSpeed(), TrainConstant.SPEED_LIMIT_EBI)) {
|
|
|
// 速度超过限额 制动
|
|
|
- trainItem.setAcceleration(TrainConstant.DECELERATION_LIMIT);
|
|
|
- } else if (NumberUtil.isGreaterOrEqual(trainItem.getSpeed(), TrainConstant.SPEED_LIMIT_SBI)) {
|
|
|
+ train.setAcceleration(TrainConstant.DECELERATION_LIMIT);
|
|
|
+ } else if (NumberUtil.isGreaterOrEqual(train.getSpeed(), TrainConstant.SPEED_LIMIT_SBI)) {
|
|
|
// 速度超过推荐速度 惰性
|
|
|
- trainItem.setAcceleration(TrainConstant.SLOW_DOWN_LIMIT);
|
|
|
+ train.setAcceleration(TrainConstant.SLOW_DOWN_LIMIT);
|
|
|
} else {
|
|
|
// 加速
|
|
|
- trainItem.setAcceleration(TrainConstant.ACCELERATION_LIMIT);
|
|
|
+ train.setAcceleration(TrainConstant.ACCELERATION_LIMIT);
|
|
|
+ }
|
|
|
+ return train;
|
|
|
+ }
|
|
|
+
|
|
|
+ private BigDecimal getDistanceMa(List<SectionItem> list, Long sectionId, BigDecimal offset) {
|
|
|
+ BigDecimal ma = BigDecimal.ZERO;
|
|
|
+ Optional<SectionItem> sectionItem = list.stream().filter(item -> item.getSectionId().equals(sectionId)).findFirst();
|
|
|
+ if (sectionItem.isPresent()) {
|
|
|
+ SectionItem item = sectionItem.get();
|
|
|
+ ma = ma.add(switch (item.getDirection()) {
|
|
|
+ case UP -> NumberUtil.equals(offset, BigDecimal.ZERO) ? item.getSectionLength().abs() : offset.abs();
|
|
|
+ case DOWN -> NumberUtil.sub(sectionItem.get().getSectionLength(), offset).abs();
|
|
|
+ });
|
|
|
+ ma = ma.add(getDistanceMa(list, sectionItem.get().getSectionNextId(), BigDecimal.ZERO));
|
|
|
+ }
|
|
|
+ return ma;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取列车心行驶后的区段
|
|
|
+ *
|
|
|
+ * @param list 区段数据
|
|
|
+ * @param sectionId 当前的区段
|
|
|
+ * @param offset 当前偏移量
|
|
|
+ * @param distance 行驶的距离
|
|
|
+ * @return 行驶后所在的区段
|
|
|
+ */
|
|
|
+ private List<SectionItem> getSectionByDistance(LinkedList<SectionItem> list, Long sectionId, BigDecimal offset, BigDecimal distance) {
|
|
|
+ Optional<SectionItem> sectionOptional = StreamUtils.filter(list, item -> item.getSectionId().equals(sectionId)).stream().findFirst();
|
|
|
+ if (sectionOptional.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException(String.format("没有找到当前列车的所在的区段, 区段Id: %s, offset: %s", sectionId, offset));
|
|
|
+ }
|
|
|
+ List<SectionItem> resultList = new ArrayList<>();
|
|
|
+ SectionItem sectionItem = sectionOptional.get();
|
|
|
+ BigDecimal offsetDistance = NumberUtil.add(offset, NumberUtil.mul(distance, sectionItem.getDirection().getFactor()));
|
|
|
+ if (!NumberUtil.isContain(offsetDistance, BigDecimal.ZERO, sectionItem.getSectionLength())) {
|
|
|
+ // 不在当前的区段
|
|
|
+ BigDecimal distanceNext = switch (sectionItem.getDirection()) {
|
|
|
+ case UP -> offsetDistance.abs();
|
|
|
+ case DOWN -> NumberUtil.sub(offsetDistance, sectionItem.getSectionLength());
|
|
|
+ };
|
|
|
+ return getSectionByDistance(list, sectionItem.getSectionNextId(), new BigDecimal("0.00"), distanceNext);
|
|
|
+ } else {
|
|
|
+ resultList.add(sectionItem);
|
|
|
+ return CollUtil.reverse(resultList);
|
|
|
}
|
|
|
- return trainItem;
|
|
|
}
|
|
|
}
|