ソースを参照

多socket连接方案改版。

tom 2 ヶ月 前
コミット
af2559d8f1
20 ファイル変更861 行追加522 行削除
  1. 2 1
      ruoyi-admin/src/main/resources/application-druid.yml
  2. 10 10
      ruoyi-admin/src/main/resources/application.yml
  3. 4 0
      ruoyi-sim/src/main/java/com/ruoyi/sim/constant/CommConst.java
  4. 41 26
      ruoyi-sim/src/main/java/com/ruoyi/sim/controller/HardwareCommDebugController.java
  5. 18 10
      ruoyi-sim/src/main/java/com/ruoyi/sim/controller/SeatController.java
  6. 5 27
      ruoyi-sim/src/main/java/com/ruoyi/sim/controller/TestIotController.java
  7. 10 3
      ruoyi-sim/src/main/java/com/ruoyi/sim/domain/Seat.java
  8. 4 0
      ruoyi-sim/src/main/java/com/ruoyi/sim/domain/Sim.java
  9. 30 3
      ruoyi-sim/src/main/java/com/ruoyi/sim/domain/vo/SimSocketVo.java
  10. 24 9
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommBuildService.java
  11. 276 0
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommCheckService.java
  12. 28 0
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommParseUtils.java
  13. 4 71
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommReceiveService.java
  14. 240 277
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommSendService.java
  15. 17 3
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/FailedCountService.java
  16. 10 6
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamCollectionService.java
  17. 9 7
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamService.java
  18. 76 8
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SeatService.java
  19. 16 9
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SimService.java
  20. 37 52
      ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SocketService.java

+ 2 - 1
ruoyi-admin/src/main/resources/application-druid.yml

@@ -18,7 +18,8 @@ spring:
                 # password: 8M6ahN7BXsXXDccR
 
                 # server-现场实验室
-                url: jdbc:mysql://192.168.1.61:4886/pla-chem-sim-dev-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                # url: jdbc:mysql://192.168.1.61:4886/pla-chem-sim-dev-1?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                url: jdbc:mysql://192.168.1.61:4886/pla-chem-sim-dev-2?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
                 username: root
                 password: 7ZNo#9Arn3DFBN8N
 

+ 10 - 10
ruoyi-admin/src/main/resources/application.yml

@@ -76,23 +76,23 @@ spring:
     # server-阿里云47服务器内网
     # host: 127.0.0.1
     # host: 192.168.1.40
-    # host: 192.168.1.61
+    host: 192.168.1.61
     # server-阿里云47服务器外网
-    host: 47.104.188.84
+    # host: 47.104.188.84
     # 端口,默认为6379
-    # port: 6379
-    port: 65007
+    port: 6379
+    # port: 65007
     # 数据库索引
     # server-阿里云47服务器内网
-    database: 2
+    # database: 2
     # server-其他
     # database: 0
-    # database: 2
+    database: 2
     # 密码
     # server-阿里云47
-    password: Z*eQ8xXK7ryYynFv
+    # password: Z*eQ8xXK7ryYynFv
     # server-现场实验室
-    # password: x2fs#W3rZ9dZXiMb
+    password: x2fs#W3rZ9dZXiMb
     # server-李硕红米本机
     # password: redis123456
     # 连接超时时间
@@ -153,10 +153,10 @@ xss:
 sim-module-config:
   # 123.112.16.165
   # routerIp: 221.218.212.74
-  routerIp: 221.218.210.247
+  routerIp: 192.168.1.1
   # 123.112.16.165
   # rs485Ip: 221.218.212.74
-  rs485Ip: 221.218.210.247
+  rs485Ip: 127.0.0.1
   #
   rs485Port: 8899
   #

+ 4 - 0
ruoyi-sim/src/main/java/com/ruoyi/sim/constant/CommConst.java

