zhanghaijun před 2 roky
rodič
revize
ca356ae458

+ 2 - 0
train-common/train-common-core/src/main/java/top/haijunit/common/config/AsyncConfig.java

@@ -6,6 +6,7 @@ import org.springframework.boot.autoconfigure.AutoConfiguration;
 import org.springframework.scheduling.annotation.AsyncConfigurer;
 import org.springframework.scheduling.annotation.EnableAsync;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+import top.haijunit.common.utils.ErrorUtil;
 
 import java.lang.reflect.Method;
 import java.util.concurrent.Executor;
@@ -40,6 +41,7 @@ public class AsyncConfig implements AsyncConfigurer {
     public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
         return (Throwable ex, Method method, Object... params) -> {
             log.error("glob async exception: {},method: {}, params: {}", ex.getMessage(), method.getName(), params);
+            ErrorUtil.errorInfoToString(ex);
         };
     }
 }

+ 42 - 6
train-server/src/main/java/top/haijunit/train/domain/entity/SelectionEntity.java

@@ -1,13 +1,16 @@
 package top.haijunit.train.domain.entity;
 
-import jakarta.persistence.*;
+import jakarta.persistence.Entity;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
 import lombok.Data;
 import top.haijunit.train.domain.constant.DirectionEnum;
 import top.haijunit.train.domain.constant.RouteStatusEnum;
+import top.haijunit.train.utils.NumberUtil;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.util.List;
 
 /**
  * @author zhang
@@ -42,11 +45,44 @@ public class SelectionEntity implements Serializable {
     // 是否是道岔
     private boolean isSwitch;
 
-    // @OneToMany
-    // @JoinColumn(name = "selectionId")
-    // private List<SignStopEntity> signStopList;
-
     public BigDecimal getEndKilometer() {
         return this.startKilometer.add(this.selectionLength);
     }
+
+    /**
+     * @param kilometer 公里标
+     * @param direction 方向
+     * @return 公里标是否在当前区段中
+     */
+    public boolean isContain(BigDecimal kilometer, DirectionEnum direction) {
+        if (this.getDirection() != direction) {
+            return false;
+        }
+        return NumberUtil.isContain(kilometer, this.getStartKilometer(), this.getEndKilometer());
+    }
+
+    /**
+     * @param tailKilometer 公里标
+     * @param direction     方向
+     * @return 是否已经通过当前区段
+     */
+    public boolean isPass(BigDecimal headKilometer, BigDecimal tailKilometer, DirectionEnum direction) {
+        if (this.isContain(headKilometer, direction)) {
+            return false;
+        }
+        if (isContain(tailKilometer, direction)) {
+            return false;
+        }
+        return switch (direction) {
+            // 上行 车尾 <= 区段的开始里程
+            case UP -> NumberUtil.isLessOrEqual(tailKilometer, this.getStartKilometer());
+            // 下行 车位 > 区段的结束里程
+            case DOWN -> NumberUtil.isGreater(tailKilometer, this.getEndKilometer());
+        };
+    }
+
+    public boolean isNonPass(BigDecimal headKilometer, BigDecimal tailKilometer, DirectionEnum direction) {
+        return !isPass(headKilometer, tailKilometer, direction);
+    }
+
 }

+ 46 - 15
train-server/src/main/java/top/haijunit/train/service/SelectionService.java

@@ -1,15 +1,20 @@
 package top.haijunit.train.service;
 
-import cn.hutool.core.util.StrUtil;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import top.haijunit.train.domain.constant.DirectionEnum;
+import top.haijunit.train.domain.constant.RouteStatusEnum;
+import top.haijunit.train.domain.entity.RouteEntity;
 import top.haijunit.train.domain.entity.SelectionEntity;
+import top.haijunit.train.domain.repository.RouteRepository;
 import top.haijunit.train.domain.repository.SelectionRepository;
