zhanghaijun 2 éve%!(EXTRA string=óta)
szülő
commit
581b3f15e5

+ 0 - 21
train-server/src/main/java/top/haijunit/train/domain/constant/CacheNames.java

@@ -1,21 +0,0 @@
-package top.haijunit.train.domain.constant;
-
-/**
- * @author zhanghaijun
- * @date 2023/12/18 13:20
- * @description 缓存组名称常量
- * <p>
- * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
- * <p>
- * ttl 过期时间 如果设置为0则不过期 默认为0
- * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
- * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
- * <p>
- * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
- */
-public interface CacheNames {
-
-    String SECTION = "section";
-    String LICENSE = "license";
-
-}

+ 38 - 0
train-server/src/main/java/top/haijunit/train/domain/constant/ControlConstant.java

@@ -0,0 +1,38 @@
+package top.haijunit.train.domain.constant;
+
+/**
+ * @author zhanghaijun
+ * @date 2024/3/15 22:16
+ * @description [一句话描述该类的功能]
+ * @version 1.0
+ */
+public interface ControlConstant {
+
+    /**
+     * 屏蔽门的控制
+     */
+    interface DoorControl {
+        String LCW = "LCW";
+        String PSL = "PSL";
+        String IBP = "IBP";
+        String AUTO = "AUTO";
+    }
+
+    /**
+     * @author zhanghaijun
+     * @date 2023/12/18 13:20
+     * @description 缓存组名称常量
+     * <p>
+     * key 格式为 cacheNames#ttl#maxIdleTime#maxSize
+     * <p>
+     * ttl 过期时间 如果设置为0则不过期 默认为0
+     * maxIdleTime 最大空闲时间 根据LRU算法清理空闲数据 如果设置为0则不检测 默认为0
+     * maxSize 组最大长度 根据LRU算法清理溢出数据 如果设置为0则无限长 默认为0
+     * <p>
+     * 例子: test#60s、test#0#60s、test#0#1m#1000、test#1h#0#500
+     */
+    interface CacheNames {
+        String SECTION = "section";
+        String LICENSE = "license";
+    }
+}

+ 6 - 5
train-server/src/main/java/top/haijunit/train/domain/entity/PlatformDoorEntity.java

@@ -24,12 +24,13 @@ public class PlatformDoorEntity implements Serializable {
     private String name;
     // 门的状态
     private DoorStatusEnum status;
-
-    // 所属站台
-    private Long stationId;
-    // 所属平台
-    private Long platformId;
     // 站台门方向
     private DirectionEnum direction;
+    // 所属平台
+    private Long platformId;
+    // 所属站台
+    private Long stationId;
+    // 门的控制源 LCW > PSL > IBP > AUTO
+    private String controlOrigin;
 
 }

+ 15 - 2
train-server/src/main/java/top/haijunit/train/domain/repository/PlatformDoorRepository.java

@@ -1,6 +1,7 @@
 package top.haijunit.train.domain.repository;
 
 import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
 import org.springframework.stereotype.Repository;
 import top.haijunit.train.domain.constant.DirectionEnum;
 import top.haijunit.train.domain.entity.PlatformDoorEntity;
@@ -15,7 +16,19 @@ import java.util.List;
 @Repository
 public interface PlatformDoorRepository extends JpaRepository<PlatformDoorEntity, Long> {
 
-    List<PlatformDoorEntity> findByStationIdAndDirection(Long stationId, DirectionEnum direction);
+    /**
+     * 根据平台id和控制来源查询屏蔽门
+     */
+    @Query("SELECT d FROM db_platform_door d WHERE d.platformId = ?1 AND d.controlOrigin IN (?2)")
+    List<PlatformDoorEntity> getPlatformIdAndControlOriginIn(Long platformId, List<String> controlOrigins);
+
+    /**
+     * 根据站台id和方向查询屏蔽门
+     */
+    @Query("SELECT d FROM db_platform_door d WHERE d.stationId = ?1 AND d.direction=?2 AND d.controlOrigin IN (?3)")
+    List<PlatformDoorEntity> getStationIdAndDirection(Long stationId, DirectionEnum direction, List<String> controlOrigins);
+
+    @Query("SELECT d FROM db_platform_door d WHERE d.stationId = ?1 AND d.direction = ?2 AND d.status = 1")
+    List<PlatformDoorRepository> getDoorOpen(Long stationId, DirectionEnum direction);
 
-    List<PlatformDoorEntity> findByPlatformId(Long platformId);
 }