@@ -37,6 +37,8 @@ public interface CommConst {
      * 设备类型读取
      */
     String CMD_READ_TYPE = "03";
+
+    String BLANK_SIM_NUM = "00";
     /**
      * 状态读取 RESISTANCE电阻值
      */
@@ -83,6 +85,8 @@ public interface CommConst {
 
     int SOCKET_TIME_OUT = 200;
 
+    int PING_TIME_OUT = 1000;
+
     String[] TYPE_1_BIND_MSG = new String[]{"01", "02", "03", "04", "05",
             "06", "07", "08", "09", "0A"};
 

+ 41 - 26
ruoyi-sim/src/main/java/com/ruoyi/sim/controller/HardwareCommDebugController.java

@@ -9,17 +9,22 @@ import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.web.bind.annotation.*;
 
 @RestController
 @RequestMapping("/sim/debug")
 @Api("硬件通信DebugController")
 public class HardwareCommDebugController extends BaseController {
+
     @Autowired
+    @Lazy
     private CommSendService commSendService;
     @Autowired
+    @Lazy
     private CommBuildService commBuildService;
     @Autowired
+    @Lazy
     private ApplicationContext applicationContext;
 
     @GetMapping(value = "/spring-boot-close")
@@ -28,42 +33,42 @@ public class HardwareCommDebugController extends BaseController {
         ((ConfigurableApplicationContext) applicationContext).close();
     }
 
-    @GetMapping(value = "/debugReadSimType/{simNum}")
+    @GetMapping(value = "/debugReadSimType/{seatId}")
     @ApiOperation("debug读取模拟器类型序列号")
-    public AjaxResult debugReadSimType(@PathVariable("simNum") final String simNum) {
-        return success(commSendService.debugReadSimType(simNum));
+    public AjaxResult debugReadSimType(@PathVariable("seatId") final Long seatId) {
+        return success(commSendService.debugReadSimType(seatId));
     }
 
-    @GetMapping(value = "/debugReadOneFaultResistance/{simNum}/{bindHardwareMsg}")
+    @GetMapping(value = "/debugReadOneFaultResistance/{seatId}/{bindHardwareMsg}")
     @ApiOperation("debug读取一个故障位置数据")
-    public AjaxResult debugReadOneFaultResistance(@PathVariable("simNum") final String simNum,
+    public AjaxResult debugReadOneFaultResistance(@PathVariable("seatId") final Long seatId,
                                                   @PathVariable("bindHardwareMsg") final String bindHardwareMsg) {
-        return success(commSendService.debugReadOneFaultResistance(simNum, bindHardwareMsg));
+        return success(commSendService.debugReadOneFaultResistance(seatId, bindHardwareMsg));
     }
 
-    @GetMapping(value = "/debugReadAllFaultResistance/{simNum}")
+    @GetMapping(value = "/debugReadAllFaultResistance/{seatId}")
     @ApiOperation("debug读取全部故障位置数据")
-    public AjaxResult debugReadAllFaultResistance(@PathVariable("simNum") final String simNum) {
-        return success(commSendService.debugReadAllFaultResistance(simNum));
+    public AjaxResult debugReadAllFaultResistance(@PathVariable("seatId") final Long seatId) {
+        return success(commSendService.debugReadAllFaultResistance(seatId));
     }
 
-    @GetMapping(value = "/debugReadAllFaultResistanceBySimNum/{simNum}")
+    @GetMapping(value = "/debugReadAllFaultResistanceBySimNum/{seatId}")
     @ApiOperation("debug通过simNum读取一台模拟器所有故障答题值,保存[debug_fault]表中,类似交卷")
-    public AjaxResult debugReadAllFaultResistanceBySimNum(@PathVariable("simNum") final String simNum) {
-        return commSendService.debugReadAllFaultResistanceBySimNum(simNum);
+    public AjaxResult debugReadAllFaultResistanceBySimNum(@PathVariable("seatId") final Long seatId) {
+        return commSendService.debugReadAllFaultResistanceBySimNum(seatId);
     }
 
-    @GetMapping(value = "/debugClearOneFault/{simNum}/{bindHardwareMsg}")
+    @GetMapping(value = "/debugClearOneFault/{seatId}/{bindHardwareMsg}")
     @ApiOperation("debug清除一个故障")
-    public AjaxResult debugClearOneFault(@PathVariable("simNum") final String simNum,
+    public AjaxResult debugClearOneFault(@PathVariable("seatId") final Long seatId,
                                          @PathVariable("bindHardwareMsg") final String bindHardwareMsg) {
-        return success(commSendService.debugClearOneFault(simNum, bindHardwareMsg));
+        return success(commSendService.debugClearOneFault(seatId, bindHardwareMsg));
     }
 
-    @GetMapping(value = "/debugClearAllFaultBySimNum/{simNum}")
+    @GetMapping(value = "/debugClearAllFaultBySimNum/{seatId}")
     @ApiOperation("debug通过simNum清除一台模拟器所有故障")
-    public AjaxResult debugClearAllFaultBySimNum(@PathVariable("simNum") final String simNum) {
-        return commSendService.debugClearAllFaultBySimNum(simNum);
+    public AjaxResult debugClearAllFaultBySimNum(@PathVariable("seatId") final Long seatId) {
+        return commSendService.debugClearAllFaultBySeatId(seatId);
     }
 
     @GetMapping(value = "/debugClearAllOnlineSimAllFault/")
@@ -74,26 +79,26 @@ public class HardwareCommDebugController extends BaseController {
 
     @GetMapping(value = "/debugWriteOneFault/{simNum}/{bindHardwareMsg}")
     @ApiOperation("debug下发一个故障")
-    public AjaxResult debugWriteOneFault(@PathVariable("simNum") final String simNum,
+    public AjaxResult debugWriteOneFault(@PathVariable("seatId") final Long seatId,
                                          @PathVariable("bindHardwareMsg") final String bindHardwareMsg) {
-        return success(commSendService.debugWriteOneFault(simNum, bindHardwareMsg));
+        return success(commSendService.debugWriteOneFault(seatId, bindHardwareMsg));
     }
 
-    @GetMapping(value = "/debugWriteAllFault/{simNum}")
+    @GetMapping(value = "/debugWriteAllFault/{seatId}")
     @ApiOperation("debug下发所有故障")
-    public AjaxResult debugWriteAllFault(@PathVariable("simNum") final String simNum) {
-        return success(commSendService.debugWriteAllFault(simNum));
+    public AjaxResult debugWriteAllFault(@PathVariable("seatId") final Long seatId) {
+        return success(commSendService.debugWriteAllFault(seatId));
     }
 
     @GetMapping(value = "/debugWriteSelectedFaultBySimNum/{simNum}/{faultIds}")
     @ApiOperation("debug下发所选故障,保存[debug_fault]表中,类似开始考试")
-    public AjaxResult debugWriteSelectedFaultBySimNum(@PathVariable("simNum") final String simNum,
+    public AjaxResult debugWriteSelectedFaultBySimNum(@PathVariable("seatId") final Long seatId,
                                                       @PathVariable("faultIds") final String[] faultIds,
                                                       @RequestParam final Boolean checkReplace) {
-        return commSendService.debugWriteSelectedFaultBySimNum(simNum, faultIds, checkReplace);
+        return commSendService.debugWriteSelectedFaultBySimNum(seatId, faultIds, checkReplace);
     }
-
     @GetMapping(value = "/buildMsg/")
+
     @ApiOperation("buildMsg")
     public AjaxResult buildSendMsg(@RequestParam final String simNum,
                                    @RequestParam final String orn,
@@ -102,4 +107,14 @@ public class HardwareCommDebugController extends BaseController {
                                    @RequestParam final String data) {
         return commBuildService.buildSendMsgAR(simNum, orn, cmd, cmdId, data);
     }
+
+    @ApiOperation("buildMsgAndSend")
+    public AjaxResult buildMsgAndSend(@RequestParam final String simNum,
+                                   @RequestParam final String orn,
+                                   @RequestParam final String cmd,
+                                   @RequestParam final String cmdId,
+                                   @RequestParam final String data) {
+        // todo:
+        return commBuildService.buildSendMsgAR(simNum, orn, cmd, cmdId, data);
+    }
 }

+ 18 - 10
ruoyi-sim/src/main/java/com/ruoyi/sim/controller/SeatController.java

@@ -53,7 +53,7 @@ public class SeatController extends BaseController {
      */
     // @PreAuthorize("@ss.hasPermi('sim:seat:list')")
     @GetMapping("/listAll")
-    @ApiOperation("[老师][轮询]查询全部座列表")
+    // @ApiOperation("[老师][轮询]查询全部座列表")
     public TableDataInfo listAll() {
         Seat seat = new Seat();
         startPage();
@@ -64,8 +64,8 @@ public class SeatController extends BaseController {
     /**
      * 导出座列表
      */
-    @PreAuthorize("@ss.hasPermi('sim:seat:export')")
-    @Log(title = "座", businessType = BusinessType.EXPORT)
+    // @PreAuthorize("@ss.hasPermi('sim:seat:export')")
+    // @Log(title = "座", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, Seat seat) {
         List<Seat> list = seatService.selectSeatList(seat);
@@ -76,7 +76,7 @@ public class SeatController extends BaseController {
     /**
      * 获取座详细信息
      */
-    @PreAuthorize("@ss.hasPermi('sim:seat:query')")
+    // @PreAuthorize("@ss.hasPermi('sim:seat:query')")
     @GetMapping(value = "/{seatId}")
     public AjaxResult getInfo(@PathVariable("seatId") Long seatId) {
         return success(seatService.selectSeatBySeatId(seatId));
@@ -85,8 +85,8 @@ public class SeatController extends BaseController {
     /**
      * 新增座
      */
-    @PreAuthorize("@ss.hasPermi('sim:seat:add')")
-    @Log(title = "座", businessType = BusinessType.INSERT)
+    // @PreAuthorize("@ss.hasPermi('sim:seat:add')")
+    // @Log(title = "座", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody Seat seat) {
         return toAjax(seatService.insertSeat(seat));
@@ -95,8 +95,8 @@ public class SeatController extends BaseController {
     /**
      * 修改座
      */
-    @PreAuthorize("@ss.hasPermi('sim:seat:edit')")
-    @Log(title = "座", businessType = BusinessType.UPDATE)
+    // @PreAuthorize("@ss.hasPermi('sim:seat:edit')")
+    // @Log(title = "座", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody Seat seat) {
         return toAjax(seatService.updateSeat(seat));
@@ -105,10 +105,18 @@ public class SeatController extends BaseController {
     /**
      * 删除座
      */
-    @PreAuthorize("@ss.hasPermi('sim:seat:remove')")
-    @Log(title = "座", businessType = BusinessType.DELETE)
+    // @PreAuthorize("@ss.hasPermi('sim:seat:remove')")
+    // @Log(title = "座", businessType = BusinessType.DELETE)
     @DeleteMapping("/{seatIds}")
     public AjaxResult remove(@PathVariable Long[] seatIds) {
         return toAjax(seatService.deleteSeatBySeatIds(seatIds));
     }
+
+    // -------------------------------- tom add  --------------------------------
+
+    @GetMapping("/listAllEnable")
+    @ApiOperation("获取所有没有被禁用的座列表")
+    public AjaxResult listAllEnable() {
+        return seatService.listAllEnableAj();
+    }
 }

+ 5 - 27
ruoyi-sim/src/main/java/com/ruoyi/sim/controller/TestIotController.java

@@ -9,6 +9,7 @@ import com.ruoyi.sim.service.impl.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -22,51 +23,29 @@ import java.util.List;
 public class TestIotController extends BaseController {
 
     @Autowired
-    private MajorService majorService;
-    @Autowired
+    @Lazy
     private CommSendService commSendService;
     @Autowired
+    @Lazy
     private RealExamService realExamService;
     @Autowired
+    @Lazy
     private FaultService faultService;
     @Autowired
+    @Lazy
     private SocketService socketService;
 
     @ApiOperation("debug666")
     @GetMapping(value = "/{codeId}")
     public AjaxResult testIndex(@PathVariable("codeId") Integer codeId) {
-
         long examId = 2L;
         switch (codeId) {
-            case 1: {
-                commSendService.checkAllSimState();
-            }
-            break;
-            case 2: {
-                // commSnedService.clearOneFault(null, null, null);
-            }
-            break;
-            case 3: {
-                // commSnedService.readOneFaultResistance(null, null);
-            }
-            break;
-            case 4: {
-                // commSnedService.writeOneFault(null, null, null);
-            }
-            break;
-            case 5: {
-//                for (int i = 0; i < 100; i++) {
-//                    commSnedService.readOneFaultResistance(null, null);
-//                }
-            }
-            break;
             case 6: {
                 RealExam re = realExamService.selectRealExamByExamId(1L);
                 commSendService.clearOneSimAllFaultByExam(re);
             }
             break;
             case 10: {
-                commSendService.checkAllSimState();
                 commSendService.clearAll();
                 RealExam re = realExamService.selectRealExamByExamId(1L);
                 commSendService.clearOneSimAllFaultByExam(re);
@@ -82,7 +61,6 @@ public class TestIotController extends BaseController {
                 //
             }
             break;
-
             case 20: {
                 realExamService.studentEnterRealExam(examId);
                 //

+ 10 - 3
ruoyi-sim/src/main/java/com/ruoyi/sim/domain/Seat.java

@@ -1,5 +1,6 @@
 package com.ruoyi.sim.domain;
 
+import com.ruoyi.sim.domain.vo.SimSocketVo;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -24,7 +25,7 @@ public class Seat extends BaseEntity {
      * 座号
      */
     @Excel(name = "座号")
-    private Long seatNum;
+    private Integer seatNum;
 
     /**
      * [电脑]绑定的[IP地址]
@@ -70,11 +71,11 @@ public class Seat extends BaseEntity {
         return seatId;
     }
 
-    public void setSeatNum(Long seatNum) {
+    public void setSeatNum(Integer seatNum) {
         this.seatNum = seatNum;
     }
 
-    public Long getSeatNum() {
+    public Integer getSeatNum() {
         return seatNum;
     }
 
@@ -147,10 +148,16 @@ public class Seat extends BaseEntity {
 
     // -------------------------------- tom add  --------------------------------
 
+    public static final Long ID_0 = 0L;
+
     public interface SocketState {
         String ENABLE_INIT = "0";
         String ONLINE = "1";
         String OFFLINE = "2";
         String DISABLE = "5";
     }
+
+    public SimSocketVo toSimSocketVo() {
+        return new SimSocketVo(this.getSeatRs485Ip(), this.getSeatRs485Port());
+    }
 }

+ 4 - 0
ruoyi-sim/src/main/java/com/ruoyi/sim/domain/Sim.java

@@ -151,6 +151,8 @@ public class Sim extends BaseEntity {
     }
 
     // -------------------------------- tom add  --------------------------------
+
+    public static final Long ID_0 = 0L;
     /**
      * FZD04B
      */
@@ -180,7 +182,9 @@ public class Sim extends BaseEntity {
         String ENABLE_INIT = "0";
         String ONLINE = "1";
         String OFFLINE = "2";
+        @Deprecated
         String GATEWAY_OFFLINE = "3";
+        @Deprecated
         String SIM_ERROR = "4";
         String DISABLE = "5";
     }

+ 30 - 3
ruoyi-sim/src/main/java/com/ruoyi/sim/domain/vo/SimSocketVo.java

@@ -1,5 +1,9 @@
 package com.ruoyi.sim.domain.vo;
 
+import com.ruoyi.sim.constant.CommConst;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 
 public class SimSocketVo {
@@ -13,9 +17,6 @@ public class SimSocketVo {
      */
     private Integer port;
 
-    public SimSocketVo() {
-    }
-
     public SimSocketVo(String ip, Integer port) {
         this.ip = ip;
         this.port = port;
@@ -44,4 +45,30 @@ public class SimSocketVo {
                 .append("port", port)
                 .toString();
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+
+        if (o == null || getClass() != o.getClass()) return false;
+
+        SimSocketVo that = (SimSocketVo) o;
+
+        return new EqualsBuilder().append(ip, that.ip).append(port, that.port).isEquals();
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(17, 37).append(ip).append(port).toHashCode();
+    }
+
+    public String toKey() {
+        if (StringUtils.isBlank(ip)) {
+            throw new IllegalArgumentException("ip error");
+        }
+        if (port == null || port <= CommConst.PORT_MIN || port >= CommConst.PORT_MAX) {
+            throw new IllegalArgumentException("port error");
+        }
+        return ip + ":" + port;
+    }
 }

+ 24 - 9
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommBuildService.java

@@ -27,7 +27,7 @@ public class CommBuildService {
     private SnowflakeIdService idService;
 
     /**
-     * 设备类型读取
+     * 读取设备序列号
      *
      * @param simNum sim.sim_num
      */
@@ -36,6 +36,15 @@ public class CommBuildService {
     }
 
     /**
+     * 询问设备类型和序列号
+     *
+     * @return
+     */
+    public SimMsg buildSendMsgWhichSim() {
+        return buildSendMsg(CommConst.BLANK_SIM_NUM, CMD_READ_TYPE, CMD_ID_GET_SN);
+    }
+
+    /**
      * 故障下发
      *
      * @param simNum          sim.sim_num
@@ -46,7 +55,7 @@ public class CommBuildService {
     }
 
     /**
-     * 状态读取
+     * 读取故障
      *
      * @param simNum          sim.sim_num
      * @param bindHardwareMsg fault.bind_hardware_msg
@@ -56,7 +65,7 @@ public class CommBuildService {
     }
 
     /**
-     * 故障清清除
+     * 清除故障
      *
      * @param simNum          sim.sim_num
      * @param bindHardwareMsg fault.bind_hardware_msg
@@ -65,11 +74,21 @@ public class CommBuildService {
         return buildSendMsg(simNum, CMD_CLEAR_FAULT, bindHardwareMsg);
     }
 
+    /**
+     * 填充内容为空。
+     *
+     * @param simNum
+     * @param cmd
+     * @param cmdId
+     * @return
+     */
     public SimMsg buildSendMsg(final String simNum, final String cmd, final String cmdId) {
-        return buildSendMsg(simNum, ORN_SEND, cmd, cmdId, CMD_DATA_PLACE_HOLDER);
+        return buildSendMsg(simNum, ORN_SEND, cmd, cmdId, CommConst.CMD_DATA_PLACE_HOLDER);
     }
 
     /**
+     * 生成发送指令基本方法。
+     *
      * @param simNum
      * @param orn
      * @param cmd
@@ -122,11 +141,7 @@ public class CommBuildService {
         return smS;
     }
 
-    public AjaxResult buildSendMsgAR(final String simNum,
-                                     final String orn,
-                                     final String cmd,
-                                     final String cmdId,
-                                     final String data) {
+    public AjaxResult buildSendMsgAR(final String simNum, final String orn, final String cmd, final String cmdId, final String data) {
         final SimMsg sm = buildSendMsg(simNum, orn, cmd, cmdId, data);
         return AjaxResult.success(sm);
     }

+ 276 - 0
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommCheckService.java

@@ -0,0 +1,276 @@
+package com.ruoyi.sim.service.impl;
+
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.sim.config.SimConfig;
+import com.ruoyi.sim.constant.CommConst;
+import com.ruoyi.sim.domain.Seat;
+import com.ruoyi.sim.domain.Sim;
+import com.ruoyi.sim.domain.SimMsg;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+
+import static com.ruoyi.sim.constant.CommConst.RETRY_COUNT_0;
+import static com.ruoyi.sim.constant.CommConst.RETRY_COUNT_QUERY_SN_IMPORTANT;
+
+@Service
+public class CommCheckService {
+
+    private static final Logger l = LoggerFactory.getLogger(CommCheckService.class);
+
+    @Autowired
+    private SimConfig config;
+    @Autowired
+    private SeatService seatService;
+    @Autowired
+    private SimService simService;
+    @Autowired
+    private CommBuildService commBuildService;
+    @Autowired
+    private CommSendService commSendService;
+    @Autowired
+    private FailedCountService failedCountService;
+
+    /**
+     * 等同于ping命令。
+     *
+     * @param ipV4
+     * @return
+     * @throws IOException
+     */
+    public boolean pingIsReachable(String ipV4) {
+        InetAddress ia = null;
+        try {
+            ia = InetAddress.getByName(ipV4);
+            return ia.isReachable(CommConst.PING_TIME_OUT);
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+            return false;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * ping路由器得到状态
+     *
+     * @param routerIp
+     * @return
+     */
+    public AjaxResult checkRouterState(final String routerIp) {
+        if (routerIp == null || StringUtils.isBlank(routerIp)) {
+            throw new IllegalArgumentException("routerIp isBlank");
+        }
+        if (pingIsReachable(routerIp)) {
+            return AjaxResult.success();
+        } else {
+            simService.updateAllEnableState(Sim.State.OFFLINE);
+            seatService.updateAllEnableState(Seat.SocketState.OFFLINE);
+            return AjaxResult.error("路由器[" + routerIp + "]无法连接!");
+        }
+    }
+
+    /**
+     * pingRS485得到状态
+     *
+     * @param rs485Ip
+     * @return
+     */
+    public AjaxResult checkPingRs485State(final String rs485Ip) {
+        if (rs485Ip == null || StringUtils.isBlank(rs485Ip)) {
+            throw new IllegalArgumentException("rs485Ip isBlank");
+        }
+        if (pingIsReachable(rs485Ip)) {
+            return AjaxResult.success();
+        } else {
+            seatService.updateSocketStateByRs485Ip(rs485Ip, Seat.SocketState.OFFLINE);
+            return AjaxResult.error("RS485物联网网关[" + rs485Ip + "]无法连接!");
+        }
+    }
+
+    public AjaxResult checkPingStudentPcState(final String studentIp) {
+        if (studentIp == null || StringUtils.isBlank(studentIp)) {
+            throw new IllegalArgumentException("studentIp isBlank");
+        }
+        if (pingIsReachable(studentIp)) {
+            return AjaxResult.success();
+        } else {
+            return AjaxResult.error("学员操作端[" + studentIp + "]无法连接!");
+        }
+    }
+
+    /**
+     * 检查一个座次状况。
+     * 维护 seat表-socket_state字段;
+     * 维护 seat表-sim_id字段;
+     *
+     * @param seat
+     * @param important true:重要的场景 开始考试 重试次数不同,也会进行序列号检查。false:不重要场景 定时巡查。
+     * @return
+     */
+    public AjaxResult checkOneSeatState(final Seat seat, final boolean important) {
+        // check args.
+        if (seat == null) {
+            throw new IllegalArgumentException("seat is null");
+        }
+        //
+        int retryTotalCount;
+        if (important) {
+            AjaxResult ar01 = checkRouterState(config.getRouterIp());
+            if (ar01.isError()) {
+                return ar01;
+            }
+            AjaxResult ar02 = checkPingRs485State(seat.getSeatRs485Ip());
+            if (ar02.isError()) {
+                // 更新SimId
+                seatService.updateSimIdBySeatNum(seat.getSeatNum(), Seat.ID_0);
+                // 更新SocketState
+                seatService.updateSocketStateBySeatNum(seat.getSeatNum(), Seat.SocketState.OFFLINE);
+                return ar02;
+            } else {
+                // Ping通不代表在线
+            }
+            retryTotalCount = RETRY_COUNT_QUERY_SN_IMPORTANT;
+        } else {
+            retryTotalCount = RETRY_COUNT_0;
+        }
+        SimMsg smS01 = commBuildService.buildSendMsgWhichSim();
+        SimMsg smR01 = commSendService.send(smS01, seat, null, retryTotalCount, CommConst.SLEEP_SHORT);
+        final String simNum = CommParseUtils.subSimNum(smR01);
+        Sim sim = simService.uniqueBySimNum(simNum);
+        if (sim == null) {
+            return AjaxResult.error("找不到模拟器[" + simNum + "]对应数据。");
+        }
+        if (Sim.State.DISABLE.equals(sim.getSimState())) {
+            l.warn("sim DISABLE,模拟器被禁用,sim = {}", sim);
+            return AjaxResult.error("模拟器[" + simNum + "]被禁用");
+        }
+        if (StringUtils.isBlank(sim.getSimType()) || StringUtils.isBlank(sim.getSimNum())) {
+            l.warn("sim error data {}", sim);
+            return AjaxResult.error("模拟器数据错误。");// todo:
+        }
+        // 更新SimId
+        seatService.updateSimIdBySeatNum(seat.getSeatNum(), sim.getSimId());
+        return AjaxResult.success("成功,检查一个座次[" + seat.getSeatNum() + "]OK!");
+    }
+
+    /**
+     * 默认Seat中已经有CurrentSimId数据
+     *
+     * @param seat
+     * @param important
+     * @return
+     */
+    public AjaxResult checkOneSimState(final Seat seat, final boolean important) {
+        // check args.
+        if (seat == null) {
+            throw new IllegalArgumentException("seat is null");
+        }
+        if (seat.getCurrentSimId() == null || Sim.ID_0.equals(seat.getCurrentSimId())) {
+            return AjaxResult.error("模拟器ID不存在!");
+        }
+        if (!simService.existBySimId(seat.getCurrentSimId())) {
+            return AjaxResult.error("模拟器ID[" + seat.getCurrentSimId() + "]不存在!");
+        }
+        //
+        int retryTotalCount;
+        if (important) {
+            retryTotalCount = RETRY_COUNT_QUERY_SN_IMPORTANT;
+        } else {
+            retryTotalCount = RETRY_COUNT_0;
+        }
+        Sim sim = simService.selectSimBySimId(seat.getCurrentSimId());
+        SimMsg smS02 = commBuildService.buildSendMsgReadSimType(sim.getSimNum());
+        SimMsg smR02 = commSendService.send(smS02, seat, sim, retryTotalCount, CommConst.SLEEP_SHORT);
+        if (StringUtils.isNotBlank(smR02.getReceiveMsg())) {
+            // 只要返回正确报文,即认为在线。
+            simService.updateSimStateBySimId(sim.getSimId(), Sim.State.ONLINE);
+            // SocketOldService实现。
+            // socketOldService.commFailCountClearOne(sim.getSimId());
+            failedCountService.reset0(seat.toSimSocketVo());
+            return AjaxResult.success("成功,检查一个模拟器[" + sim.getSimNum() + "]OK!");
+        }
+        return AjaxResult.error("失败,检查一个模拟器[" + sim.getSimNum() + "]在线状态执行错误!");
+    }
+
+    /**
+     * 默认Seat中已经有CurrentSimId数据
+     * 检查回应报文模拟器类型是否正确。
+     *
+     * @param seat
+     * @param important
+     * @param targetSimType 期望模拟器目标类型
+     * @return
+     */
+    public AjaxResult checkOneSimType(final Seat seat, final boolean important, final String targetSimType) {
+        // check args.
+        if (seat == null) {
+            throw new IllegalArgumentException("seat is null");
+        }
+        if (seat.getCurrentSimId() == null || Sim.ID_0.equals(seat.getCurrentSimId())) {
+            throw new IllegalArgumentException("sim id is 0");
+        }
+        if (!simService.existBySimId(seat.getCurrentSimId())) {
+            return AjaxResult.error("模拟器ID[" + seat.getCurrentSimId() + "]不存在!");
+        }
+        //
+        final String msgError = "连接模拟器类型或序列号不正确!应该连接型号:";
+        final String msgOk = "连接模拟器类型或序列号正确!";
+        int retryTotalCount;
+        if (important) {
+            retryTotalCount = RETRY_COUNT_QUERY_SN_IMPORTANT;
+        } else {
+            retryTotalCount = RETRY_COUNT_0;
+        }
+        Sim sim = simService.selectSimBySimId(seat.getCurrentSimId());
+        SimMsg smS02 = commBuildService.buildSendMsgReadSimType(sim.getSimNum());
+        SimMsg smR02 = commSendService.send(smS02, seat, sim, retryTotalCount, CommConst.SLEEP_SHORT);
+        if (StringUtils.isNotBlank(smR02.getReceiveMsg())) {
+            final String content = CommParseUtils.subContentData(smR02);
+            switch (targetSimType) {
+                case Sim.TYPE_0001 -> {
+                    if (content.startsWith(CommConst.TYPE_0001_SN_PREFIX) && content.endsWith(sim.getSimNum())) {
+                        return AjaxResult.success(msgOk);
+                    } else {
+                        return AjaxResult.error(msgError + Sim.TYPE_NAME_MAP.get(targetSimType));
+                    }
+                }
+                case Sim.TYPE_0002 -> {
+                    if (content.startsWith(CommConst.TYPE_0002_SN_PREFIX) && content.endsWith(sim.getSimNum())) {
+                        return AjaxResult.success(msgOk);
+                    } else {
+                        return AjaxResult.error(msgError + Sim.TYPE_NAME_MAP.get(targetSimType));
+                    }
+                }
+                case Sim.TYPE_0003 -> {
+                    if (content.startsWith(CommConst.TYPE_0003_SN_PREFIX) && content.endsWith(sim.getSimNum())) {
+                        return AjaxResult.success(msgOk);
+                    } else {
+                        return AjaxResult.error(msgError + Sim.TYPE_NAME_MAP.get(targetSimType));
+                    }
+                }
+                default -> throw new IllegalStateException("Unexpected value: " + targetSimType);
+            }
+        }
+        return AjaxResult.error("失败,检查一个模拟器[" + sim.getSimNum() + "]型号或序列号执行错误!");
+    }
+
+    /**
+     * [定时执行]查找所有没有被手动禁用的座次 和 座次上的模拟器。
+     */
+    public void checkAllSeatAndSimState() {
+        List<Seat> list = seatService.listAllEnable();
+        list.forEach(seat -> {
+            checkOneSeatState(seat, false);
+            checkOneSimState(seat, false);
+        });
+    }
+}

+ 28 - 0
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommParseUtils.java

@@ -0,0 +1,28 @@
+package com.ruoyi.sim.service.impl;
+
+
+import com.ruoyi.sim.domain.SimMsg;
+import org.apache.commons.lang3.StringUtils;
+
+public class CommParseUtils {
+
+    /**
+     * 截取 内容报文。
+     *
+     * @param
+     * @return "01 02 03 04"
+     */
+    public static String subContentData(SimMsg sm) {
+        if (StringUtils.isBlank(sm.getReceiveMsg())) {
+            throw new IllegalArgumentException("sm isBlank");
+        }
+        return StringUtils.substring(sm.getReceiveMsg(), 10, 18);
+    }
+
+    public static String subSimNum(SimMsg sm) {
+        if (StringUtils.isBlank(sm.getReceiveMsg())) {
+            throw new IllegalArgumentException("sm isBlank");
+        }
+        return StringUtils.substring(sm.getReceiveMsg(), 2, 4);
+    }
+}

+ 4 - 71
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommReceiveService.java

@@ -32,62 +32,6 @@ public class CommReceiveService {
     @Autowired
     private FaultService faultService;
 
-    /**
-     * 只要返回信息,即认为在线。
-     *
-     * @param sm
-     * @param s
-     * @return
-     */
-    public AjaxResult checkOneSimState(SimMsg sm, Sim s) {
-        if (s == null) {
-            l.warn("s is null");
-            return AjaxResult.error("sim is null");
-        }
-        if (StringUtils.isNotBlank(sm.getReceiveMsg())) {
-            simService.updateSimStateBySimId(s.getSimId(), Sim.State.ONLINE);
-        }
-        return AjaxResult.success();
-    }
-
-    /**
-     * c.endsWith(s.getSimNum())
-     * 检查回应报文模拟器类型是否正确。
-     *
-     * @param sm
-     * @param s
-     * @return
-     */
-    public AjaxResult checkOneSimSn(SimMsg sm, Sim s) {
-        final String simType = s.getSimType();
-        final String c = subContentData(sm.getReceiveMsg());
-        final String msgError = "连接模拟器类型不正确!";
-        switch (simType) {
-            case Sim.TYPE_0001 -> {
-                if (c.startsWith(CommConst.TYPE_0001_SN_PREFIX)) {
-                    return AjaxResult.success();
-                } else {
-                    return AjaxResult.error(msgError);
-                }
-            }
-            case Sim.TYPE_0002 -> {
-                if (c.startsWith(CommConst.TYPE_0002_SN_PREFIX)) {
-                    return AjaxResult.success();
-                } else {
-                    return AjaxResult.error(msgError);
-                }
-            }
-            case Sim.TYPE_0003 -> {
-                if (c.startsWith(CommConst.TYPE_0003_SN_PREFIX)) {
-                    return AjaxResult.success();
-                } else {
-                    return AjaxResult.error(msgError);
-                }
-            }
-            default -> throw new IllegalStateException("Unexpected value: " + simType);
-        }
-    }
-
     public void clearOneFault(SimMsg sm, Sim s, RealExamFault reF, Fault f) {
         // check
 
@@ -111,7 +55,7 @@ public class CommReceiveService {
         // check
 
         //
-        String faultQuestionValue = subContentData(sm.getReceiveMsg());
+        String faultQuestionValue = CommParseUtils.subContentData(sm);
         // todo:
         if (StringUtils.isBlank(faultQuestionValue)) {
             l.warn("faultQuestionValue is empty!");
@@ -178,7 +122,7 @@ public class CommReceiveService {
     public void setFaultAnswerValue(SimMsg sm, Sim s, RealExamFault reF, Fault f, String refState) {
         // check
         //
-        String faultAnswerValue = subContentData(sm.getReceiveMsg());
+        String faultAnswerValue = CommParseUtils.subContentData(sm);
         // todo:
         if (StringUtils.isBlank(faultAnswerValue)) {
             l.warn("faultAnswerValue is empty!");
@@ -206,18 +150,6 @@ public class CommReceiveService {
         }
     }
 
-    /**
-     * 截取 内容报文。
-     *
-     * @param receiveMsg
-     * @return
-     */
-    public String subContentData(String receiveMsg) {
-        if (StringUtils.isEmpty(receiveMsg)) {
-            return "";
-        }
-        return StringUtils.substring(receiveMsg, 10, 18);
-    }
 
     /**
      * 开始考试 检查 故障部位 检查
@@ -228,7 +160,7 @@ public class CommReceiveService {
      * @return
      */
     public AjaxResult getOneFaultCheck(SimMsg sm, Sim s, Fault f) {
-        String checkValue = subContentData(sm.getReceiveMsg());
+        String checkValue = CommParseUtils.subContentData(sm);
         if (s == null) {
             return AjaxResult.error("没有对应模拟器!");
         }
@@ -257,6 +189,7 @@ public class CommReceiveService {
 
     /**
      * 处理报文前端加00的情况,最多可能加5组00
+     *
      * @param receiveMsg
      * @return
      */

+ 240 - 277
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommSendService.java

@@ -4,7 +4,9 @@ import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.sim.config.SimConfig;
 import com.ruoyi.sim.config.SimDebugConfig;
+import com.ruoyi.sim.constant.CommConst;
 import com.ruoyi.sim.domain.*;
+import com.ruoyi.sim.domain.vo.SimSocketVo;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Logger;
@@ -16,9 +18,7 @@ import org.springframework.transaction.annotation.Transactional;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.net.InetAddress;
 import java.net.Socket;
-import java.net.UnknownHostException;
 import java.util.*;
 
 import static com.ruoyi.sim.constant.CommConst.*;
@@ -36,6 +36,8 @@ public class CommSendService {
     @Autowired
     private CommReceiveService simReceiveService;
     @Autowired
+    private SeatService seatService;
+    @Autowired
     private SimService simService;
     @Autowired
     private FaultService faultService;
@@ -49,8 +51,13 @@ public class CommSendService {
     private CommBuildService commBuildService;
     @Autowired
     private DebugFaultService debugFaultService;
+    // private SocketOldService socketOldService;
+    @Autowired
+    private SocketService socketService;
+    @Autowired
+    private FailedCountService failedCountService;
     @Autowired
-    private SocketOldService socketOldService;
+    private CommCheckService commCheckService;
     @Autowired
     CommReceiveService commReceiveService;
     @Autowired
@@ -72,13 +79,12 @@ public class CommSendService {
             }
             List<RealExamFault> listRef = realExamFaultService.listAllType2State2and3ByExamId(e.getExamId());
             listRef.forEach(ref -> {
-                Sim s = simService.selectSimBySimId(e.getSimId());
+                RealExam re = realExamService.selectRealExamByExamId(ref.getExamId());
+                Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+                Sim sim = simService.selectSimBySimId(e.getSimId());
                 Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
-                if (f != null &&
-                        Fault.Type.REAL_GZBW.equals(f.getFaultType()) &&
-                        Fault.State.ENABLE.equals(f.getFaultState())
-                ) {
-                    readOneSimOneFaultResistance(s, ref, f, null);
+                if (f != null && Fault.Type.REAL_GZBW.equals(f.getFaultType()) && Fault.State.ENABLE.equals(f.getFaultState())) {
+                    readOneSimOneFaultResistance(seat, sim, ref, f, null);
                 }
             });
         });
@@ -96,9 +102,10 @@ public class CommSendService {
         l.info("readOneExamAtLast");
         List<RealExamFault> list = realExamFaultService.listAllType2State2and3ByExamId(re.getExamId());
         for (RealExamFault ref : list) {
-            Sim s = simService.selectSimBySimId(re.getSimId());
+            Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+            Sim sim = simService.selectSimBySimId(re.getSimId());
             Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
-            readOneSimOneFaultResistance(s, ref, f, RealExamFault.State.FINISH);
+            readOneSimOneFaultResistance(seat, sim, ref, f, RealExamFault.State.FINISH);
         }
         // 计算扣分。
         // 最后都读取到,才算扣分。
@@ -108,36 +115,34 @@ public class CommSendService {
         }
     }
 
-    public void readOneSimAtLastByDebug(Sim s) {
+    public void readOneSimAtLastByDebug(Seat seat, Sim sim) {
         l.info("readOneSimAtLastByDebug");
-        List<Fault> list = faultService.listType3(s.getSimType());
+        List<Fault> list = faultService.listType3(sim.getSimType());
         for (Fault f : list) {
-            readOneSimOneFaultResistance(s, null, f, null);
+            readOneSimOneFaultResistance(seat, sim, null, f, null);
         }
     }
 
-    public AjaxResult debugReadAllFaultResistanceBySimNum(String simNum) {
+    public AjaxResult debugReadAllFaultResistanceBySimNum(final Long seatId) {
         // check
 
         //
         // 打开socket
         {
-            AjaxResult ar1 = socketOldService.openSocket();
-            if (ar1.isError()) {
-                return ar1;
-            }
+            socketService.openAll();
         }
         //
-        Sim s = simService.uniqueBySimNum(simNum);
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
         {
-            AjaxResult arE3 = checkOneSimStateActive(s);
+            AjaxResult arE3 = checkOneSimStateActive(seat);
             if (arE3.isError()) {
                 return arE3;
             }
         }
-        s = simService.uniqueBySimNum(simNum);
+        sim = simService.uniqueBySimNum(sim.getSimNum());
         // todo: aj改造
-        readOneSimAtLastByDebug(s);
+        readOneSimAtLastByDebug(seat, sim);
         return AjaxResult.success("成功读取!");
     }
 
@@ -169,7 +174,7 @@ public class CommSendService {
      */
     public void scheduledReadSim() {
         l.info("scheduledReadSim");
-        if (!realExamCollectionService.existOpened()) {
+        if (!realExamCollectionService.existAtLeastOneOpened()) {
             l.info("没有open的考试集合");
             return;
         }
@@ -198,15 +203,6 @@ public class CommSendService {
         // debugReadSimType(simNum);
         // l.info("bRea:" + bRea);
 
-        {
-            Socket s = socketOldService.getCachedSocket();
-            l.info("cachedSocket.isConnected():" + s.isConnected());
-            l.info("cachedSocket.isBound():" + s.isBound());
-            l.info("cachedSocket.isClosed():" + s.isClosed());
-            l.info("cachedSocket.isInputShutdown():" + s.isInputShutdown());
-            l.info("cachedSocket.isOutputShutdown():" + s.isOutputShutdown());
-        }
-
 //            debugReadAllFaultResistance(simNum, simType);
 
 //            debugWriteAllFault(simNum, simType);
@@ -224,7 +220,7 @@ public class CommSendService {
 
     /**
      * 连接情况 的 定时任务。
-     * 执行频率: 3min
+     * 执行频率: 5min
      */
     public void scheduledConnect() {
         l.info("scheduled####Connect  连接情况 的 定时任务");
@@ -232,113 +228,63 @@ public class CommSendService {
             l.info("连接情况 的 定时任务被禁用!");
             return;
         }
-        if (!realExamCollectionService.existOpened()) {
-            l.info("没有open的考试集合");
-            return;
-        }
-        if (!isReachable(config.getRouterIp())) {
-            l.warn("ping RouterIp not ok");
-            simService.updateAllEnableState(Sim.State.GATEWAY_OFFLINE);
-            return;
-        }
-        if (!isReachable(config.getRs485Ip())) {
-            l.warn("ping Rs485Ip not ok");
-            simService.updateAllEnableState(Sim.State.GATEWAY_OFFLINE);
-            return;
-        }
 
-        socketOldService.openSocket();
-        if (socketOldService.isCachedSocketOk()) {
-            checkAllSimState();
+        // 暂时注释
+//        if (!realExamCollectionService.existAtLeastOneOpened()) {
+//            l.info("没有open的任何集合");
+//            return;
+//        }
+
+        //
+        {
+            AjaxResult ar01 = commCheckService.checkRouterState(config.getRouterIp());
+            if (ar01.isError()) {
+                return;
+            }
+        }
+        //
+        {
+            seatService.listAllRs485Ip().forEach(rs485Ip -> {
+                commCheckService.checkPingRs485State(rs485Ip);
+            });
         }
+        // SocketOldService实现
+        // socketOldService.openSocket();
+        socketService.openAll();
+        // SocketOldService实现
+//        if (socketOldService.isCachedSocketOk()) {
+//            checkAllSeatAndSimState();
+//        }
+        commCheckService.checkAllSeatAndSimState();
     }
 
     /**
      * 主动更新模拟器状态。
+     * todo:需要重新考虑
      *
-     * @param s
+     * @param seat
      * @return
      */
-    public AjaxResult checkOneSimStateActive(Sim s) {
-        final long simId = s.getSimId();
+    public AjaxResult checkOneSimStateActive(Seat seat) {
         // 这句可能会调整模拟器状态,后面需要重新查询。
-        {
-            AjaxResult ar1 = checkOneSimState(s, true);
-            if (ar1.isError()) {
-                return ar1;
-            }
+        commCheckService.checkOneSeatState(seat, true);
+        AjaxResult ar1 = commCheckService.checkOneSimState(seat, true);
+        if (ar1.isError()) {
+            return ar1;
         }
         // 重新最新模拟器。
-        s = simService.selectSimBySimId(simId);
+        Sim sim = gggSimBySeatId(seat.getSeatId());
         // 如果模拟器离线
-        if (s != null && Sim.State.ONLINE.equals(s.getSimState())) {
+        if (sim != null && Sim.State.ONLINE.equals(sim.getSimState())) {
             return AjaxResult.success();
         } else {
             return AjaxResult.error("未连接模拟器,请检查连接!");
         }
     }
 
-    /**
-     * @param s
-     * @param important true 重试次数不同,也会进行序列号检查。
-     * @return
-     */
-    public AjaxResult checkOneSimState(final Sim s, final boolean important) {
-        // check
-        if (s == null) {
-            return AjaxResult.error("sim is null");
-        }
-        if (Sim.State.DISABLE.equals(s.getSimState())) {
-            l.warn("sim DISABLE,模拟器被禁用,sim = {}", s);
-            return AjaxResult.error("模拟器被禁用");
-        }
-        if (StringUtils.isBlank(s.getSimType()) || StringUtils.isBlank(s.getSimNum())) {
-            l.warn("sim error data {}", s);
-            return AjaxResult.error("模拟器数据错误。");
-        }
-        //
-        SimMsg smS = commBuildService.buildSendMsgReadSimType(s.getSimNum());
-        int retryCount = RETRY_COUNT_0;
-        long sleep = SLEEP_SHORT;
-        if (important) {
-            retryCount = RETRY_COUNT_QUERY_SN_IMPORTANT;
-        }
-        SimMsg smR = send(smS, s, retryCount, sleep);
-        if (StringUtils.isNotBlank(smR.getReceiveMsg())) {
-            l.info("isNotBlank");
-            socketOldService.commFailCountClearOne(s.getSimId());
-        }
-        simReceiveService.checkOneSimState(smR, s);
-        {
-            if (important) {
-                AjaxResult ar1 = simReceiveService.checkOneSimSn(smR, s);
-                if (ar1.isError()) {
-                    return ar1;
-                }
-            }
-        }
-        return AjaxResult.success();
-    }
-
-    /**
-     * 查找所有没有被手动禁用,并order by sim_num的模拟器列表。检查所有模拟器状态。
-     */
-    public void checkAllSimState() {
-        // RealExamCollection ecF = realExamCollectionService.selectRealExamCollectionOpened();
-        // l.info("ecF.getSimType() = {}", ecF.getSimType());
-        // if (ecF != null) {
-
-        // }
-        List<Sim> list = simService.listAllEnable(); // ecF.getSimType()
-        l.info("checkAllSimState list.size() = {}", list.size());
-        list.forEach(s -> {
-            checkOneSimState(s, false);
-        });
-    }
-
     @Async("tp-comm")
     public void checkAllSimStateAsync() {
-        checkAllSimState();
+        commCheckService.checkAllSeatAndSimState();
     }
 
     /**
@@ -360,27 +306,26 @@ public class CommSendService {
             l.info("清除exam list = {}", list.size());
         }
         assert list != null;
-        list
-                .forEach(ref -> {
-                    Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
-                    if (faultService.isDisable(f.getFaultId())) {
-                        l.warn("故障 {} -被禁用", f.getName());
-                        throw new IllegalArgumentException("故障被禁用");
-                    }
-                    Sim s = simService.selectSimBySimId(re.getSimId());
-                    // check
+        Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+        list.forEach(ref -> {
+            Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
+            if (faultService.isDisable(f.getFaultId())) {
+                l.warn("故障 {} -被禁用", f.getName());
+                throw new IllegalArgumentException("故障被禁用");
+            }
+            Sim sim = simService.selectSimBySimId(re.getSimId());
+            // check
 
-                    //
-                    clearOneSimOneFault(s, ref, f);
-                });
+            //
+            clearOneSimOneFault(seat, sim, ref, f);
+        });
     }
 
-    public void clearOneSimAllFaultBySim(Sim s) {
-        l.info("clearOneSimAllFaultBySim = {}", s);
-        faultService.listType3EnableBySimType(s.getSimType())
-                .forEach(f -> {
-                    clearOneSimOneFault(s, null, f);
-                });
+    public void clearOneSimAllFaultBySim(Seat seat, Sim sim) {
+        l.info("clearOneSimAllFaultBySim = {}", sim);
+        faultService.listType3EnableBySimType(sim.getSimType()).forEach(f -> {
+            clearOneSimOneFault(seat, sim, null, f);
+        });
     }
 
     /**
@@ -395,11 +340,13 @@ public class CommSendService {
 
     public void clearAll() {
         // todo:
-        simService.listAllEnable().forEach(s -> {
-            String simType = s.getSimType();
+        // 根据Seat数据遍历
+        seatService.listAllEnable().forEach(seat -> {
+            Sim sim = gggSimBySeatId(seat.getSeatId());
+            String simType = sim.getSimType();
             List<Fault> listF = faultService.listType3EnableBySimType(simType);
             listF.forEach(f -> {
-                clearOneSimOneFault(s, null, f);
+                clearOneSimOneFault(seat, sim, null, f);
             });
         });
     }
@@ -407,31 +354,34 @@ public class CommSendService {
     /**
      * debug读取模拟器类型序列号
      *
-     * @param simNum
+     * @param seatId
      * @return
      */
-    public SimMsg debugReadSimType(final String simNum) {
-        SimMsg sm = commBuildService.buildSendMsgReadSimType(simNum);
-        return send(sm, null, RETRY_COUNT_0, SLEEP_SHORT);
+    public SimMsg debugReadSimType(final Long seatId) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
+        SimMsg sm = commBuildService.buildSendMsgReadSimType(sim.getSimNum());
+        return send(sm, seat, sim, CommConst.RETRY_COUNT_0, CommConst.SLEEP_SHORT);
     }
 
     /**
      * debug清除一个故障
      *
-     * @param simNum
+     * @param seatId
      * @param bindHardwareMsg
      * @return
      */
-    public SimMsg debugClearOneFault(final String simNum, final String bindHardwareMsg) {
-        SimMsg sm = commBuildService.buildSendMsgClearFault(simNum, bindHardwareMsg);
-        Sim s = simService.uniqueBySimNum(simNum);
-        return send(sm, s, RETRY_COUNT_CLEAR_ONE_FAULT, SLEEP_LONG);
+    public SimMsg debugClearOneFault(final Long seatId, final String bindHardwareMsg) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
+        SimMsg sm = commBuildService.buildSendMsgClearFault(sim.getSimNum(), bindHardwareMsg);
+        return send(sm, seat, sim, RETRY_COUNT_CLEAR_ONE_FAULT, SLEEP_LONG);
     }
 
     public AjaxResult debugClearAllOnlineSimAllFault() {
         l.info("debugClearAllOnlineSimAllFault");
         simService.listAllOnline().forEach(s -> {
-            AjaxResult ar = debugClearAllFaultBySimNum(s.getSimNum());
+            // AjaxResult ar = debugClearAllFaultBySeatId(0);// todo:尚未实现
         });
         debugFaultService.deleteAll();
         return AjaxResult.success("清除成功,清除所有在线模拟器所有故障!");
@@ -440,28 +390,30 @@ public class CommSendService {
     /**
      * debug清除所有故障
      *
-     * @param simNum
+     * @param seatId
      * @return
      */
     @Transactional
-    public AjaxResult debugClearAllFaultBySimNum(final String simNum) {
-        Sim s = simService.uniqueBySimNum(simNum);
+    public AjaxResult debugClearAllFaultBySeatId(final Long seatId) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
+        // todo:
         {
-            AjaxResult arE3 = checkOneSimStateActive(s);
+            AjaxResult arE3 = checkOneSimStateActive(seat);
             if (arE3.isError()) {
                 return arE3;
             }
         }
-        s = simService.uniqueBySimNum(simNum);
-        if (s == null) {
+        sim = simService.uniqueBySimNum(sim.getSimNum());
+        if (sim == null) {
             return AjaxResult.error("清除失败,对应simNum模拟器不存在!");
         }
-        if (!Sim.State.ONLINE.equals(s.getSimState())) {
+        if (!Sim.State.ONLINE.equals(sim.getSimState())) {
             return AjaxResult.error("清除失败,模拟器尚未在线!");
         }
         List<SimMsg> list = new ArrayList<>();
-        for (String b : getGZBWBySimType(s.getSimType())) {
-            SimMsg sm = debugClearOneFault(simNum, b);
+        for (String b : getGZBWBySimType(sim.getSimType())) {
+            SimMsg sm = debugClearOneFault(seatId, b);
             list.add(sm);
             if (sm != null && !sm.getOk()) {
 
@@ -472,20 +424,19 @@ public class CommSendService {
     }
 
     /**
-     * @param s
+     * @param sim
      * @param reF 可以为空,表示不关联考试的,单独执行的。调试模式下为空。
      * @param f
      */
-    public void clearOneSimOneFault(Sim s, RealExamFault reF, Fault f) {
-        l.info("clearOneSimOneFault 清除One故障:getSimNum = {},getBindHardwareMsg = {},fault.getName = {}",
-                s.getSimNum(), f.getBindHardwareMsg(), f.getName());
+    public void clearOneSimOneFault(Seat seat, Sim sim, RealExamFault reF, Fault f) {
+        l.info("clearOneSimOneFault 清除One故障:getSimNum = {},getBindHardwareMsg = {},fault.getName = {}", sim.getSimNum(), f.getBindHardwareMsg(), f.getName());
         // check todo:
 
         // step1
-        SimMsg smS = commBuildService.buildSendMsgClearFault(s.getSimNum(), f.getBindHardwareMsg());
-        SimMsg smR = send(smS, s, RETRY_COUNT_CLEAR_ONE_FAULT, SLEEP_LONG);
+        SimMsg smS = commBuildService.buildSendMsgClearFault(sim.getSimNum(), f.getBindHardwareMsg());
+        SimMsg smR = send(smS, seat, sim, RETRY_COUNT_CLEAR_ONE_FAULT, SLEEP_LONG);
         if (reF != null) {
-            simReceiveService.clearOneFault(smR, s, reF, f);
+            simReceiveService.clearOneFault(smR, sim, reF, f);
         } else {
             l.info("reF == null");
         }
@@ -508,29 +459,31 @@ public class CommSendService {
     /**
      * debug下发一个故障
      *
-     * @param simNum
+     * @param seatId
      * @param bindHardwareMsg
      * @return
      * @throws IOException
      */
-    public SimMsg debugWriteOneFault(final String simNum, final String bindHardwareMsg) {
-        SimMsg sm = commBuildService.buildSendMsgWriteFault(simNum, bindHardwareMsg);
-        return send(sm, null, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
+    public SimMsg debugWriteOneFault(final Long seatId, final String bindHardwareMsg) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
+        SimMsg sm = commBuildService.buildSendMsgWriteFault(sim.getSimNum(), bindHardwareMsg);
+        return send(sm, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
     }
 
     /**
      * debug下发所有故障
      *
-     * @param simNum
+     * @param seatId
      * @return
      * @throws IOException
      */
-    public List<SimMsg> debugWriteAllFault(final String simNum) {
+    public List<SimMsg> debugWriteAllFault(final Long seatId) {
         List<SimMsg> list = new ArrayList<>();
-        Sim s = simService.uniqueBySimNum(simNum);
-        String simType = s.getSimType();
-        for (String b : getGZBWBySimType(simType)) {
-            list.add(debugWriteOneFault(simNum, b));
+        Sim sim = gggSimBySeatId(seatId);
+        String simType = sim.getSimType();
+        for (String bind : getGZBWBySimType(simType)) {
+            list.add(debugWriteOneFault(seatId, bind));
         }
         return list;
     }
@@ -539,15 +492,13 @@ public class CommSendService {
      * todo:尚未实现
      * 实现方式类似 studentStartRealExam方法。
      *
-     * @param simNum
+     * @param seatId
      * @param faultIds
      * @param checkReplace 是否进行可换件检查
      * @return
      */
     @Transactional
-    public AjaxResult debugWriteSelectedFaultBySimNum(final String simNum,
-                                                      final String[] faultIds,
-                                                      final Boolean checkReplace) {
+    public AjaxResult debugWriteSelectedFaultBySimNum(final Long seatId, final String[] faultIds, final Boolean checkReplace) {
         //
         l.info("faultIds.length = {}", faultIds.length);
         {
@@ -557,29 +508,26 @@ public class CommSendService {
         // check faultIds 有效性
 
         //
-        Sim s = simService.uniqueBySimNum(simNum);
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
         // check sim
         // 打开socket
         {
-            AjaxResult ar1 = socketOldService.openSocket();
-            l.info("ar1 = {}", ar1);
-            if (ar1.isError()) {
-                return ar1;
-            }
+            socketService.openAll();
         }
         // Step 1 主动查询一次模拟器状态。
         {
-            AjaxResult arE3 = checkOneSimStateActive(s);
+            AjaxResult arE3 = checkOneSimStateActive(seat);
             if (arE3.isError()) {
                 return arE3;
             }
         }
-        s = simService.uniqueBySimNum(simNum);
+        sim = simService.uniqueBySimNum(sim.getSimNum());
         // Step 2
         // msg判断,是否含有"故障部位"字符串
         {
             if (checkReplace) {
-                AjaxResult arE2 = readOneSimAllFaultCheck(s);
+                AjaxResult arE2 = readOneSimAllFaultCheck(seat, sim);
                 if (arE2.isError()) {
                     return arE2;
                 }
@@ -587,7 +535,7 @@ public class CommSendService {
         }
         // Step 3 清除对应一台模拟器 所有故障部位故障。
         {
-            clearOneSimAllFaultBySim(s);
+            clearOneSimAllFaultBySim(seat, sim);
         }
         // Step 4 下发对应一台模拟器 出题选中的 故障位置故障。
         {
@@ -595,11 +543,11 @@ public class CommSendService {
             for (int i = 0; i < faultIds.length; i++) {
                 faults[i] = faultService.selectFaultByFaultId(faultIds[i]);
             }
-            writeOneSimAllSelectFaultByDebug(s, faults);
+            writeOneSimAllSelectFaultByDebug(seat, sim, faults);
         }
         // Step 5 读取
         {
-            readOneSimAllFaultFirstTimeBySim(s, faultIds);
+            readOneSimAllFaultFirstTimeBySim(seat, sim, faultIds);
         }
         return AjaxResult.success("下发故障流程执行成功!");
     }
@@ -634,41 +582,42 @@ public class CommSendService {
         realExamService.updateOneState(re.getExamId(), RealExam.State.SIM_WRITING);
         List<RealExamFault> list = realExamFaultService.listAllType2FlagYesClearedStateByExamId(re.getExamId());
         for (RealExamFault ref : list) {
-            Sim s = simService.selectSimBySimId(re.getSimId());
+            Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+            Sim sim = simService.selectSimBySimId(re.getSimId());
             Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
             // 选中的才下发。
-            writeOneSimOneFault(s, ref, f);
+            writeOneSimOneFault(seat, sim, ref, f);
         }
     }
 
-    public void writeOneSimAllSelectFaultByDebug(Sim s, Fault[] faults) {
+    public void writeOneSimAllSelectFaultByDebug(Seat seat, Sim sim, Fault[] faults) {
         for (Fault f : faults) {
-            writeOneSimOneFault(s, null, f);
+            writeOneSimOneFault(seat, sim, null, f);
         }
     }
 
-    public void writeOneSimOneFault(Sim s, RealExamFault ref, Fault f) {
-        l.info("下发故障:getSimId = {},fault.getName = {}", s.getSimId(), f.getName());
+    public void writeOneSimOneFault(Seat seat, Sim sim, RealExamFault ref, Fault f) {
+        l.info("下发故障:getSimId = {},fault.getName = {}", sim.getSimId(), f.getName());
         // todo:ref is null.
         // 下发故障
-        SimMsg smA1 = commBuildService.buildSendMsgWriteFault(s.getSimNum(), f.getBindHardwareMsg());
-        SimMsg smA2 = send(smA1, s, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
+        SimMsg smA1 = commBuildService.buildSendMsgWriteFault(sim.getSimNum(), f.getBindHardwareMsg());
+        SimMsg smA2 = send(smA1, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
     }
 
     /**
      * 开始考试前检查读取。
      *
-     * @param s
+     * @param sim
      * @return
      */
-    public AjaxResult readOneSimAllFaultCheck(Sim s) {
+    public AjaxResult readOneSimAllFaultCheck(Seat seat, Sim sim) {
         Fault q = new Fault();
         q.setFaultType(Fault.Type.REAL_GZBW);
-        q.setSimType(s.getSimType());
+        q.setSimType(sim.getSimType());
         List<Fault> list = faultService.selectFaultList(q);
         List<Fault> listNG = new ArrayList<>();
         for (Fault f : list) {
-            AjaxResult ar = readOneSimOneFaultCheck(s, f);
+            AjaxResult ar = readOneSimOneFaultCheck(seat, sim, f);
             if (ar.isError()) {
                 listNG.add(f);
                 l.info("故障部位[" + f.getBindHardwareMsg() + "][" + f.getReplaceName() + "]未正确安装;");
@@ -691,15 +640,15 @@ public class CommSendService {
     /**
      * 检查读取。
      *
-     * @param s
+     * @param sim
      * @param f
      * @return
      */
-    public AjaxResult readOneSimOneFaultCheck(Sim s, Fault f) {
-        l.info("readOneSimOneFaultCheck s = {},f = {}", s, f);
-        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(s.getSimNum(), f.getBindHardwareMsg());
-        SimMsg sm2 = send(sm1, s, RETRY_COUNT_CHECK_ONE_FAULT, SLEEP_LONG);
-        return simReceiveService.getOneFaultCheck(sm2, s, f);
+    public AjaxResult readOneSimOneFaultCheck(Seat seat, Sim sim, Fault f) {
+        l.info("readOneSimOneFaultCheck s = {},f = {}", sim, f);
+        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
+        SimMsg sm2 = send(sm1, seat, sim, RETRY_COUNT_CHECK_ONE_FAULT, SLEEP_LONG);
+        return simReceiveService.getOneFaultCheck(sm2, sim, f);
     }
 
     /**
@@ -711,91 +660,93 @@ public class CommSendService {
         l.info("readOneSimAllFaultFirstTimeByExam re = {}", re);
         List<RealExamFault> list = realExamFaultService.listAllType2(re.getExamId());
         for (RealExamFault ref : list) {
-            Sim s = simService.selectSimBySimId(re.getSimId());
+            Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+            Sim sim = simService.selectSimBySimId(re.getSimId());
             Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
-            readOneSimOneFaultFirstTime(s, ref, f, null);
+            readOneSimOneFaultFirstTime(seat, sim, ref, f, null);
         }
     }
 
     /**
      * 第一次读取,作为出题值。debug模式。
      *
-     * @param s
+     * @param sim
      * @param faultIds
      */
-    public void readOneSimAllFaultFirstTimeBySim(Sim s, final String[] faultIds) {
-        l.info("readOneSimAllFaultFirstTimeBySim s = {}", s);
-        List<Fault> list = faultService.listType3(s.getSimType());
+    public void readOneSimAllFaultFirstTimeBySim(Seat seat, Sim sim, final String[] faultIds) {
+        l.info("readOneSimAllFaultFirstTimeBySim s = {}", sim);
+        List<Fault> list = faultService.listType3(sim.getSimType());
         for (Fault f : list) {
-            readOneSimOneFaultFirstTime(s, null, f, faultIds);
+            readOneSimOneFaultFirstTime(seat, sim, null, f, faultIds);
         }
     }
 
     /**
      * 第一次读取,作为出题值。
      *
-     * @param s
+     * @param sim
      * @param ref      debug调试模式为空。可以为空。
      * @param f
      * @param faultIds debug调试模式为空。
      */
-    public void readOneSimOneFaultFirstTime(Sim s, RealExamFault ref, Fault f, String[] faultIds) {
+    public void readOneSimOneFaultFirstTime(Seat seat, Sim sim, RealExamFault ref, Fault f, String[] faultIds) {
         l.info("readOneSimOneFaultFirstTime");
         // 读取一次当前电阻代表值作为出题值。
-        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(s.getSimNum(), f.getBindHardwareMsg());
-        SimMsg sm2 = send(sm1, s, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
-        simReceiveService.setFaultQuestionValue(sm2, s, ref, f, faultIds);
+        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
+        SimMsg sm2 = send(sm1, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, SLEEP_LONG);
+        simReceiveService.setFaultQuestionValue(sm2, sim, ref, f, faultIds);
     }
 
     /**
      * debug读取一个故障位置数据
      *
-     * @param simNum
+     * @param seatId
      * @param bindHardwareMsg
      * @return
-     * @throws IOException
      */
-    public SimMsg debugReadOneFaultResistance(final String simNum, final String bindHardwareMsg) {
-        SimMsg sm = commBuildService.buildSendMsgReadFaultResistance(simNum, bindHardwareMsg);
-        return send(sm, null, RETRY_COUNT_0, SLEEP_SHORT);
+    public SimMsg debugReadOneFaultResistance(final Long seatId, final String bindHardwareMsg) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        Sim sim = gggSimBySeatId(seatId);
+        SimMsg sm = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), bindHardwareMsg);
+        return send(sm, seat, null, RETRY_COUNT_0, SLEEP_SHORT);
     }
 
     /**
      * debug读取全部故障位置数据
      *
-     * @param simNum
+     * @param seatId
      * @return
-     * @throws IOException
      */
-    public List<SimMsg> debugReadAllFaultResistance(final String simNum) {
+    public List<SimMsg> debugReadAllFaultResistance(final Long seatId) {
+        Sim sim = gggSimBySeatId(seatId);
         List<SimMsg> list = new ArrayList<>();
-        String simType = simService.uniqueBySimNum(simNum).getSimType();
+        String simType = simService.uniqueBySimNum(sim.getSimNum()).getSimType();
         for (String b : getGZBWBySimType(simType)) {
-            list.add(debugReadOneFaultResistance(simNum, b));
+            list.add(debugReadOneFaultResistance(seatId, b));
         }
         return list;
     }
 
     /**
-     * @param s
+     * @param sim
      * @param reF
      * @param f
      * @param refState 中间轮询是null,交卷最后一次读取为finish状态。用来修改状态的。debug模式下执行为null。
      */
-    public void readOneSimOneFaultResistance(Sim s, RealExamFault reF, Fault f, String refState) {
+    public void readOneSimOneFaultResistance(Seat seat, Sim sim, RealExamFault reF, Fault f, String refState) {
         l.info("readOneSimOneFaultResistance");
-        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(s.getSimNum(), f.getBindHardwareMsg());
+        SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
         SimMsg sm2 = null;
         if (reF != null && refState != null) {
             if (RealExamFault.State.FINISH.equals(refState)) { // 是否最后一次读取。
-                sm2 = send(sm1, s, RETRY_COUNT_READ_ONE_RESISTANCE, SLEEP_SHORT);
+                sm2 = send(sm1, seat, sim, RETRY_COUNT_READ_ONE_RESISTANCE, SLEEP_SHORT);
             } else {
-                sm2 = send(sm1, s, RETRY_COUNT_0, SLEEP_SHORT);
+                sm2 = send(sm1, seat, sim, RETRY_COUNT_0, SLEEP_SHORT);
             }
         } else {
-            sm2 = send(sm1, s, RETRY_COUNT_READ_ONE_RESISTANCE, SLEEP_SHORT);
+            sm2 = send(sm1, seat, sim, RETRY_COUNT_READ_ONE_RESISTANCE, SLEEP_SHORT);
         }
-        simReceiveService.setFaultAnswerValue(sm2, s, reF, f, refState);
+        simReceiveService.setFaultAnswerValue(sm2, sim, reF, f, refState);
     }
 
     private long previousSendSleep = 0;
@@ -804,12 +755,13 @@ public class CommSendService {
      * send hex message
      *
      * @param sm              发送
-     * @param s               可以为空,更新最后发送/接收时间 用。
+     * @param seat            不能为空
+     * @param sim             可以为空!更新最后发送/接收时间 用。
      * @param retryTotalCount 重试次数
      * @param sleep           不使用传入0,不进行挂起。
      * @return
      */
-    public synchronized SimMsg send(final SimMsg sm, final Sim s, final int retryTotalCount, final long sleep) {
+    public synchronized SimMsg send(final SimMsg sm, final Seat seat, final Sim sim, final int retryTotalCount, final long sleep) {
         try {
             if (!config.isCommGlobal()) {
                 l.warn("isCommGlobal == false [模拟器通信被禁用!]");
@@ -818,6 +770,10 @@ public class CommSendService {
             if (sm == null || sm.getSendMsg() == null || StringUtils.isBlank(sm.getSendMsg())) {
                 throw new IllegalArgumentException("SimMsg IllegalArgument");
             }
+            // sim
+            if (seat == null) {
+                throw new IllegalArgumentException("seat is null");
+            }
             if (sleep < 0) {
                 throw new IllegalArgumentException("SimMsg sleep");
             }
@@ -826,21 +782,25 @@ public class CommSendService {
                 l.info("####SendMsg#### == [{}]", sm);
             }
             // 如果没有打开socket,顺道打开。正好后面要sleep
-            socketOldService.openSocket();
+            // SocketOldService实现
+            // socketOldService.openSocket();
+            socketService.openOne(seat.toSimSocketVo());
             {
-                // sleep ,追求顺序请求。
+                // sleep挂起线程,追求顺序请求。
                 if (sleep > 0 && previousSendSleep != 0L) {
                     Thread.sleep(previousSendSleep);
                 }
             }
             previousSendSleep = sleep;
-            Socket socket = socketOldService.getCachedSocket();
+            // SocketOldService实现
+            // Socket socket = socketOldService.getCachedSocket();
+            Socket socket = socketService.get(seat);
             InputStream is = socket.getInputStream();
             OutputStream os = socket.getOutputStream();
             os.write(hexStrToByteArrs(sm.getSendMsg()));
             sm.setSendTime(DateUtils.getNowDate());
-            if (s != null) {
-                simService.updateLastSentTime(s);
+            if (sim != null) {
+                simService.updateLastSentTime(sim);
             }
             byte[] buffer = new byte[LENGTH_24];
             int length = is.read(buffer);
@@ -849,6 +809,7 @@ public class CommSendService {
                 sbHex.append(String.format("%02X", buffer[i]));
             }
             String receiveWith0 = sbHex.toString();
+            // 原始带0的收到报文。
             sm.setReceiveOriginalMsg(receiveWith0);
             sm.setReceiveMsg(commReceiveService.removeRrefix0(receiveWith0));
             sm.setReceiveTime(DateUtils.getNowDate());
@@ -864,17 +825,23 @@ public class CommSendService {
                     return sm;
                 }
             }
-            if (s != null) {
-                simService.updateLastReceivedTime(s);
+            if (sim != null) {
+                simService.updateLastReceivedTime(sim);
             }
         } catch (InterruptedException | IOException e) {   // SocketTimeoutException
             e.printStackTrace();
             // 失败计数
-            l.info("fail sim data = {}", s);
-            boolean limit = socketOldService.commFailCountAdd1(Objects.requireNonNull(s).getSimId());
-            if (limit) {
-                simService.updateSimStateBySimId(s.getSimId(), Sim.State.OFFLINE);
-                socketOldService.commFailCountClearOne(s.getSimId());
+            l.info("fail sim data = {}", sim);
+            // SocketOldService实现
+            // boolean limit = socketOldService.commFailCountAdd1(Objects.requireNonNull(sim).getSimId());
+            SimSocketVo ssv = seat.toSimSocketVo();
+            failedCountService.plus1(ssv);
+            if (failedCountService.isReachedMax(ssv, retryTotalCount)) {
+                // 达到重试次数上限,认为模拟器离线
+                simService.updateSimStateBySimId(sim.getSimId(), Sim.State.OFFLINE);
+                // SocketOldService实现
+                // socketOldService.commFailCountClearOne(sim.getSimId());
+                failedCountService.reset0(ssv);
             }
             // 先考虑一台模拟器演示。
             // 进行重试 start
@@ -884,13 +851,14 @@ public class CommSendService {
             }
             if (sm.getRetryCount() < retryTotalCount) {
                 sm.retryCountPlus1();
-                send(sm, s, retryTotalCount, sleep);
+                send(sm, seat, sim, retryTotalCount, sleep);
                 l.warn("####RetryTotalCount重试#### = {}", sm);
             } else {
                 l.warn("####RetryTotalCount达到重试上限#### = {}", sm);
             }
             // 进行重试 end
         }
+        // 最后返回报文实体。
         return sm;
     }
 
@@ -907,28 +875,6 @@ public class CommSendService {
         return null;
     }
 
-
-    /**
-     * 等同于ping命令。
-     *
-     * @param ipV4
-     * @return
-     * @throws IOException
-     */
-    public boolean isReachable(String ipV4) {
-        InetAddress ia = null;
-        try {
-            ia = InetAddress.getByName(ipV4);
-            return ia.isReachable(2048);
-        } catch (UnknownHostException e) {
-            e.printStackTrace();
-            return false;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return false;
-        }
-    }
-
     /**
      * https://mvnrepository.com/artifact/com.infiniteautomation/modbus4j/3.0.3
      */
@@ -980,4 +926,21 @@ public class CommSendService {
         }
         return hexString.toString();
     }
+
+    /**
+     * todo:不存在部分数据
+     *
+     * @param seatId
+     * @return
+     */
+    public Sim gggSimBySeatId(Long seatId) {
+        Seat seat = seatService.selectSeatBySeatId(seatId);
+        AjaxResult ar01 = commCheckService.checkOneSeatState(seat, true);
+        if (ar01.isSuccess()) {
+            Long simId = seatService.selectSeatBySeatId(seatId).getCurrentSimId();
+            return simService.selectSimBySimId(simId);
+        } else {
+            throw new IllegalArgumentException("error gggSimBySeatId");
+        }
+    }
 }

+ 17 - 3
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/FailedCountService.java

@@ -1,5 +1,6 @@
 package com.ruoyi.sim.service.impl;
 
+import com.ruoyi.sim.domain.vo.SimSocketVo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -17,7 +18,18 @@ public class FailedCountService {
     private static final int INIT_SIZE = 16;
     private static HashMap<String, AtomicInteger> failedMap = new HashMap<>(INIT_SIZE);
 
-    public int plus1(final String key) {
+    /**
+     * @param key
+     * @param limit include limit
+     * @return
+     */
+    public boolean isReachedMax(final SimSocketVo ssv, final int limit) {
+        final String key = ssv.toKey();
+        return (failedMap.containsKey(key) && failedMap.get(key).get() >= limit);
+    }
+
+    public int plus1(final SimSocketVo ssv) {
+        final String key = ssv.toKey();
         if (!failedMap.containsKey(key)) {
             failedMap.put(key, new AtomicInteger(COUNT_ADD_1));
         } else {
@@ -26,7 +38,8 @@ public class FailedCountService {
         return failedMap.get(key).get();
     }
 
-    public int get(final String key) {
+    public int get(final SimSocketVo ssv) {
+        final String key = ssv.toKey();
         if (failedMap.containsKey(key)) {
             return failedMap.get(key).get();
         } else {
@@ -34,7 +47,8 @@ public class FailedCountService {
         }
     }
 
-    public void reset0(final String key) {
+    public void reset0(final SimSocketVo ssv) {
+        final String key = ssv.toKey();
         if (failedMap.containsKey(key)) {
             failedMap.get(key).set(COUNT_0);
         }

+ 10 - 6
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamCollectionService.java

@@ -36,7 +36,7 @@ public class RealExamCollectionService extends Ele6RYBaseService {
     @Lazy
     private CommSendService commSendService;
     @Autowired
-    private SocketOldService socketOldService;
+    private SocketService socketService;
 
     /**
      * 查询考试集合
@@ -321,8 +321,10 @@ public class RealExamCollectionService extends Ele6RYBaseService {
             closeAllExamExcludeId(ref.getExamCollectionId());
         }
         // Step 4:尝试打开所有Socket,提前准备,允许有打开失败的
-        // SocketOldService
-        AjaxResult ar1 = socketOldService.openSocket();
+        // SocketOldService实现。
+        // AjaxResult ar1 = socketOldService.openSocket();
+        //
+        AjaxResult ar1 = socketService.openAll();
         if (ar1.isError()) {
             return ar1;
         }
@@ -346,11 +348,11 @@ public class RealExamCollectionService extends Ele6RYBaseService {
     }
 
     /**
-     * 是否有一个open的考试集合。
+     * 是否有一个open的任何类型的集合。
      *
      * @return
      */
-    public boolean existOpened() {
+    public boolean existAtLeastOneOpened() {
         return (selectRealExamCollectionOpened() != null);
     }
 
@@ -387,7 +389,9 @@ public class RealExamCollectionService extends Ele6RYBaseService {
     }
 
     public AjaxResult closeAll() {
-        return socketOldService.closeSocket();
+        // SocketOldService实现
+        // return socketOldService.closeSocket();
+        return AjaxResult.success();
     }
 
     private void closeAllType(final String type) {

+ 9 - 7
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamService.java

@@ -310,19 +310,20 @@ public class RealExamService {
         // Step 3:如果有缓存Socket并且可用,使用缓存Socket,检查并建立Socket连接;否则返回对应错误。
         // Step 4:发送通用询问指令,询问是连接的哪种型号的哪一台模拟器;否则返回对应错误。
         RealExam re = selectRealExamByExamId(examId);
-        Sim s = simService.selectSimBySimId(re.getSimId());
+        Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+        Sim sim = simService.selectSimBySimId(re.getSimId());
         // Step 5:主动查询一次模拟器状态。更新模拟器在线/离线状态;否则返回对应错误。
         {
-            AjaxResult arE3 = commSendService.checkOneSimStateActive(s);
+            AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
             if (arE3.isError()) {
                 return arE3;
             }
         }
         // Step 6:修改exam表对应examId的一条数据,填充并锁定seat_id和sim_id值。
-        s = simService.selectSimBySimId(re.getSimId());
+        sim = simService.selectSimBySimId(re.getSimId());
         // Step 7:可换件检查,读取对应一台模拟器 所有故障部位值。检查模拟器所有的 真实的 故障部位 是否异常 或者 空值。特殊的故障部位要单独判断。
         if (SimDebugConfig.CHECK_REPLACE_EMPTY) {
-            AjaxResult arE2 = commSendService.readOneSimAllFaultCheck(s);
+            AjaxResult arE2 = commSendService.readOneSimAllFaultCheck(seat, sim);
             if (arE2.isError()) {
                 return arE2;
             }
@@ -443,15 +444,16 @@ public class RealExamService {
             }
         }
         // 检查一下模拟器状态。
-        Sim s = simService.selectSimBySimId(re.getSimId());
+        Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+        Sim sim = simService.selectSimBySimId(re.getSimId());
         // 如果模拟器离线
         {
-            AjaxResult arE3 = commSendService.checkOneSimStateActive(s);
+            AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
             if (arE3.isError()) {
                 return arE3;
             }
         }
-        s = simService.selectSimBySimId(re.getSimId());
+        sim = simService.selectSimBySimId(re.getSimId());
         // 最后读取一下模拟器电阻值。
         commSendService.readOneExamAtLast(re);
         if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) {

+ 76 - 8
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SeatService.java

@@ -1,9 +1,8 @@
 package com.ruoyi.sim.service.impl;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
 
+import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.DateUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -102,11 +101,80 @@ public class SeatService {
 
     public List<Seat> listAllEnable() {
         List<Seat> list = new ArrayList<>();
-        seatMapper.selectSeatList(new Seat())
-                .stream()
-                .filter(Objects::nonNull)
-                .filter(s -> !StringUtils.equals(Seat.SocketState.DISABLE, s.getSeatRs485SocketState()))
-                .forEach(list::add);
+        seatMapper.selectSeatList(new Seat()).stream().filter(Objects::nonNull).filter(s -> !StringUtils.equals(Seat.SocketState.DISABLE, s.getSeatRs485SocketState())).forEach(list::add);
         return list;
     }
+
+    /**
+     * 获取所有没有被禁用的 座 列表
+     *
+     * @return
+     */
+    public AjaxResult listAllEnableAj() {
+        return AjaxResult.success(listAllEnable());
+    }
+
+    public Seat uniqueBySeatNum(final Integer seatNum) {
+        Seat q = new Seat();
+        q.setSeatNum(seatNum);
+        List<Seat> list = seatMapper.selectSeatList(q);
+        if (list.isEmpty()) {
+            return null;
+        } else if (list.size() == 1) {
+            return list.get(0);
+        } else {
+            throw new IllegalArgumentException("Seat数据错误。");
+        }
+    }
+
+    public Seat uniqueByIpAndPort(final String rs485Ip, final int rs485Port) {
+        Seat q = new Seat();
+        q.setSeatRs485Ip(rs485Ip);
+        q.setSeatRs485Port(rs485Port);
+        List<Seat> list = seatMapper.selectSeatList(q);
+        if (list.isEmpty()) {
+            return null;
+        } else if (list.size() == 1) {
+            return list.get(0);
+        } else {
+            throw new IllegalArgumentException("Seat数据错误。");
+        }
+    }
+
+    public void updateSimIdBySeatNum(final Integer seatNum, final Long simId) {
+        Seat f = uniqueBySeatNum(seatNum);
+        f.setCurrentSimId(simId);
+        updateSeat(f);
+    }
+
+    public void updateSocketStateBySeatNum(final Integer seatNum, final String socketState) {
+        Seat f = uniqueBySeatNum(seatNum);
+        f.setSeatRs485SocketState(socketState);
+        updateSeat(f);
+    }
+
+    public int updateAllEnableState(final String socketState) {
+        List<Seat> list = listAllEnable();
+        for (Seat seat : list) {
+            seat.setSeatRs485SocketState(socketState);
+            seatMapper.updateSeat(seat);
+        }
+        return list.size();
+    }
+
+    public List<String> listAllRs485Ip() {
+        List<Seat> list = listAllEnable();
+        Set<String> ips = new HashSet<>();
+        list.forEach(seat -> ips.add(seat.getSeatRs485Ip()));
+        return ips.stream().toList();
+    }
+
+    public void updateSocketStateByRs485Ip(final String rs485Ip, final String socketState) {
+        Seat q = new Seat();
+        q.setSeatRs485Ip(rs485Ip);
+        selectSeatList(q).forEach(seat -> {
+            seat.setSeatRs485SocketState(socketState);
+            seatMapper.updateSeat(seat);
+        });
+    }
 }

+ 16 - 9
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SimService.java

@@ -91,7 +91,12 @@ public class SimService {
     }
 
     // -------------------------------- tom add  --------------------------------
+    private static final Logger l = LoggerFactory.getLogger(SimService.class);
 
+    /**
+     * @param simId
+     * @return true:存在,false:不存在
+     */
     public boolean existBySimId(final Long simId) {
         if (simId == null) {
             return false;
@@ -128,10 +133,13 @@ public class SimService {
         Sim q = new Sim();
         q.setSimNum(simNum);
         List<Sim> list = simMapper.selectSimList(q);
-        if (!list.isEmpty()) {
+        if (list.isEmpty()) {
+            return null;
+        } else if (list.size() == 1) {
             return list.get(0);
+        } else {
+            throw new IllegalArgumentException("Sim数据错误。");
         }
-        return null;
     }
 
     public List<Sim> listAll() {
@@ -154,9 +162,6 @@ public class SimService {
                 .filter(Objects::nonNull)
                 .filter(s -> !StringUtils.equals(Sim.State.DISABLE, s.getSimState()))
                 .forEach(list::add);
-        for (Sim sim : list) {
-            l.info("listAllEnable : {}", sim);
-        }
         return list;
     }
 
@@ -211,13 +216,11 @@ public class SimService {
         return Sim.STATE_SET.contains(simState);
     }
 
-    private static final Logger l = LoggerFactory.getLogger(SimService.class);
-
     @Transactional
-    public int updateSimStateBySimId(Long simId, String simState) {
+    public int updateSimStateBySimId(final Long simId, final String simState) {
         // check
         if (checkState(simState)) {
-
+            throw new IllegalArgumentException("simState wrong!");
         }
         //
         Sim q = selectSimBySimId(simId);
@@ -228,6 +231,10 @@ public class SimService {
         return updateSim(q);
     }
 
+    public int updateSimStateBySimNum(final String simNum, final String simState) {
+        return updateSimStateBySimId(uniqueBySimNum(simNum).getSimId(), simState);
+    }
+
     public boolean isSimStateBySimId(Long simId, String simState) {
         // check
         if (checkState(simState)) {

+ 37 - 52
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SocketService.java

@@ -2,11 +2,9 @@ package com.ruoyi.sim.service.impl;
 
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.sim.config.SimConfig;
-import com.ruoyi.sim.constant.CommConst;
 import com.ruoyi.sim.domain.Seat;
 import com.ruoyi.sim.domain.vo.SimSocketVo;
 import com.ruoyi.sim.domain.vo.SocketWrapValue;
-import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -50,29 +48,26 @@ public class SocketService {
     private SeatService seatService;
 
     /**
-     * @param ip   ip v4.
-     * @param port
+     * @param ssv
      * @return true:socket ok!
      */
-    public boolean isOk(final String ip, final Integer port) {
-        final String key = buildKey(ip, port);
-        if (cachedMap.containsKey(key)) {
-            Socket s = cachedMap.get(key).getSocket();
+    public boolean isOk(final SimSocketVo ssv) {
+        if (cachedMap.containsKey(ssv.toKey())) {
+            Socket s = cachedMap.get(ssv.toKey()).getSocket();
             if (s != null) {
-                return (s.isConnected() && s.isBound() && !s.isClosed() && !isTimeout(ip, port));
+                return (s.isConnected() && s.isBound() && !s.isClosed() && !isTimeout(ssv));
             }
         }
         return false;
     }
 
-    public boolean isNotOk(final String ip, final Integer port) {
-        return !isOk(ip, port);
+    public boolean isNotOk(final SimSocketVo ssv) {
+        return !isOk(ssv);
     }
 
-    public boolean isTimeout(final String ip, final Integer port) {
-        final String key = buildKey(ip, port);
-        if (cachedMap.containsKey(key)) {
-            Long cached = cachedMap.get(key).getOkTimeMillis();
+    public boolean isTimeout(SimSocketVo ssv) {
+        if (cachedMap.containsKey(ssv.toKey())) {
+            Long cached = cachedMap.get(ssv.toKey()).getOkTimeMillis();
             if (cached == null || cached == 0L) {
                 return true;
             }
@@ -84,21 +79,6 @@ public class SocketService {
     }
 
     /**
-     * @param ip   ip v4.
-     * @param port
-     * @return
-     */
-    public String buildKey(final String ip, final Integer port) {
-        if (StringUtils.isBlank(ip)) {
-            throw new IllegalArgumentException("ip error");
-        }
-        if (port == null || port <= CommConst.PORT_MIN || port >= CommConst.PORT_MAX) {
-            throw new IllegalArgumentException("port error");
-        }
-        return ip + ":" + port;
-    }
-
-    /**
      * @param ssv
      * @return
      */
@@ -110,28 +90,27 @@ public class SocketService {
         }
         //
         try {
-            final String ip = ssv.getIp();
-            final Integer port = ssv.getPort();
-            final String key = buildKey(ip, port);
-            if (isNotOk(ip, port)) {
-                l.info("openSocket cachedSocket is not ok!new socket ip = {}:{}!", ip, port);
+            if (isNotOk(ssv)) {
+                final String key = ssv.toKey();
+                l.info("openSocket cachedSocket is not ok!new socket ip = {}:{}!", ssv.getIp(), ssv.getPort());
                 closeOne(ssv);
-                Socket s = new Socket(ip, port);
+                Socket s = new Socket(ssv.getIp(), ssv.getPort());
                 s.setSoTimeout(SOCKET_TIME_OUT);
-                SocketWrapValue value = new SocketWrapValue(ip, port, s, System.currentTimeMillis());
+                SocketWrapValue value = new SocketWrapValue(ssv.getIp(), ssv.getPort(), s, System.currentTimeMillis());
                 cachedMap.put(key, value);
                 // failed count reset.
-                failedCountService.reset0(key);
+                failedCountService.reset0(ssv);
             } else {
-                l.info("openSocket cachedSocket cache ok!cached socket ip = {}:{}!", ip, port);
+                l.info("openSocket cachedSocket cache ok!cached socket ip = {}:{}!", ssv.getIp(), ssv.getPort());
             }
+            Seat seat = seatService.uniqueByIpAndPort(ssv.getIp(), ssv.getPort());
+            seat.setSeatRs485SocketState(Seat.SocketState.ONLINE);
+            seatService.updateSeat(seat);
+            return AjaxResult.success("Socket[" + ssv.getIp() + ":" + ssv.getPort() + "],创建成功!");
         } catch (IOException e) {
             l.error(ssv.toString());
-            throw new RuntimeException(e);
-        } finally {
-
+            return AjaxResult.error("Socket[" + ssv.getIp() + ":" + ssv.getPort() + "],创建失败!");
         }
-        return AjaxResult.success("openOneSocket Success!");
     }
 
     /**
@@ -157,7 +136,7 @@ public class SocketService {
             return AjaxResult.error("模拟器通信被禁用!");
         }
         String msgOk = "关闭Socket成功!";
-        final String key = buildKey(ssv.getIp(), ssv.getPort());
+        final String key = ssv.toKey();
         try {
             if (cachedMap.containsKey(key)) {
                 Socket s = cachedMap.get(key).getSocket();
@@ -170,7 +149,7 @@ public class SocketService {
         } finally {
             cachedMap.remove(key);
             // failed count reset.
-            failedCountService.reset0(key);
+            failedCountService.reset0(ssv);
             return AjaxResult.success(msgOk);
         }
     }
@@ -185,20 +164,26 @@ public class SocketService {
         for (Seat s : allSeat) {
             closeOne(new SimSocketVo(s.getSeatRs485Ip(), s.getSeatRs485Port()));
         }
+        failedCountService.resetAll();
         return AjaxResult.success(msgOk);
     }
 
+    public Socket get(final Seat seat) {
+        if (seat == null) {
+            throw new IllegalArgumentException("seat is null");
+        }
+        return get(new SimSocketVo(seat.getSeatRs485Ip(), seat.getSeatRs485Port()));
+    }
+
     /**
-     * @param ip
-     * @param port
+     * @param ssv
      * @return
      */
-    public Socket get(final String ip, final Integer port) {
-        final String key = buildKey(ip, port);
-        if (isNotOk(ip, port)) {
-            openOne(new SimSocketVo(ip, port));
+    public Socket get(final SimSocketVo ssv) {
+        if (isNotOk(ssv)) {
+            openOne(ssv);
         }
-        return cachedMap.get(key).getSocket();
+        return cachedMap.get(ssv.toKey()).getSocket();
     }
 
     /**