Quellcode durchsuchen

提交新485布局变更。

tom vor 2 Monaten
Ursprung
Commit
a42d9f13d2

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

@@ -1,5 +1,10 @@
 package com.ruoyi.sim.constant;
 
+/**
+ * 雷电扩展坞MAC
+ * 192.168.1.153
+ * 00-E0-4C-68-01-AD
+ */
 public interface CommConst {
 
     String PREFIX = "AA";
@@ -71,19 +76,19 @@ public interface CommConst {
      * 请求间隔睡眠时间-long
      * default:200L
      */
-    Long SLEEP_LONG = 300L;
+    Long SLEEP_LONG = 1000L;
     /**
      * 请求间隔睡眠时间-mid
      * default:100L
      */
-    Long SLEEP_MID = 300L;
+    Long SLEEP_MID = 1000L;
     /**
      * 请求间隔睡眠时间-short
      * default:64L
      */
-    Long SLEEP_SHORT = 300L;
+    Long SLEEP_SHORT = 1000L;
 
-    int SOCKET_TIME_OUT = 200;
+    int SOCKET_TIME_OUT = 50;
 
     int PING_TIME_OUT = 1000;
 
@@ -102,6 +107,7 @@ public interface CommConst {
     int RETRY_COUNT_WRITE_ONE_FAULT = 2;
     int RETRY_COUNT_READ_ONE_RESISTANCE = 4;
     int RETRY_COUNT_QUERY_SN_IMPORTANT = 1;
+    int RETRY_COUNT_WHICH_SIM_IMPORTANT = 3;
     int RETRY_COUNT_0 = 0;
 
     int OFFLINE_LIMIT = 6;

+ 19 - 2
ruoyi-sim/src/main/java/com/ruoyi/sim/controller/TestIotController.java

@@ -4,6 +4,7 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.sim.domain.Fault;
 import com.ruoyi.sim.domain.RealExam;
+import com.ruoyi.sim.domain.Seat;
 import com.ruoyi.sim.domain.vo.SimSocketVo;
 import com.ruoyi.sim.service.impl.*;
 import io.swagger.annotations.Api;
@@ -34,6 +35,12 @@ public class TestIotController extends BaseController {
     @Autowired
     @Lazy
     private SocketService socketService;
+    @Autowired
+    @Lazy
+    private CommCheckService commCheckService;
+    @Autowired
+    @Lazy
+    private SeatService seatService;
 
     @ApiOperation("debug666")
     @GetMapping(value = "/{codeId}")
@@ -91,15 +98,25 @@ public class TestIotController extends BaseController {
                 //
             }
             case 26: {
-                AjaxResult aj = socketService.openAll();
+                AjaxResult aj = socketService.tryOpenAll();
                 return aj;
             }
             case 27: {
                 socketService.closeOne(new SimSocketVo("192.168.1.202", 11001));
                 socketService.closeOne(new SimSocketVo("192.168.1.202", 11008));
             }
+            case 100: {
+                return socketService.tryOpenAll();
+            }
+            case 101: {
+                Seat seat = seatService.selectSeatBySeatId(10L);
+                return commCheckService.checkOneSeatState(seat, true);
+            }
+            case 102: {
+                return commCheckService.checkAllSeatAndSimState();
+            }
         }
-        return AjaxResult.success();
+        return AjaxResult.success("ZZZZZZZZZZZZZZZZZZZZ");
     }
 
     @ApiOperation("testIot通信")

+ 17 - 13
ruoyi-sim/src/main/java/com/ruoyi/sim/domain/RealExam.java

@@ -30,6 +30,12 @@ public class RealExam extends BaseEntity {
     private Long examCollectionId;
 
     /**
+     * 模拟器类型
+     */
+    @Excel(name = "模拟器类型")
+    private String simType;
+
+    /**
      * 学员ID/用户ID
      */
     @Excel(name = "学员ID/用户ID")
@@ -48,20 +54,9 @@ public class RealExam extends BaseEntity {
     private Long simId;
 
     /**
-     * 考试状态:
-     * [0]-未登录,
-     * [1]-已登录,
-     * [2]-模拟器检查并下发故障中,
-     * [3]:模拟器检查OK可开考,
-     * [4]-答题中,
-     * [5]-已交卷,
-     * [6]-计算成绩中,
-     * [7]-获取到成绩报告,
-     * [80]-教师标记缺考,
-     * [81]-登录未开始答题,
-     * [90]-模拟器异常结束
+     * 考试状态:[0]-未登录,[1]-已登录,[2]-模拟器检查并下发故障中,[3]:模拟器检查OK可开考,[4]-答题中,[5]-已交卷,[6]-计算成绩中,[7]-获取到成绩报告,[80]-教师标记缺考,[81]-登录未开始答题,[90]-模拟器异常结束
      */
-    @Excel(name = "考试状态")
+    @Excel(name = "考试状态:[0]-未登录,[1]-已登录,[2]-模拟器检查并下发故障中,[3]:模拟器检查OK可开考,[4]-答题中,[5]-已交卷,[6]-计算成绩中,[7]-获取到成绩报告,[80]-教师标记缺考,[81]-登录未开始答题,[90]-模拟器异常结束")
     private String examStatus;
 
     /**
@@ -120,6 +115,14 @@ public class RealExam extends BaseEntity {
         return examCollectionId;
     }
 
+    public void setSimType(String simType) {
+        this.simType = simType;
+    }
+
+    public String getSimType() {
+        return simType;
+    }
+
     public void setUserId(Long userId) {
         this.userId = userId;
     }
@@ -205,6 +208,7 @@ public class RealExam extends BaseEntity {
         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                 .append("examId", getExamId())
                 .append("examCollectionId", getExamCollectionId())
+                .append("simType", getSimType())
                 .append("userId", getUserId())
                 .append("seatId", getSeatId())
                 .append("simId", getSimId())

+ 14 - 8
ruoyi-sim/src/main/java/com/ruoyi/sim/domain/SimMsg.java

@@ -3,7 +3,6 @@ package com.ruoyi.sim.domain;
 import java.util.Date;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
@@ -98,9 +97,9 @@ public class SimMsg extends BaseEntity {
     private Integer retryCount = 0;
 
     /**
-     * default value false.
+     * default.
      */
-    private Boolean ok = false;
+    private Integer result = Result.DEFAULT_VALUE;
 
     /**
      * default ""
@@ -211,12 +210,12 @@ public class SimMsg extends BaseEntity {
         return retryCount;
     }
 
-    public Boolean getOk() {
-        return ok;
+    public Integer getResult() {
+        return result;
     }
 
-    public void setOk(Boolean ok) {
-        this.ok = ok;
+    public void setResult(Integer result) {
+        this.result = result;
     }
 
     public String getErrorMsg() {
@@ -243,13 +242,20 @@ public class SimMsg extends BaseEntity {
                 .append("receiveOriginalMsg", receiveOriginalMsg)
                 .append("receiveTime", receiveTime)
                 .append("retryCount", retryCount)
-                .append("ok", ok)
+                .append("result", result)
                 .append("errorMsg", errorMsg)
                 .toString();
     }
 
 // -------------------------------- tom add  --------------------------------
 
+    public interface Result {
+        Integer DEFAULT_VALUE = 0;
+        Integer SUCCESS = 200;
+        Integer RECEIVE_CHECK_FAIL = 500;
+        Integer SOCKET_EXCEPTION = 501;
+    }
+
     public SimMsg() {
     }
 

+ 99 - 74
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommCheckService.java

@@ -1,7 +1,6 @@
 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;
@@ -11,11 +10,13 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.List;
+import java.util.Objects;
 
 import static com.ruoyi.sim.constant.CommConst.RETRY_COUNT_0;
 import static com.ruoyi.sim.constant.CommConst.RETRY_COUNT_QUERY_SN_IMPORTANT;
@@ -26,8 +27,6 @@ public class CommCheckService {
     private static final Logger l = LoggerFactory.getLogger(CommCheckService.class);
 
     @Autowired
-    private SimConfig config;
-    @Autowired
     private SeatService seatService;
     @Autowired
     private SimService simService;
@@ -37,6 +36,8 @@ public class CommCheckService {
     private CommSendService commSendService;
     @Autowired
     private FailedCountService failedCountService;
+    @Autowired
+    private SocketService socketService;
 
     /**
      * 等同于ping命令。
@@ -91,6 +92,7 @@ public class CommCheckService {
         if (pingIsReachable(rs485Ip)) {
             return AjaxResult.success();
         } else {
+            // 更新SocketState
             seatService.updateSocketStateByRs485Ip(rs485Ip, Seat.SocketState.OFFLINE);
             return AjaxResult.error("RS485物联网网关[" + rs485Ip + "]无法连接!");
         }
@@ -111,101 +113,83 @@ public class CommCheckService {
      * 检查一个座次状况。
      * 维护 seat表-socket_state字段;
      * 维护 seat表-sim_id字段;
+     * 维护 sim表-Online、Offline状态;
      *
-     * @param seat
+     * @param seat      座次
      * @param important true:重要的场景 开始考试 重试次数不同,也会进行序列号检查。false:不重要场景 定时巡查。
      * @return
      */
+    @Transactional
     public AjaxResult checkOneSeatState(final Seat seat, final boolean important) {
         // check args.
         if (seat == null) {
             throw new IllegalArgumentException("seat is null");
         }
         //
+        socketService.tryOpenAll();
+        //
         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;
+            retryTotalCount = CommConst.RETRY_COUNT_WHICH_SIM_IMPORTANT;
         } else {
-            retryTotalCount = RETRY_COUNT_0;
+            retryTotalCount = CommConst.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:
+        Integer result = smR01.getResult();
+        if (Objects.equals(result, SimMsg.Result.SUCCESS)) {
+            final String simNum = CommParseUtils.subSimNum(smR01);
+            Sim sim = simService.uniqueBySimNum(simNum);
+            if (sim == null) {
+                return AjaxResult.error("找不到模拟器[" + simNum + "]对应数据。");
+            } else {
+                l.info("在座次[{}]上发现模拟器[{}]", seat.getSeatNum(), sim.getSimNum());
+            }
+            // 更新SimId
+            seatService.updateSimIdBySeatNum(seat.getSeatNum(), sim.getSimId());
+            // 更新Sim状态
+            simService.updateSimStateBySimId(sim.getSimId(), Sim.State.ONLINE);
+            return AjaxResult.success("成功,检查一个座次[" + seat.getSeatNum() + "]OK!模拟器[" + sim.getSimNum() + "]在线。");
+        } else if (Objects.equals(result, SimMsg.Result.RECEIVE_CHECK_FAIL)) {
+            return AjaxResult.error("失败,报文回复异常。");
+        } else if (Objects.equals(result, SimMsg.Result.SOCKET_EXCEPTION)) {
+            // 更新SimId
+            seatService.updateSimIdBySeatNum(seat.getSeatNum(), Sim.ID_0);
+            return AjaxResult.success("成功,检查一个座次[" + seat.getSeatNum() + "]OK!未连接模拟器。");
         }
-        // 更新SimId
-        seatService.updateSimIdBySeatNum(seat.getSeatNum(), sim.getSimId());
-        return AjaxResult.success("成功,检查一个座次[" + seat.getSeatNum() + "]OK!");
+        return AjaxResult.error("失败");
     }
 
     /**
-     * 默认Seat中已经有CurrentSimId数据
+     * 纯数据库查询,模拟器是否在线。
+     * 模拟器是否被禁用。
      *
-     * @param seat
-     * @param important
+     * @param simId
      * @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!");
+    public AjaxResult checkOneSimOnlineState(final Long simId) {
+        Sim sim = simService.selectSimBySimId(simId);
+        if (sim != null) {
+            switch (sim.getSimState()) {
+                case Sim.State.ONLINE: {
+                    return AjaxResult.success("模拟器[" + sim.getSimNum() + "]在线!");
+                }
+                case Sim.State.OFFLINE: {
+                    return AjaxResult.success("模拟器[" + sim.getSimNum() + "]离线!");
+                }
+                case Sim.State.DISABLE: {
+                    return AjaxResult.error("模拟器[" + sim.getSimNum() + "]禁用!");
+                }
+            }
         }
-        return AjaxResult.error("失败,检查一个模拟器[" + sim.getSimNum() + "]在线状态执行错误!");
+        return AjaxResult.error("模拟器[" + Objects.requireNonNull(sim).getSimNum() + "]XXXX!");
     }
 
     /**
      * 默认Seat中已经有CurrentSimId数据
      * 检查回应报文模拟器类型是否正确。
      *
-     * @param seat
+     * @param seat          座次
      * @param important
      * @param targetSimType 期望模拟器目标类型
      * @return
@@ -231,10 +215,10 @@ public class CommCheckService {
             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);
+        SimMsg smS = commBuildService.buildSendMsgReadSimType(sim.getSimNum());
+        SimMsg smR = commSendService.send(smS, seat, sim, retryTotalCount, CommConst.SLEEP_SHORT);
+        if (StringUtils.isNotBlank(smR.getReceiveMsg())) {
+            final String content = CommParseUtils.subContentData(smR);
             switch (targetSimType) {
                 case Sim.TYPE_0001 -> {
                     if (content.startsWith(CommConst.TYPE_0001_SN_PREFIX) && content.endsWith(sim.getSimNum())) {
@@ -264,13 +248,54 @@ public class CommCheckService {
     }
 
     /**
+     * 默认Seat中已经有CurrentSimId数据
+     *
+     * @param seat
+     * @param important
+     * @return
+     */
+    @Transactional
+    public AjaxResult checkOneSimOnlineState(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() + "]在线状态执行错误!");
+    }
+
+    /**
      * [定时执行]查找所有没有被手动禁用的座次 和 座次上的模拟器。
      */
-    public void checkAllSeatAndSimState() {
+    public AjaxResult checkAllSeatAndSimState() {
         List<Seat> list = seatService.listAllEnable();
         list.forEach(seat -> {
             checkOneSeatState(seat, false);
-            checkOneSimState(seat, false);
+            // checkOneSimState(seat, false);
         });
+        return AjaxResult.success("检查完毕,成功!");
     }
 }

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

@@ -204,7 +204,7 @@ public class CommReceiveService {
     }
 
     /**
-     * check receiveMsg
+     * 有返回报文的情况下,检查Receive的报文。
      *
      * @param receiveMsg
      * @return

+ 36 - 21
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommSendService.java

@@ -3,7 +3,6 @@ package com.ruoyi.sim.service.impl;
 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;
@@ -129,7 +128,7 @@ public class CommSendService {
         //
         // 打开socket
         {
-            socketService.openAll();
+            socketService.tryOpenAll();
         }
         //
         Seat seat = seatService.selectSeatBySeatId(seatId);
@@ -224,7 +223,8 @@ public class CommSendService {
      */
     public void scheduledConnect() {
         l.info("scheduled####Connect  连接情况 的 定时任务");
-        if (!SimDebugConfig.SCHEDULED_CONNECT) {
+//        if (!SimDebugConfig.SCHEDULED_CONNECT) {
+        if (true) {
             l.info("连接情况 的 定时任务被禁用!");
             return;
         }
@@ -250,25 +250,31 @@ public class CommSendService {
         }
         // SocketOldService实现
         // socketOldService.openSocket();
-        socketService.openAll();
+
         // SocketOldService实现
 //        if (socketOldService.isCachedSocketOk()) {
 //            checkAllSeatAndSimState();
 //        }
+
+        socketService.tryOpenAll();
         commCheckService.checkAllSeatAndSimState();
     }
 
     /**
      * 主动更新模拟器状态。
+     * <p>
+     * <p>
+     * 主动查询一次模拟器状态。更新模拟器在线/离线状态;否则返回对应错误。
      * todo:需要重新考虑
      *
      * @param seat
      * @return
      */
+    @Deprecated
     public AjaxResult checkOneSimStateActive(Seat seat) {
         // 这句可能会调整模拟器状态,后面需要重新查询。
         commCheckService.checkOneSeatState(seat, true);
-        AjaxResult ar1 = commCheckService.checkOneSimState(seat, true);
+        AjaxResult ar1 = commCheckService.checkOneSimOnlineState(seat, true);
         if (ar1.isError()) {
             return ar1;
         }
@@ -415,9 +421,9 @@ public class CommSendService {
         for (String b : getGZBWBySimType(sim.getSimType())) {
             SimMsg sm = debugClearOneFault(seatId, b);
             list.add(sm);
-            if (sm != null && !sm.getOk()) {
-
-            }
+//            if (sm != null && !sm.isOk()) {
+//
+//            }
         }
         debugFaultService.deleteAll();
         return AjaxResult.success("清除成功,清除当前模拟器所有故障!");
@@ -513,7 +519,7 @@ public class CommSendService {
         // check sim
         // 打开socket
         {
-            socketService.openAll();
+            socketService.tryOpenAll();
         }
         // Step 1 主动查询一次模拟器状态。
         {
@@ -645,7 +651,7 @@ public class CommSendService {
      * @return
      */
     public AjaxResult readOneSimOneFaultCheck(Seat seat, Sim sim, Fault f) {
-        l.info("readOneSimOneFaultCheck s = {},f = {}", sim, f);
+        l.info("readOneSimOneFaultCheck sim = {},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);
@@ -779,7 +785,7 @@ public class CommSendService {
             }
             // log.
             {
-                l.info("####SendMsg#### == [{}]", sm);
+                l.info("####发送#### == Seat[{}],SimMsg[{}]", seat, sm);
             }
             // 如果没有打开socket,顺道打开。正好后面要sleep
             // SocketOldService实现
@@ -815,30 +821,39 @@ public class CommSendService {
             sm.setReceiveTime(DateUtils.getNowDate());
             // log.
             {
-                l.info("####ReceiveMsg#### = [{}]", sm);
-            }
-            {
                 AjaxResult ar = commReceiveService.checkReceiveMsg(sm.getReceiveMsg());
                 if (ar.isError()) {
                     // todo:
-                    l.warn("####Fail#### = {}", sm);
+                    l.warn("####接收错误#### = {}", sm);
+                    sm.setResult(SimMsg.Result.RECEIVE_CHECK_FAIL);
                     return sm;
+                } else {
+                    l.info("####接收成功#### = {}", sm);
                 }
             }
             if (sim != null) {
                 simService.updateLastReceivedTime(sim);
             }
-        } catch (InterruptedException | IOException e) {   // SocketTimeoutException
+            sm.setResult(SimMsg.Result.SUCCESS);
+            // 最后返回报文实体。
+            return sm;
+        } catch (InterruptedException | IOException e) {
+            l.error("SocketTimeoutException");// SocketTimeoutException
             e.printStackTrace();
-            // 失败计数
-            l.info("fail sim data = {}", sim);
+            sm.setResult(SimMsg.Result.SOCKET_EXCEPTION);
+            if (sim != null) {
+                l.info("fail sim.getSimId() = {}", sim.getSimId());
+            }
             // 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);
+                if (sim != null) {
+                    simService.updateSimStateBySimId(sim.getSimId(), Sim.State.OFFLINE);
+                }
                 // SocketOldService实现
                 // socketOldService.commFailCountClearOne(sim.getSimId());
                 failedCountService.reset0(ssv);
@@ -857,9 +872,9 @@ public class CommSendService {
                 l.warn("####RetryTotalCount达到重试上限#### = {}", sm);
             }
             // 进行重试 end
+            // 最后返回报文实体。
+            return sm;
         }
-        // 最后返回报文实体。
-        return sm;
     }
 
     /**

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

@@ -324,7 +324,7 @@ public class RealExamCollectionService extends Ele6RYBaseService {
         // SocketOldService实现。
         // AjaxResult ar1 = socketOldService.openSocket();
         //
-        AjaxResult ar1 = socketService.openAll();
+        AjaxResult ar1 = socketService.tryOpenAll();
         if (ar1.isError()) {
             return ar1;
         }

+ 102 - 22
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamService.java

@@ -13,6 +13,7 @@ import com.ruoyi.sim.domain.vo.RealExamVo;
 import com.ruoyi.sim.domain.vo.StudentRealExamIngVo;
 import com.ruoyi.sim.domain.vo.StudentRealExamPostVo;
 import com.ruoyi.sim.domain.vo.StudentRealExamPreVo;
+import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -113,7 +114,12 @@ public class RealExamService {
     @Lazy
     private CommSendService commSendService;
     @Autowired
+    @Lazy
+    private CommCheckService commCheckService;
+    @Autowired
     private SimConfig simConfig;
+    @Autowired
+    private SocketService socketService;
 
     /**
      * examId 是否有效。
@@ -287,10 +293,12 @@ public class RealExamService {
      * [学生]开始考试
      *
      * @param examId
-     * @return RealExam
+     * @param studentBindIp
+     * @param type
+     * @return
      */
     @Transactional
-    public AjaxResult studentStartRealExam(final Long examId, final String ip, final String type) {
+    public AjaxResult studentStartRealExam(final Long examId, final String studentBindIp, final String type) {
         l.info("studentStartRealExam = {}", examId);
         // todo: 暂时没有解决方案 检查 考试的sim和seat,是否正确对应。
         {
@@ -299,28 +307,99 @@ public class RealExamService {
             // l.info("fake re = {}", re);
         }
         // check id data.
-        // Step 1:检查examId有效性。
+        // Step :检查参数有效性。
         {
-            AjaxResult arE1 = checkExamId(examId);
-            if (arE1.isError()) {
-                return arE1;
+            AjaxResult ar01 = checkExamId(examId);
+            if (ar01.isError()) {
+                return ar01;
             }
         }
-        // Step 2:ping通 路由器、学员端电脑、RS485;否则返回对应错误。
-        // Step 3:如果有缓存Socket并且可用,使用缓存Socket,检查并建立Socket连接;否则返回对应错误。
-        // Step 4:发送通用询问指令,询问是连接的哪种型号的哪一台模拟器;否则返回对应错误。
+        if (StringUtils.isBlank(studentBindIp)) {
+            return AjaxResult.error("ip地址无效。");
+        }
         RealExam re = selectRealExamByExamId(examId);
-        Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
+        Seat seat = seatService.uniqueByBindIp(studentBindIp);
+        {
+            if (seat == null) {
+                throw new IllegalArgumentException("XXX");
+            }
+        }
+        // Step :ping通 路由器。
+        {
+            AjaxResult ar = commCheckService.checkRouterState(simConfig.getRouterIp());
+            if (ar.isError()) {
+                return ar;
+            }
+        }
+        // Step :ping通 学员端电脑。
+        {
+            AjaxResult ar = commCheckService.checkPingStudentPcState(studentBindIp);
+            if (ar.isError()) {
+                return ar;
+            }
+        }
+        // Step :ping通 RS485。
+        {
+            AjaxResult ar = commCheckService.checkPingRs485State(seat.getSeatRs485Ip());
+            if (ar.isError()) {
+                // todo:重复
+                // 更新SimId
+                seatService.updateSimIdBySeatNum(seat.getSeatNum(), Seat.ID_0);
+                // 更新SocketState
+                seatService.updateSocketStateBySeatNum(seat.getSeatNum(), Seat.SocketState.OFFLINE);
+                return ar;
+            } else {
+                // Ping通不代表在线,Socket连接建立表示在线。
+            }
+        }
+        // Step :如果有缓存Socket并且可用,使用缓存Socket,检查并建立Socket连接;否则返回对应错误。
+        {
+            AjaxResult ar = socketService.openOne(seat.toSimSocketVo());
+            if (ar.isError()) {
+                return ar;
+            }
+        }
+        // Step :发送通用询问指令,询问是连接的哪种型号的哪一台模拟器;否则返回对应错误。
+        {
+            AjaxResult ar = commCheckService.checkOneSeatState(seat, true);
+            if (ar.isError()) {
+                return ar;
+            }
+        }
+        // 重新查询。已经确定simId了。
+        {
+            // 修改exam表对应examId的一条数据,填充并锁定seat_id和sim_id值。
+            // 设置上seatId和simId
+            re = selectRealExamByExamId(examId);
+            seat = seatService.uniqueByBindIp(studentBindIp);
+            re.setSeatId(seat.getSeatId());
+            re.setSimId(seat.getCurrentSimId());
+            l.debug("re = {}", re);
+            updateRealExam(re);
+        }
+        // 查询模拟器在线状态,纯DB查询。
+        {
+            AjaxResult ar = commCheckService.checkOneSimOnlineState(seat.getCurrentSimId());
+            if (ar.isError()) {
+                return ar;
+            }
+        }
         Sim sim = simService.selectSimBySimId(re.getSimId());
-        // Step 5:主动查询一次模拟器状态。更新模拟器在线/离线状态;否则返回对应错误。
+        // 检查模拟器类型
         {
-            AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
-            if (arE3.isError()) {
-                return arE3;
+            String targetSimType = re.getSimType();
+            AjaxResult ar = commCheckService.checkOneSimType(seat, true, targetSimType);
+            if (ar.isError()) {
+                return ar;
             }
         }
-        // Step 6:修改exam表对应examId的一条数据,填充并锁定seat_id和sim_id值。
-        sim = simService.selectSimBySimId(re.getSimId());
+        // Step 5:
+        {
+//            AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
+//            if (arE3.isError()) {
+//                return arE3;
+//            }
+        }
         // Step 7:可换件检查,读取对应一台模拟器 所有故障部位值。检查模拟器所有的 真实的 故障部位 是否异常 或者 空值。特殊的故障部位要单独判断。
         if (SimDebugConfig.CHECK_REPLACE_EMPTY) {
             AjaxResult arE2 = commSendService.readOneSimAllFaultCheck(seat, sim);
@@ -364,13 +443,15 @@ public class RealExamService {
         }
         RealExam re = selectRealExamByExamId(examId);
         // 检查 seat_id 是否正确存在
-        {
+        // Java后端处理填写,不检查。
+        if (false) {
             if (!seatService.exist(re.getSeatId())) {
                 return AjaxResult.error("对应座Id不存在!");
             }
         }
+        // Java后端处理填写,不检查。
         // 检查 sim_id 是否正确存在
-        {
+        if (false) {
             if (!simService.existBySimId(re.getSimId())) {
                 return AjaxResult.error("对应模拟器Id不存在!");
             }
@@ -424,7 +505,7 @@ public class RealExamService {
     }
 
     /**
-     * [学生]交卷
+     * [学生]交卷考试
      *
      * @param examId
      * @return RealExam
@@ -445,15 +526,14 @@ public class RealExamService {
         }
         // 检查一下模拟器状态。
         Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
-        Sim sim = simService.selectSimBySimId(re.getSimId());
         // 如果模拟器离线
         {
-            AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
+            // AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
+            AjaxResult arE3 = commCheckService.checkOneSeatState(seat, true);
             if (arE3.isError()) {
                 return arE3;
             }
         }
-        sim = simService.selectSimBySimId(re.getSimId());
         // 最后读取一下模拟器电阻值。
         commSendService.readOneExamAtLast(re);
         if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) {

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

@@ -4,7 +4,10 @@ import java.util.*;
 
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.DateUtils;
+import com.ruoyi.sim.domain.Sim;
+import com.ruoyi.sim.domain.vo.SeatVo;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ruoyi.sim.mapper.SeatMapper;
@@ -84,6 +87,8 @@ public class SeatService {
     }
 
     // -------------------------------- tom add  --------------------------------
+    @Autowired
+    private SimService simService;
 
     public boolean exist(Long seatId) {
         if (seatId == null) {
@@ -111,7 +116,18 @@ public class SeatService {
      * @return
      */
     public AjaxResult listAllEnableAj() {
-        return AjaxResult.success(listAllEnable());
+        List<Seat> list1 = listAllEnable();
+        List<SeatVo> list2 = new ArrayList<>();
+        for (Seat seat : list1) {
+            SeatVo vo = new SeatVo();
+            BeanUtils.copyProperties(seat, vo);
+            Sim sim = simService.selectSimBySimId(seat.getCurrentSimId());
+            if (seat != null && seat.getCurrentSimId() != null && seat.getCurrentSimId() != 0L) {
+                BeanUtils.copyProperties(sim, vo);
+            }
+            list2.add(vo);
+        }
+        return AjaxResult.success(list2);
     }
 
     public Seat uniqueBySeatNum(final Integer seatNum) {
@@ -127,7 +143,20 @@ public class SeatService {
         }
     }
 
-    public Seat uniqueByIpAndPort(final String rs485Ip, final int rs485Port) {
+    public Seat uniqueByBindIp(final String bindIp) {
+        Seat q = new Seat();
+        q.setSeatBindIp(bindIp);
+        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 uniqueByRs485IpAndPort(final String rs485Ip, final int rs485Port) {
         Seat q = new Seat();
         q.setSeatRs485Ip(rs485Ip);
         q.setSeatRs485Port(rs485Port);
@@ -147,12 +176,6 @@ public class SeatService {
         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) {
@@ -169,6 +192,12 @@ public class SeatService {
         return ips.stream().toList();
     }
 
+    public void updateSocketStateBySeatNum(final Integer seatNum, final String socketState) {
+        Seat f = uniqueBySeatNum(seatNum);
+        f.setSeatRs485SocketState(socketState);
+        updateSeat(f);
+    }
+
     public void updateSocketStateByRs485Ip(final String rs485Ip, final String socketState) {
         Seat q = new Seat();
         q.setSeatRs485Ip(rs485Ip);

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

@@ -213,7 +213,7 @@ public class SimService {
     }
 
     public boolean checkState(String simState) {
-        return Sim.STATE_SET.contains(simState);
+        return !Sim.STATE_SET.contains(simState);
     }
 
     @Transactional

+ 17 - 11
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/SocketService.java

@@ -52,10 +52,11 @@ public class SocketService {
      * @return true:socket ok!
      */
     public boolean isOk(final SimSocketVo ssv) {
-        if (cachedMap.containsKey(ssv.toKey())) {
-            Socket s = cachedMap.get(ssv.toKey()).getSocket();
+        final String key = ssv.toKey();
+        if (cachedMap.containsKey(key) && cachedMap.get(key) != null) {
+            Socket s = cachedMap.get(key).getSocket();
             if (s != null) {
-                return (s.isConnected() && s.isBound() && !s.isClosed() && !isTimeout(ssv));
+                return (s.isConnected() && s.isBound() && !s.isClosed());
             }
         }
         return false;
@@ -65,15 +66,20 @@ public class SocketService {
         return !isOk(ssv);
     }
 
+    /**
+     * 暂时不考虑超时周期。
+     * todo:
+     *
+     * @param ssv
+     * @return
+     */
     public boolean isTimeout(SimSocketVo ssv) {
         if (cachedMap.containsKey(ssv.toKey())) {
             Long cached = cachedMap.get(ssv.toKey()).getOkTimeMillis();
             if (cached == null || cached == 0L) {
                 return true;
             }
-            if (System.currentTimeMillis() - cached > TIMEOUT_LIMIT) {
-                return false;
-            }
+            return System.currentTimeMillis() - cached <= TIMEOUT_LIMIT;
         }
         return true;
     }
@@ -92,7 +98,7 @@ public class SocketService {
         try {
             if (isNotOk(ssv)) {
                 final String key = ssv.toKey();
-                l.info("openSocket cachedSocket is not ok!new socket ip = {}:{}!", ssv.getIp(), ssv.getPort());
+                l.info("openSocket cachedSocket is not ok!try new socket ip = {}:{}!", ssv.getIp(), ssv.getPort());
                 closeOne(ssv);
                 Socket s = new Socket(ssv.getIp(), ssv.getPort());
                 s.setSoTimeout(SOCKET_TIME_OUT);
@@ -103,7 +109,7 @@ public class SocketService {
             } else {
                 l.info("openSocket cachedSocket cache ok!cached socket ip = {}:{}!", ssv.getIp(), ssv.getPort());
             }
-            Seat seat = seatService.uniqueByIpAndPort(ssv.getIp(), ssv.getPort());
+            Seat seat = seatService.uniqueByRs485IpAndPort(ssv.getIp(), ssv.getPort());
             seat.setSeatRs485SocketState(Seat.SocketState.ONLINE);
             seatService.updateSeat(seat);
             return AjaxResult.success("Socket[" + ssv.getIp() + ":" + ssv.getPort() + "],创建成功!");
@@ -118,16 +124,16 @@ public class SocketService {
      *
      * @return
      */
-    public AjaxResult openAll() {
+    public AjaxResult tryOpenAll() {
         if (!config.isCommGlobal()) {
             l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal());
             return AjaxResult.error("模拟器通信被禁用!");
         }
         List<Seat> allSeat = seatService.listAllEnable();
         for (Seat s : allSeat) {
-            openOne(new SimSocketVo(s.getSeatRs485Ip(), s.getSeatRs485Port()));
+            openOne(s.toSimSocketVo());
         }
-        return AjaxResult.success("openAllSocket Success!");
+        return AjaxResult.success("所有Socket,创建成功!");
     }
 
     public AjaxResult closeOne(final SimSocketVo ssv) {

+ 7 - 1
ruoyi-sim/src/main/resources/mapper/sim/RealExamMapper.xml

@@ -7,6 +7,7 @@
     <resultMap type="RealExam" id="RealExamResult">
         <result property="examId" column="exam_id"/>
         <result property="examCollectionId" column="exam_collection_id"/>
+        <result property="simType" column="sim_type"/>
         <result property="userId" column="user_id"/>
         <result property="seatId" column="seat_id"/>
         <result property="simId" column="sim_id"/>
@@ -27,6 +28,7 @@
     <sql id="selectRealExamVo">
         select exam_id,
                exam_collection_id,
+               sim_type,
                user_id,
                seat_id,
                sim_id,
@@ -49,6 +51,7 @@
         <include refid="selectRealExamVo"/>
         <where>
             <if test="examCollectionId != null ">and exam_collection_id = #{examCollectionId}</if>
+            <if test="simType != null  and simType != ''">and sim_type = #{simType}</if>
             <if test="userId != null ">and user_id = #{userId}</if>
             <if test="seatId != null ">and seat_id = #{seatId}</if>
             <if test="simId != null ">and sim_id = #{simId}</if>
@@ -68,13 +71,14 @@
     </select>
 
     <insert id="insertRealExam" parameterType="RealExam">
-        <selectKey keyProperty="examId" order="AFTER" resultType="java.lang.Long">
+        <selectKey keyProperty="exam_id" order="AFTER" resultType="java.lang.Long">
             select LAST_INSERT_ID()
         </selectKey>
         insert into mx_real_exam
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="examId != null">exam_id,</if>
             <if test="examCollectionId != null">exam_collection_id,</if>
+            <if test="simType != null">sim_type,</if>
             <if test="userId != null">user_id,</if>
             <if test="seatId != null">seat_id,</if>
             <if test="simId != null">sim_id,</if>
@@ -94,6 +98,7 @@
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="examId != null">#{examId},</if>
             <if test="examCollectionId != null">#{examCollectionId},</if>
+            <if test="simType != null">#{simType},</if>
             <if test="userId != null">#{userId},</if>
             <if test="seatId != null">#{seatId},</if>
             <if test="simId != null">#{simId},</if>
@@ -116,6 +121,7 @@
         update mx_real_exam
         <trim prefix="SET" suffixOverrides=",">
             <if test="examCollectionId != null">exam_collection_id = #{examCollectionId},</if>
+            <if test="simType != null">sim_type = #{simType},</if>
             <if test="userId != null">user_id = #{userId},</if>
             <if test="seatId != null">seat_id = #{seatId},</if>
             <if test="simId != null">sim_id = #{simId},</if>