+ 1 - 1
train-server/src/main/java/top/haijunit/train/listener/MessageEventListener.java

@@ -68,7 +68,7 @@ public class MessageEventListener implements ApplicationListener<MessageEvent> {
                 case PSL_LOCK, PSL_CONTROL, PSL_DOOR_OPEN, PSL_DOOR_CLOSE:
                     log.info("接受消息: {}, message: {}", entity.getCode(), message);
                     // 屏蔽门控制指令
-                    doorService.updateIbpControl(entity);
+                    doorService.updateDoorControl(entity);
                     break;
                 case SITUATION_CLIENT_INFO:
                 case SITUATION_WATER_LEVEL:

+ 5 - 5
train-server/src/main/java/top/haijunit/train/service/LicenseServiceImpl.java

@@ -12,7 +12,7 @@ import top.haijunit.common.domain.LicenseItem;
 import top.haijunit.common.domain.LicenseService;
 import top.haijunit.common.domain.ResultData;
 import top.haijunit.common.exception.ServiceException;
-import top.haijunit.train.domain.constant.CacheNames;
+import top.haijunit.train.domain.constant.ControlConstant;
 import top.haijunit.train.utils.LicenseUtil;
 
 import java.io.File;
@@ -33,7 +33,7 @@ public class LicenseServiceImpl implements LicenseService {
      * @return 机器码+校验位
      */
     @Override
-    @Cacheable(cacheNames = CacheNames.LICENSE, key = "'MachineStr'")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.LICENSE, key = "'MachineStr'")
     public String getMachineStr() {
         String machineCode = getMachineCode();
         String machineStr = machineCode + LicenseUtil.MARK + LicenseUtil.generateCheckKey(machineCode);
@@ -42,14 +42,14 @@ public class LicenseServiceImpl implements LicenseService {
         return machineStr;
     }
 
-    @Cacheable(cacheNames = CacheNames.LICENSE, key = "'MachineCode'")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.LICENSE, key = "'MachineCode'")
     public String getMachineCode() {
         return LicenseUtil.getMachineCode();
     }
 
 
     @Override
-    @Cacheable(value = CacheNames.LICENSE, key = "'LicenseTime'")
+    @Cacheable(value = ControlConstant.CacheNames.LICENSE, key = "'LicenseTime'")
     public LicenseItem getLicenseItem() {
         File file = new File(LicenseUtil.LICENSE_FILE);
         if (!file.exists()) {
@@ -67,7 +67,7 @@ public class LicenseServiceImpl implements LicenseService {
     }
 
     // 校验授权码
-    @CacheEvict(value = CacheNames.LICENSE, key = "'LicenseTime'")
+    @CacheEvict(value = ControlConstant.CacheNames.LICENSE, key = "'LicenseTime'")
     public ResultData<String> licenseVerify(String licenseStr, String machineStr) {
         if (machineStr.contains(LicenseUtil.MARK)) {
             String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);

+ 89 - 25
train-server/src/main/java/top/haijunit/train/service/PlatformDoorService.java

@@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import top.haijunit.train.device.domain.CommandEnum;
 import top.haijunit.train.device.entity.DeviceEntity;
+import top.haijunit.train.domain.constant.ControlConstant;
 import top.haijunit.train.domain.constant.DirectionEnum;
 import top.haijunit.train.domain.constant.DoorStatusEnum;
 import top.haijunit.train.domain.entity.PlatformDoorEntity;
@@ -16,6 +17,7 @@ import top.haijunit.train.domain.repository.PlatformDoorRepository;
 import top.haijunit.train.utils.TrainWebSocketUtils;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -30,79 +32,141 @@ public class PlatformDoorService {
 
     private final PlatformDoorRepository platformDoorRepository;
 
-    public void updateIbpControl(DeviceEntity entity) {
+    /**
+     * 列车行驶进入站台判断是否有屏蔽门打开的情况,需要情急停车
+     */
+    public boolean existDoorOpen(Long stationId, DirectionEnum direction) {
+        return !platformDoorRepository.getDoorOpen(stationId, direction).isEmpty();
+    }
+
+    public void updateDoorControl(DeviceEntity entity) {
         switch (entity.getCommand()) {
             case AFC_IBP:
             case PSL_LOCK:
                 break;
             case IBP_CONTROL:
+                if (updateDoorControl(ControlConstant.DoorControl.IBP, entity.getStatus() == 0, Convert.toLong(entity.getCode(), 0L))) {
+                    log.info("更新站台门控制: {} -> {}", entity.getStatus(), ControlConstant.DoorControl.IBP);
+                }
+                break;
             case PSL_CONTROL:
-                // 更新权限
+                if (updateDoorControl(ControlConstant.DoorControl.PSL, entity.getStatus() == 0, Convert.toLong(entity.getCode(), 0L))) {
+                    log.info("更新站台门控制: {} -> {}", entity.getStatus(), ControlConstant.DoorControl.PSL);
+                }
                 break;
             case IBP_DOOR_OPEN:
+                updateDoorStatus(ControlConstant.DoorControl.IBP, DoorStatusEnum.OPEN, Convert.toLong(entity.getCode(), 0L));
+                break;
             case PSL_DOOR_OPEN:
-                sendDoorStatus(DoorStatusEnum.OPEN, Convert.toLong(entity.getCode(), 0L));
+                updateDoorStatus(ControlConstant.DoorControl.PSL, DoorStatusEnum.OPEN, Convert.toLong(entity.getCode(), 0L));
                 break;
             case IBP_DOOR_CLOSE:
+                updateDoorStatus(ControlConstant.DoorControl.IBP, DoorStatusEnum.CLOSE, Convert.toLong(entity.getCode(), 0L));
+                break;
             case PSL_DOOR_CLOSE:
-                sendDoorStatus(DoorStatusEnum.CLOSE, Convert.toLong(entity.getCode(), 0L));
+                updateDoorStatus(ControlConstant.DoorControl.PSL, DoorStatusEnum.CLOSE, Convert.toLong(entity.getCode(), 0L));
                 break;
             case PLATFORM_DOOR_LCW:
                 // LCW 更新屏蔽门
-                List<PlatformDoorEntity> list = new ArrayList<>();
-                list.add(platformDoorRepository.getReferenceById(Convert.toLong(entity.getCode(), 0L)));
+                Long doorId = Convert.toLong(entity.getCode(), 0L);
                 switch (entity.getStatus()) {
                     case 1:
-                        sendDoorStatus(DoorStatusEnum.OPEN, list);
+                        updateDoorStatus(ControlConstant.DoorControl.LCW, DoorStatusEnum.OPEN, Collections.singletonList(platformDoorRepository.getReferenceById(doorId)));
                         break;
                     case 2:
-                        sendDoorStatus(DoorStatusEnum.CLOSE, list);
+                        updateDoorStatus(ControlConstant.DoorControl.LCW, DoorStatusEnum.CLOSE, Collections.singletonList(platformDoorRepository.getReferenceById(doorId)));
                         break;
                     case 3:
-                        sendDoorStatus(DoorStatusEnum.DISABLE, list);
+                        updateDoorStatus(ControlConstant.DoorControl.LCW, DoorStatusEnum.DISABLE, Collections.singletonList(platformDoorRepository.getReferenceById(doorId)));
                         break;
                 }
-
                 break;
         }
     }
 
+    /**
+     * 获取可操作的控制源
+     * 控制顺序:LCW > PSL > IBP > AUTO
+     */
+    private List<String> getControlOriginList(String origin) {
+        List<String> originList = new ArrayList<>();
+        switch (origin) {
+            case "LCW":
+                originList.add(origin);
+            case "PSL":
+                originList.add(origin);
+            case "IBP":
+                originList.add(origin);
+            case "AUTO":
+            default:
+                originList.add(origin);
+        }
+        return originList;
+    }
+
+    /**
+     * 更新对屏蔽门的控制
+     */
+    public boolean updateDoorControl(String origin, boolean effect, Long platformId) {
+        List<String> originList = getControlOriginList(origin);
+        List<PlatformDoorEntity> list = platformDoorRepository.getPlatformIdAndControlOriginIn(platformId, originList);
+        if (CollUtil.isEmpty(list)) {
+            return false;
+        }
+        list.forEach(entity -> {
+            entity.setControlOrigin(effect ? origin : ControlConstant.DoorControl.AUTO);
+            platformDoorRepository.save(entity);
+        });
+        return true;
+    }
+
     /**
      * 信号更新屏蔽门
-     * @param state 状态
-     * @param stationId 车站Id
-     * @param direction 方向
      */
-    public void sendDoorStatus(DoorStatusEnum state, Long stationId, DirectionEnum direction) {
-        sendDoorStatus(state, platformDoorRepository.findByStationIdAndDirection(stationId, direction));
+    public void updateDoorStatus(DoorStatusEnum state, Long stationId, DirectionEnum direction) {
+        updateDoorStatus(ControlConstant.DoorControl.AUTO, state, platformDoorRepository.getStationIdAndDirection(stationId, direction, getControlOriginList(ControlConstant.DoorControl.AUTO)));
     }
 
     /**
-     * IBP PSL 更新屏蔽门
-     * @param state 状态
-     * @param platformId 平台Id
+     * 更新屏蔽门状态
+     *
+     * @param origin 来源
+     * @param state    状态
+     * @param platformId 站台Id
      */
-    public List<PlatformDoorEntity> sendDoorStatus(DoorStatusEnum state, Long platformId) {
-        return sendDoorStatus(state, platformDoorRepository.findByPlatformId(platformId));
+    public void updateDoorStatus(String origin, DoorStatusEnum state, Long platformId) {
+        List<String> originList = getControlOriginList(origin);
+        List<PlatformDoorEntity> list = platformDoorRepository.getPlatformIdAndControlOriginIn(platformId, originList);
+        if (CollUtil.isEmpty(list)) {
+            log.error("没有找到屏蔽门,站台:{},控制源:{}", platformId, originList);
+            return;
+        }
+        updateDoorStatus(origin, state, list);
     }
 
-    private List<PlatformDoorEntity> sendDoorStatus(DoorStatusEnum state, List<PlatformDoorEntity> doorList) {
+
+    /**
+     * 信号更新屏蔽门
+     *
+     * @param state    状态
+     * @param doorList 站台门实体
+     */
+    private void updateDoorStatus(String origin, DoorStatusEnum state, List<PlatformDoorEntity> doorList) {
         List<PlatformDoorEntity> list = new ArrayList<>();
         for (PlatformDoorEntity entity : doorList) {
             if (entity.getStatus() == DoorStatusEnum.DISABLE) {
                 continue;
             }
             entity.setStatus(state);
+            entity.setControlOrigin(origin);
             list.add(entity);
             JSONObject doorData = JSONUtil.createObj();
             doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getNameStr(), Convert.toStr(entity.getId(), ""));
             doorData.set(CommandEnum.PLATFORM_DOOR_LOCK.getStatusStr(), state.getCode());
             TrainWebSocketUtils.sendMessage(CommandEnum.PLATFORM_DOOR_LOCK, doorData);
         }
-        if (CollUtil.isEmpty(list)) {
-            return list;
-        } else {
-            return platformDoorRepository.saveAllAndFlush(list);
+        if (CollUtil.isNotEmpty(list)) {
+            platformDoorRepository.saveAllAndFlush(list);
         }
     }
 }

+ 8 - 8
train-server/src/main/java/top/haijunit/train/service/SectionService.java

@@ -7,7 +7,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
 import top.haijunit.common.utils.StreamUtils;
-import top.haijunit.train.domain.constant.CacheNames;
+import top.haijunit.train.domain.constant.ControlConstant;
 import top.haijunit.train.domain.constant.DirectionEnum;
 import top.haijunit.train.domain.constant.SectionStatusEnum;
 import top.haijunit.train.domain.entity.RouteEntity;
@@ -40,20 +40,20 @@ public class SectionService {
      * 获取所有停车标信息
      * @return 停车标集合
      */
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'StopSignalMap'")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'StopSignalMap'")
     public HashMap<Long, SignStopEntity> getStopSignalMap() {
         List<SignStopEntity> list = signStopRepository.findAll();
         return list.stream().collect(HashMap::new, (map, entity) -> map.put(entity.getSelectionId(), entity), HashMap::putAll);
     }
 
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'id-'+#id")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'id-'+#id")
     public SectionItem getSectionItem(String id) {
         return StreamUtils.filter(getAllSection(), item -> {
             return item.getId().equals(id);
         }).stream().findFirst().orElseThrow(() -> new IllegalArgumentException(String.format("不存在该区段,uid: %s", id)));
     }
 
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'sectionId-'+#sectionId+'-direction-'+#direction.name()")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'sectionId-'+#sectionId+'-direction-'+#direction.name()")
     public SectionItem getSectionItem(Long sectionId, DirectionEnum direction) {
         List<SectionItem> filter = StreamUtils.filter(getAllSection(), item -> item.getSectionId().equals(sectionId));
         if (filter.size() == 1) {
@@ -64,12 +64,12 @@ public class SectionService {
         }).stream().findFirst().orElseThrow(() -> new IllegalArgumentException(String.format("不存在该区段,sectionId: %s", sectionId)));
     }
 
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'routeId-'+#routeId")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'routeId-'+#routeId")
     public List<SectionItem> getSectionAll(Long routeId) {
         return StreamUtils.filter(getAllSection(), item -> item.getRouteId().equals(routeId));
     }
 
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'routeIds-' + T(java.util.Objects).hash(#routeIds)")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'routeIds-' + T(java.util.Objects).hash(#routeIds)")
     public List<SectionItem> getSectionAll(Collection<Long> routeIds) {
         if (CollUtil.isEmpty(routeIds)) {
             return new ArrayList<>();
@@ -87,7 +87,7 @@ public class SectionService {
         return StreamUtils.filter(getAllSection(), item -> CollUtil.contains(routeIds, item.getRouteId()));
     }
 
-    @Cacheable(cacheNames = CacheNames.SECTION + "-RouteNextId", key = "'sectionId-'+#sectionId+'-direction-'+#direction.name()")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION + "-RouteNextId", key = "'sectionId-'+#sectionId+'-direction-'+#direction.name()")
     public Long getRouteNextId(Long sectionId, DirectionEnum direction) {
         SectionItem sectionItem = this.getSectionItem(sectionId, direction);
         RouteEntity route = routeRepository.findById(sectionItem.getRouteId()).orElseThrow(() -> new IllegalArgumentException(String.format("未找到进路数据,routeId: %s", sectionItem.getRouteId())));
@@ -103,7 +103,7 @@ public class SectionService {
      *
      * @return 全部进路的区段
      */
-    @Cacheable(cacheNames = CacheNames.SECTION, key = "'section_all'")
+    @Cacheable(cacheNames = ControlConstant.CacheNames.SECTION, key = "'section_all'")
     public List<SectionItem> getAllSection() {
         List<SectionItem> sectionList = new ArrayList<>();
         List<RouteEntity> routeList = routeRepository.findAll();

+ 25 - 5
train-server/src/main/java/top/haijunit/train/simulation/train/TrainComputerService.java

@@ -61,12 +61,18 @@ public class TrainComputerService {
         BigDecimal distance = train.getDistance(nowMillis);
 
         long timeDifference = Math.abs(nowMillis - train.getStopTimeMillis());
-        if (NumberUtil.isLessOrEqual(distance, BigDecimal.ZERO) && timeDifference <= TrainConstant.STOP_TIME_SECOND * 1000) {
+        if (NumberUtil.isLessOrEqual(distance, BigDecimal.ZERO)) {
             // 更新列车数据时间
             train.setUpdateTimeMillis(nowMillis);
-            // 停车状态 时间不足30S
-            train.setOpenDoor(this.openOrCloseDoor(sectionList, train, timeDifference > (TrainConstant.STOP_TIME_SECOND - 2) * 1000 ? DoorStatusEnum.CLOSE : DoorStatusEnum.OPEN));
-            return train;
+            if (timeDifference <= TrainConstant.STOP_TIME_SECOND * 1000) {
+                // 停车状态 时间不足30S
+                train.setOpenDoor(this.openOrCloseDoor(sectionList, train, timeDifference > (TrainConstant.STOP_TIME_SECOND - 2) * 1000 ? DoorStatusEnum.CLOSE : DoorStatusEnum.OPEN));
+                return train;
+            }
+            if (platformDoorService.existDoorOpen(train.getBlockId(), train.getDirection())) {
+                // 进入站台存在屏蔽门打开的情况,继续停车
+                return train;
+            }
         }
         // 更新速度和时间
         train.setSpeed(CalculatorUtil.velocity(train.getSpeed(), train.getAcceleration(), train.getUpdateTimeMillis(), nowMillis).abs());
@@ -102,6 +108,10 @@ public class TrainComputerService {
                                 train.setBlockId(sectionItem.getSectionId());
                                 train.setBlockNo(sectionItem.getSectionName());
                                 train.setOffset(BigDecimal.ZERO);
+                                // 折返后再次停车30s
+                                train.setSpeed(BigDecimal.ZERO);
+                                train.setAcceleration(BigDecimal.ZERO);
+                                return train;
                             } else {
                                 train.setSpeed(BigDecimal.ZERO);
                                 train.setAcceleration(BigDecimal.ZERO);
@@ -118,6 +128,10 @@ public class TrainComputerService {
                                 train.setBlockId(sectionItem.getSectionId());
                                 train.setBlockNo(sectionItem.getSectionName());
                                 train.setOffset(sectionItem.getSectionLength().subtract(new BigDecimal("1")));
+                                // 折返后再次停车30s
+                                train.setSpeed(BigDecimal.ZERO);
+                                train.setAcceleration(BigDecimal.ZERO);
+                                return train;
                             } else {
                                 train.setSpeed(BigDecimal.ZERO);
                                 train.setAcceleration(BigDecimal.ZERO);
@@ -128,6 +142,12 @@ public class TrainComputerService {
                 }
             }
         }
+        // 紧急停车 进入站台存在屏蔽门打开的情况
+        if (platformDoorService.existDoorOpen(train.getBlockId(), train.getDirection())) {
+            train.setAcceleration(TrainConstant.DECELERATION_LIMIT);
+            return train;
+        }
+        // TODO 触发紧急停车按钮
         // 计算MA
         BigDecimal distanceMa = this.getDistanceMa(sectionList, train);
         BigDecimal distanceLimit = CalculatorUtil.distanceStopLimit(train.getSpeed());
@@ -205,7 +225,7 @@ public class TrainComputerService {
         TrainWebSocketUtils.sendMessage(CommandEnum.TRAIN_OPEN_DOOR, trainData);
         // 站台屏蔽门开关门
         Optional<SectionItem> optional = sectionList.stream().filter(item -> item.getSectionId().equals(train.getBlockId())).findFirst();
-        optional.ifPresent(sectionItem -> platformDoorService.sendDoorStatus(statusEnum, sectionItem.getStationId(), train.getDirection()));
+        optional.ifPresent(sectionItem -> platformDoorService.updateDoorStatus(statusEnum, sectionItem.getStationId(), train.getDirection()));
         return statusEnum.getCode() != 0;
     }
 }

+ 3 - 2
train-server/src/main/resources/db/changelog/train/db-server-train.xml

@@ -249,9 +249,10 @@
             </column>
             <column name="name" type="VARCHAR(128)" defaultValue="" remarks="站台活动门编码"/>
             <column name="status" type="${number.type}" defaultValueNumeric="0" remarks="状态"/>
-            <column name="station_id" type="${autoing.type}" defaultValueNumeric="0" remarks="所属站台"/>
-            <column name="platform_id" type="${autoing.type}" defaultValueNumeric="0" remarks="所属平台"/>
             <column name="direction" type="${number.type}" defaultValueNumeric="1" remarks="方向"/>
+            <column name="platform_id" type="${autoing.type}" defaultValueNumeric="0" remarks="所属平台"/>
+            <column name="station_id" type="${autoing.type}" defaultValueNumeric="0" remarks="所属站台"/>
+            <column name="control_origin" type="VARCHAR(64)" defaultValue="AUTO" remarks="控制源"/>
         </createTable>
     </changeSet>
     <changeSet id="load-db_platform_door-data" author="zhanghaijun" failOnError="false" runOnChange="true">

+ 6 - 4
train-web/src/pages/device/DeviceAfc.vue

@@ -11,9 +11,11 @@ const devices = reactive<DeviceIcon[]>([
   { id: 133007, icon: "客服中心", size: 30, style: "left: 1056px;top: 280px;" },
   { id: 133008, icon: "客服中心", size: 30, style: "left: 1056px;top: 316px;" },
   { id: 132005, icon: "售票机", size: 30, style: "left: 50px;top: 220px;" },
-  { id: 132006, icon: "售票机", size: 30, style: "left: 50px;top: 280px;" },
-  { id: 132007, icon: "售票机", size: 30, style: "right: 80px;top: 220px;" },
-  { id: 132008, icon: "售票机", size: 30, style: "right: 80px;top: 280px;" },
+  { id: 132006, icon: "售票机", size: 30, style: "left: 50px;top: 270px;" },
+  { id: 132007, icon: "售票机", size: 30, style: "left: 50px;top: 320px;" },
+  { id: 132008, icon: "售票机", size: 30, style: "right: 80px;top: 220px;" },
+  { id: 132009, icon: "售票机", size: 30, style: "right: 80px;top: 270px;" },
+  { id: 132010, icon: "售票机", size: 30, style: "right: 80px;top: 320px;" },
   { id: 131009, icon: "闸机", size: 30, style: "left: 476px;top:362px" },
   { id: 131010, icon: "闸机", size: 30, style: "left: 504px;top:362px" },
   { id: 131011, icon: "闸机", size: 30, style: "left: 533px;top:362px" },
@@ -23,4 +25,4 @@ const devices = reactive<DeviceIcon[]>([
   { id: 131015, icon: "闸机", size: 30, style: "left: 770px;top:362px" },
   { id: 131016, icon: "闸机", size: 30, style: "left: 798px;top:362px" },
 ]);
-</script>
+</script>

+ 49 - 21
train-web/src/pages/train/controller/DrawTrack.ts

@@ -1,7 +1,7 @@
-import { crtPath } from '../core/Utils'
-import { DirectionEnum, RouteItem, SectionStatusEnum, SelectionItem, SignalItem, SwitchItem, TowardEnum } from "@/types/train-travel";
-import { useStoreSocket } from '@/store/socket'
-import { calculateComponents } from "../core/CalculateUtils.ts";
+import {crtPath} from '../core/Utils'
+import {DirectionEnum, RouteItem, SectionStatusEnum, SelectionItem, SignalItem, SwitchItem, TowardEnum} from "@/types/train-travel";
+import {useStoreSocket} from '@/store/socket'
+import {calculateComponents} from "../core/CalculateUtils.ts";
 
 export class DrawTrack {
 
@@ -38,23 +38,23 @@ export class DrawTrack {
       } else {
         this.canvas.strokeStyle = "#898989";
       }
-      const { x, y } = calculateComponents(141.42);
+      const {x, y} = calculateComponents(141.42);
       switch (item.type) {
         case 1:
           crtPath(this.canvas, [item.kilometer, this.line_up, item.kilometer + x, this.line_up + y]);
-          // this.canvas.fillText(item.name, item.kilometer, this.line_up);
+          this.canvas.fillText(item.name, item.kilometer + 40, this.line_up + 60);
           break;
         case 2:
           crtPath(this.canvas, [item.kilometer, this.line_down, item.kilometer + x, this.line_down - y]);
-          // this.canvas.fillText(item.name, item.kilometer, this.line_down);
+          this.canvas.fillText(item.name, item.kilometer + 50, this.line_down - 60);
           break;
         case 3:
           crtPath(this.canvas, [item.kilometer, this.line_down, item.kilometer - x, this.line_down - y]);
-          // this.canvas.fillText(item.name, item.kilometer, this.line_down);
+          this.canvas.fillText(item.name, item.kilometer - 70, this.line_down - 60);
           break;
         case 4:
           crtPath(this.canvas, [item.kilometer, this.line_up, item.kilometer - x, this.line_up + y]);
-          // this.canvas.fillText(item.name, item.kilometer, this.line_up);
+          this.canvas.fillText(item.name, item.kilometer - 70, this.line_up + 60);
           break;
       }
       this.canvas.stroke();
@@ -73,6 +73,9 @@ export class DrawTrack {
 
   // 绘制信号灯
   drawSignal(name: string, x: number, direction: DirectionEnum, toward: TowardEnum, routeItem: RouteItem | undefined): void {
+    if (name == "S0102" || name == "S0101") {
+      toward = toward == TowardEnum.REVERSE ? TowardEnum.FORWARD : TowardEnum.REVERSE;
+    }
     const space: number = toward == TowardEnum.REVERSE ? -10 : 10;
     const y: number = direction == DirectionEnum.UP ? this.line_up : this.line_down;
     this.canvas.save();
@@ -83,11 +86,11 @@ export class DrawTrack {
     this.canvas.arc(x - space, y - 1.5 * space, Math.abs(space * 0.5), 0, Math.PI * 2);
     this.canvas.lineWidth = 0.5;
     this.canvas.fillStyle = "#FF0000";
-    if (name == "S0102") {
-      // console.log("xinhaodeng", direction);
-    }
+
     const turnBack: boolean = routeItem?.turnBack || false;
-    if ((!turnBack && direction == DirectionEnum.UP && toward == TowardEnum.FORWARD)
+    if (name == "S0102" && direction == DirectionEnum.UP && routeItem?.sectionStatus == SectionStatusEnum.LOCK) {
+      this.canvas.fillStyle = "#00FF00";
+    } else if ((!turnBack && direction == DirectionEnum.UP && toward == TowardEnum.FORWARD)
       || (turnBack && direction == DirectionEnum.DOWN && toward == TowardEnum.FORWARD)
       || (!turnBack && direction == DirectionEnum.DOWN && toward == TowardEnum.REVERSE)
       || (turnBack && direction == DirectionEnum.UP && toward == TowardEnum.REVERSE)) {
@@ -121,9 +124,6 @@ export class DrawTrack {
     const routeList: RouteItem[] = storeSocket.getRouteList;
     for (let i: number = 0; i < selectionList.length; i++) {
       const item: SelectionItem = selectionList[i];
-      if (item.switch) {
-        continue;
-      }
       let Y: number = 0;
       switch (item.direction) {
         case DirectionEnum.UP:
@@ -133,6 +133,14 @@ export class DrawTrack {
           Y = this.line_down;
           break;
       }
+      if (i == 0) {
+        this.drawSymbol(item.startKilometer, Y, true);
+      } else if (i == selectionList.length - 1) {
+        this.drawSymbol(Number(item.startKilometer) + Number(item.selectionLength) + 159, Y, false);
+      }
+      if (item.switch) {
+        continue;
+      }
       this.canvas.save();
       this.canvas.beginPath();
       this.canvas.moveTo(item.startKilometer, Y)
@@ -157,11 +165,6 @@ export class DrawTrack {
         this.canvas.fillStyle = '#31355d';
         this.canvas.fillText(item.name.split("-")[0], Number(item.startKilometer) + Number(item.selectionLength) * 0.5, Y - 3);
       }
-      if (i == 0) {
-        this.drawSymbol(item.startKilometer, Y, true);
-      } else if (i == selectionList.length - 1) {
-        this.drawSymbol(Number(item.startKilometer) + Number(item.selectionLength) + 159, Y, false);
-      }
     }
   }
 
@@ -177,4 +180,29 @@ export class DrawTrack {
     }
     this.canvas.stroke();
   }
+
+  // 绘制站台
+  drawStation(x: number, text: string): void {
+    const height: number = (this.line_down - this.line_up) / 4;
+    const width: number = 145.63;
+    const space: number = 20;
+    this.canvas.beginPath();
+    this.canvas.fillStyle = "#4672C4";
+    this.canvas.fillRect(x, this.line_up + space, width, height);
+
+    this.canvas.fillStyle = "#4672C4";
+    this.canvas.fillRect(x, this.line_down - height - space, width, height);
+
+    this.canvas.font = "18px Arial";
+    this.canvas.fillStyle = "#ffffff";
+    this.canvas.fillText("上行站台", x + width * 2.5 / 10, this.line_up + space + height * 2 / 3);
+
+    this.canvas.fillStyle = "#ffffff";
+    this.canvas.fillText("下行站台", x + width * 2.5 / 10, this.line_down - space - height * 2 / 3 + space);
+
+    this.canvas.font = "26px Arial";
+    this.canvas.fillStyle = "#4672C4";
+    this.canvas.fillText(text, x + width * 2 / 5, this.line_up + (this.line_down - this.line_up) * 5.5 / 10);
+    this.canvas.save();
+  }
 }

+ 9 - 5
train-web/src/pages/train/index.vue

@@ -1,14 +1,14 @@
 <template>
   <div ref="canvasBox"
-    style="height: 100%; min-height: 380px; border: 1px solid #a6afbe;overflow-x: auto;overflow-y: hidden">
+       style="height: 100%; min-height: 380px; border: 1px solid #a6afbe;overflow-x: auto;overflow-y: hidden">
     <canvas ref="canvasRef" width="7460"></canvas>
   </div>
 </template>
 <script setup lang="ts">
-import { DrawTrack } from './controller/DrawTrack'
-import { DrewTrain } from './controller/DrewTrain.ts'
-import { StoreTrain } from '@/store/modules/train.ts'
-import { useStoreSocket } from '@/store/socket'
+import {DrawTrack} from './controller/DrawTrack'
+import {DrewTrain} from './controller/DrewTrain.ts'
+import {StoreTrain} from '@/store/modules/train.ts'
+import {useStoreSocket} from '@/store/socket'
 import TrainImg from "./controller/train.svg"
 
 const canvasBox = ref<HTMLElement>();
@@ -48,6 +48,10 @@ const drawAll = function (): void {
   // ctx.translate(INIT_SPACE, 0);
   ctx.save();
 
+  // 绘制站台
+  drawTrack.drawStation(450.0, "A站");
+  drawTrack.drawStation(3595.63, "B站");
+  drawTrack.drawStation(6741.26, "C站");
   // 绘制车道
   drawTrack.drawLine(storeTrain.getSelectionUpList);
   drawTrack.drawLine(storeTrain.getSelectionDownList);

+ 1 - 0
train-web/src/types/train-travel.ts

@@ -134,4 +134,5 @@ export interface DoorItem {
   stationId: number;
   platformId: number;
   direction: DirectionEnum;
+  controlOrigin: string;
 }