+import top.haijunit.train.simulation.domain.RouteItem;
 import top.haijunit.train.utils.NumberUtil;
 
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -23,6 +28,7 @@ import java.util.List;
 public class SelectionService {
 
     private final SelectionRepository selectionRepository;
+    private final RouteRepository routeRepository;
 
     // 根据公里标和上下行获取区段的问题
     public SelectionEntity getSelection(BigDecimal kilometer, DirectionEnum direction) {
@@ -34,20 +40,45 @@ public class SelectionService {
         return null;
     }
 
-    // 当前进路是否在该区段之后
-    public boolean isPass(Long selectionId, List<Long> list) {
-        SelectionEntity entity = selectionRepository.findById(selectionId).orElseThrow(() -> new IllegalArgumentException(StrUtil.format("未找到该区段, ID:{}", selectionId)));
-        DirectionEnum direction = entity.getDirection();
-        List<SelectionEntity> entityList = selectionRepository.findByDirection(direction, list);
-        return switch (direction) {
-            case UP -> {
-                BigDecimal min = entityList.stream().map(SelectionEntity::getEndKilometer).min(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
-                yield NumberUtil.isLess(entity.getEndKilometer(), min);
-            }
-            case DOWN -> {
-                BigDecimal max = entityList.stream().map(SelectionEntity::getEndKilometer).max(BigDecimal::compareTo).orElse(BigDecimal.ZERO);
-                yield NumberUtil.isGreaterOrEqual(entity.getEndKilometer(), max);
+    /**
+     * 获取进路中的区段
+     *
+     * @param route 进路
+     * @return 进路中的区段
+     */
+    public LinkedList<SelectionEntity> getSelectionList(RouteItem route) {
+        ArrayList<Long> seleIds = routeRepository.findById(route.getId()).map(RouteEntity::getSelectionList).orElseThrow(() -> new IllegalArgumentException("进路不存在,进路Id:" + route.getId()));
+        List<SelectionEntity> selectionList = selectionRepository.findAllById(seleIds);
+        for (SelectionEntity entity : selectionList) {
+            entity.setStatus(RouteStatusEnum.LOCK);
+        }
+        return new LinkedList<>(selectionList);
+    }
+
+    /**
+     * 获取列车当前进路中未驶过的区段
+     *
+     * @param route         进路
+     * @param headKilometer 车头
+     * @param tailKilometer 车位
+     * @return 进路中未驶过的区段
+     */
+    public LinkedList<SelectionEntity> getSelectionList(RouteItem route, BigDecimal headKilometer, BigDecimal tailKilometer) {
+        ArrayList<Long> seleIds = routeRepository.findById(route.getId()).map(RouteEntity::getSelectionList).orElseThrow(() -> new IllegalArgumentException("进路不存在,进路Id:" + route.getId()));
+        List<SelectionEntity> selectionList = selectionRepository.findAllById(seleIds);
+        LinkedList<SelectionEntity> list = new LinkedList<>();
+        for (SelectionEntity entity : selectionList) {
+            if (entity.isNonPass(headKilometer, tailKilometer, route.getDirection())) {
+                // 列车还未驶过该区段
+                if (entity.isContain(headKilometer, route.getDirection()) || entity.isContain(tailKilometer, route.getDirection())) {
+                    // 车头或者车尾在区段内,则是占用状态
+                    entity.setStatus(RouteStatusEnum.OCCUPY);
+                } else {
+                    entity.setStatus(RouteStatusEnum.LOCK);
+                }
+                list.addLast(entity);
             }
-        };
+        }
+        return list;
     }
 }

+ 40 - 11
train-server/src/main/java/top/haijunit/train/simulation/domain/RouteItem.java

@@ -31,6 +31,8 @@ public class RouteItem implements Serializable {
     private String name;
     // 触发点位置
     private BigDecimal trigPosition;
+    // 进路方向
+    private DirectionEnum direction;
     // 进路状态
     private RouteStatusEnum routeStatus;
     // 下一条进路Id
@@ -47,6 +49,7 @@ public class RouteItem implements Serializable {
         this.id = route.getId();
         this.name = route.getName();
         this.trigPosition = route.getTrigPosition();
+        this.direction = route.getDirection();
         this.routeStatus = RouteStatusEnum.IDLE;
         this.nextId = route.getNextId();
         this.switchStateMap = route.getSwitchMap();
@@ -69,24 +72,53 @@ public class RouteItem implements Serializable {
         return this.getSelectionList().stream().map(SelectionEntity::getId).collect(Collectors.toCollection(LinkedList::new));
     }
 
-    public boolean isContain(Long selectionId) {
-        return this.getSelectionIds().contains(selectionId);
+    /**
+     * @param kilometer 公里标
+     * @param direction 方向
+     * @return 公里标是否在当前进路中
+     */
+    public boolean isContain(BigDecimal kilometer, DirectionEnum direction) {
+        if (this.getDirection() != direction) {
+            return false;
+        }
+        return NumberUtil.isContain(kilometer, this.getHeadKilometer(), this.getTailKilometer());
     }
 
-    public boolean isTrigger(BigDecimal kilometer, DirectionEnum direction) {
+    /**
+     * @param tailKilometer 公里标
+     * @param direction     方向
+     * @return 是否已经通过当前进路
+     */
+    public boolean isPass(BigDecimal headKilometer, BigDecimal tailKilometer, DirectionEnum direction) {
+        if (isContain(headKilometer, direction)) {
+            return true;
+        }
+        if (isContain(tailKilometer, direction)) {
+            return true;
+        }
         return switch (direction) {
-            case UP -> NumberUtil.isContain(kilometer, this.getHeadKilometer(), this.getTrigPosition());
-            case DOWN -> NumberUtil.isContain(kilometer, this.getTrigPosition(), this.getTailKilometer());
+            // 上行 车尾 <= 进路的开始里程
+            case UP -> NumberUtil.isLessOrEqual(tailKilometer, this.getHeadKilometer());
+            // 下行 车位 > 进路的结束里程
+            case DOWN -> NumberUtil.isGreater(tailKilometer, this.getTailKilometer());
         };
     }
 
-    public boolean isPass(BigDecimal tailKilometer, DirectionEnum direction) {
+    // 是否触发下一个进路
+    public boolean isTrigger(BigDecimal kilometer, DirectionEnum direction) {
+        if (!isContain(kilometer, direction)) {
+            // 车头不在进路中,直接返回false
+            return false;
+        }
         return switch (direction) {
-            case UP -> NumberUtil.isLessOrEqual(tailKilometer, this.getHeadKilometer());
-            case DOWN -> NumberUtil.isGreater(tailKilometer, this.getTailKilometer());
+            // 上行 进路开始公里数 <= 车头公里数 <= 触发点公里数
+            case UP -> NumberUtil.isIn(kilometer, this.getHeadKilometer(), this.getTrigPosition());
+            // 下行 触发点公里数 <= 车头公里数 <= 进路结束公里数
+            case DOWN -> NumberUtil.isIn(kilometer, this.getTrigPosition(), this.getTailKilometer());
         };
     }
 
+
     // 是否包含道岔
     public boolean isEmptySwitch() {
         return this.getSwitchStateMap().isEmpty();
@@ -95,8 +127,5 @@ public class RouteItem implements Serializable {
     // 改变状态
     public void setStatus(RouteStatusEnum status){
         this.routeStatus = status;
-        for (SelectionEntity selectionEntity : this.getSelectionList()) {
-            selectionEntity.setStatus(status);
-        }
     }
 }

+ 14 - 1
train-server/src/main/java/top/haijunit/train/simulation/train/RouteComputerService.java

@@ -5,6 +5,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import top.haijunit.common.utils.StreamUtils;
 import top.haijunit.train.domain.constant.RouteStatusEnum;
+import top.haijunit.train.domain.entity.SelectionEntity;
 import top.haijunit.train.service.RouteService;
 import top.haijunit.train.service.SelectionService;
 import top.haijunit.train.service.SwitchService;
@@ -25,6 +26,7 @@ public class RouteComputerService {
 
     private final SwitchService switchService;
     private final RouteService routeService;
+    private final SelectionService selectionService;
 
     public void run(TrainItem train) {
         // 更新进路共享数据
@@ -44,12 +46,22 @@ public class RouteComputerService {
             log.info("初始化,没有找到当前的进路信息,列车: {}", train.getTrainId());
             // 初始化为空处理
             currentRoute = routeService.getCurrentRoute(train);
+            if (null == currentRoute) {
+                log.error("--- 计算列车的初始位置错误:方向:{},列车位置:{}", train.getDirection(), train.getKilometer());
+                return routeList;
+            }
+            LinkedList<SelectionEntity> list = selectionService.getSelectionList(currentRoute, train.getHeadKilometer(), train.getTailKilometer());
+            if (list.isEmpty()) {
+                log.error("--- 进路的区段计算错误:进路:{},列车位置:{}", currentRoute.getId(), train.getKilometer());
+                return routeList;
+            }
             currentRoute.setStatus(RouteStatusEnum.OCCUPY);
+            currentRoute.setSelectionList(list);
             routeList = new LinkedList<>();
             routeList.add(currentRoute);
         }
         // 释放资源 进路的全部区段的公里标 小于 当前列车所在的区段
-        routeList.removeIf(item -> item.isPass(train.getTailKilometer(), train.getDirection()));
+        routeList.removeIf(item -> item.isPass(train.getHeadKilometer(), train.getTailKilometer(), train.getDirection()));
         // 开始计算下一跳进路
         if (!currentRoute.isTrigger(train.getHeadKilometer(), train.getDirection())) {
             // 没有触发进路
@@ -71,6 +83,7 @@ public class RouteComputerService {
         }
         // c)出清(如果包括道岔,道岔已就位),将进路至于锁闭状态
         nextRoute.setStatus(RouteStatusEnum.LOCK);
+        nextRoute.setSelectionList(selectionService.getSelectionList(nextRoute));
         routeList.addLast(nextRoute);
         return routeList;
     }

+ 1 - 6
train-server/src/main/java/top/haijunit/train/simulation/train/TrainSimulationHolder.java

@@ -54,12 +54,7 @@ public class TrainSimulationHolder {
     public static RouteItem getCurrentRoute(TrainItem train) {
         Assert.notNull(train, "获取当前列车所在的进路,train:{}", train.getTrainNo());
         LinkedList<RouteItem> routeList = getRouteList(train.getTrainId());
-        for (RouteItem route : routeList) {
-            if (route.isContain(train.getSelectionId())) {
-                return route;
-            }
-        }
-        return null;
+        return routeList.stream().filter(item -> item.isContain(train.getHeadKilometer(), train.getDirection())).findFirst().orElse(null);
     }
 
     public static boolean isRouteIdle(Long routeId) {