浏览代码

修复 正在考试-坐席列表 学员不被顶替的问题。

tom 3 周之前
父节点
当前提交
56a7a5ae79

+ 4 - 1
ruoyi-sim/src/main/java/com/ruoyi/sim/controller/TestIotController.java

@@ -31,6 +31,9 @@ public class TestIotController extends BaseController {
     private RealExamService realExamService;
     @Autowired
     @Lazy
+    private RealExamCollectionService realExamCollectionService;
+    @Autowired
+    @Lazy
     private FaultService faultService;
     @Autowired
     @Lazy
@@ -117,7 +120,7 @@ public class TestIotController extends BaseController {
                 return commCheckService.checkPingRs485State(ip);
             }
             case 201: {
-                realExamService.studentMiddleReadRealExam();
+                realExamCollectionService.studentMiddleRead();
             }
         }
         return AjaxResult.success("ZZZZZZZZZZZZZZZZZZZZ");

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

@@ -67,6 +67,11 @@ public class Sim extends BaseEntity {
     // @Excel(name = "最后一次成功收到报文时间", width = 30, dateFormat = "yyyy-MM-dd")
     private Date lastReceivedTime;
 
+    /**
+     * 充电计数
+     */
+    private Integer chargingCount;
+
     public void setSimId(Long simId) {
         this.simId = simId;
     }
@@ -131,6 +136,14 @@ public class Sim extends BaseEntity {
         return lastReceivedTime;
     }
 
+    public Integer getChargingCount() {
+        return chargingCount;
+    }
+
+    public void setChargingCount(Integer chargingCount) {
+        this.chargingCount = chargingCount;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@@ -142,6 +155,7 @@ public class Sim extends BaseEntity {
                 .append("simNum", getSimNum())
                 .append("lastSentTime", getLastSentTime())
                 .append("lastReceivedTime", getLastReceivedTime())
+                .append("chargingCount", getChargingCount())
                 .append("createBy", getCreateBy())
                 .append("createTime", getCreateTime())
                 .append("updateBy", getUpdateBy())
@@ -152,6 +166,9 @@ public class Sim extends BaseEntity {
 
     // -------------------------------- tom add  --------------------------------
 
+    /**
+     * 表明座上没有任何模拟器
+     */
     public static final Long ID_0 = 0L;
     /**
      * FZD04B

+ 11 - 10
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommCheckService.java

@@ -16,6 +16,7 @@ import java.io.IOException;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.text.MessageFormat;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 
@@ -136,7 +137,7 @@ public class CommCheckService {
         SimMsg smS = commBuildService.buildSendMsgWhichSim();
         SimMsg smR = commSendService.send(smS, seat, null, retryTotalCount, commStrategy.getSleepShort());
         Integer result = smR.getResult();
-        if (Objects.equals(result, SimMsg.Result.SUCCESS)) {
+        if (Objects.equals(result, SimMsg.Result.SUCCESS)) { // 在线
             final String simNum = CommParseUtils.subSimNum(smR.getReceiveMsg());
             Sim sim = simService.uniqueBySimNum(simNum);
             if (sim == null) {
@@ -144,7 +145,7 @@ public class CommCheckService {
             } else {
                 l.info("座号[{}]上发现模拟器[{}]", seat.getSeatNum(), sim.getSimNum());
             }
-            // 更新SimId
+            // 更新座上SimId
             seatService.updateCurrentSimIdBySeatNum(seat.getSeatNum(), sim.getSimId());
             // 更新Sim状态
             simService.updateSimStateBySimId(sim.getSimId(), Sim.State.ONLINE);
@@ -155,17 +156,17 @@ public class CommCheckService {
             String msg = MessageFormat.format(msgTemp, seat.getSeatNum(), Sim.TYPE_NAME_MAP.get(sim.getSimType()), sim.getSimNum());
             // 成功的话,Obj为Sim对象。
             return AjaxResult.success(msg, sim);
-        } else if (Objects.equals(result, SimMsg.Result.RECEIVE_CHECK_FAIL)) {
-            return smR.getDefaultErrorAR();
-        } else if (Objects.equals(result, SimMsg.Result.READ_TIMEOUT_EXCEPTION)) {
-            // 更新SimId
+        } else if (Objects.equals(result, SimMsg.Result.READ_TIMEOUT_EXCEPTION)) { // 离线
+            // 更新座上SimId
             seatService.updateCurrentSimIdBySeatNum(seat.getSeatNum(), Sim.ID_0);
             String msgTemp = "座号[{0}]-没有连接任何接模拟器,检查线缆开关和线缆连接!";
             String msg = MessageFormat.format(msgTemp, seat.getSeatNum());
-            // 构造一个Sim对象
-            Sim simF = new Sim();
-            simF.setSimState(Sim.State.OFFLINE);
-            return AjaxResult.success(msg, simF);
+            // 构造一个假的Sim对象,用来返回结果。不知道之前模拟器的状态。
+            Sim simFake = new Sim();
+            simFake.setSimState(Sim.State.OFFLINE);
+            return AjaxResult.success(msg, simFake);
+        } else if (Objects.equals(result, SimMsg.Result.RECEIVE_CHECK_FAIL)) {
+            return smR.getDefaultErrorAR();
         } else if (Objects.equals(result, SimMsg.Result.RECEIVE_NOT_MATCH)) {
             return smR.getDefaultErrorAR();
         }

+ 42 - 8
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/CommSendService.java

@@ -305,14 +305,6 @@ public class CommSendService {
     }
 
     /**
-     * 每10min运行一次。
-     */
-    public void scheduledExamMiddleRead() {
-        l.info("scheduledExamMiddleRead");
-        realExamService.studentMiddleReadRealExam();
-    }
-
-    /**
      * 每30min运行一次。
      */
     public void scheduledSystemAutoCleanExam() {
@@ -682,6 +674,7 @@ public class CommSendService {
             String text = ar.get(AjaxResult.MSG_TAG).toString();
             // 新查询
             seat = seatService.selectSeatBySeatId(seat.getSeatId());
+            // 座上sim
             Sim sim = simService.selectSimBySimId(seat.getCurrentSimId());
             if (sim != null) {
                 list.add(new ScanSeatVo(seat.getSeatId(), sim.getSimId(), sim.getSimType(), text));
@@ -1233,4 +1226,45 @@ public class CommSendService {
         debugFaultService.deleteAll();
         return AjaxResult.success("全部重置成功。" + SimDateUtil.getNow());
     }
+
+    /**
+     * 每30s执行一次。
+     */
+    public void scheduledChargingCount() {
+        // 查询到唯一打开的 考试 或 训练
+        RealExamCollection rec = realExamCollectionService.selectRealExamCollectionOpenedNotSelfExercise();
+        //
+        if (rec == null) {
+            l.info("rec == null!");
+            return;
+        }
+        Long recId = rec.getExamCollectionId();
+        RealExam q = new RealExam();
+        q.setExamCollectionId(recId);
+        // 只需要0002型模拟器。
+        q.setSimType(Sim.TYPE_0002);
+        List<RealExam> list = realExamService.selectRealExamList(q);
+        if (list == null || list.isEmpty()) {
+            return;
+        }
+        list.forEach((RealExam re) -> {
+            if (re == null || re.getExamId() == null || re.getExamId() == 0L) {
+                return;
+            }
+            if (StringUtils.equalsAny(re.getExamStatus(), RealExam.State.LOGGED_IN)) {
+                Seat qSeat = seatService.selectSeatBySeatId(re.getSeatId());
+                // 检查后就知道seat上是否有sim
+                commCheckService.checkOneSeatState(qSeat, true);
+                Long currSimId = seatService.selectSeatBySeatId(re.getSeatId()).getCurrentSimId();
+                if (!currSimId.equals(Sim.ID_0)) {
+                    Integer count = simService.updateChargingCountPlusBySimId(currSimId);
+                    l.info("scheduledChargingCount count = {}", count);
+                } else {
+                    l.info("scheduledChargingCount Sim.ID_0 seatId = {}", qSeat.getSeatId());
+                }
+            } else {
+                l.info("skip");
+            }
+        });
+    }
 }

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

@@ -1,6 +1,7 @@
 package com.ruoyi.sim.service.impl;
 
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -9,6 +10,7 @@ import cn.ele6.catalyzer.ruoyi.vue.enhance.TableDataInfo;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.sim.domain.RealExam;
+import com.ruoyi.sim.domain.Sim;
 import com.ruoyi.sim.domain.vo.RealExamCollectionVo;
 import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
@@ -438,6 +440,25 @@ public class RealExamCollectionService extends Ele6RYBaseService {
         return (!list.isEmpty());
     }
 
+    /**
+     * 不是练习 的 打开的 考试集合。
+     *
+     * @return
+     */
+    public RealExamCollection selectRealExamCollectionOpenedNotSelfExercise() {
+        RealExamCollection q = selectRealExamCollectionOpened();
+        if (StringUtils.equals(q.getExamCollectionType(), RealExamCollection.Type.SELF_EXERCISE)) {
+            return null;
+        } else {
+            return q;
+        }
+    }
+
+    /**
+     * 获取打开的考试集合。
+     *
+     * @return 可能是练习类型,注意判断
+     */
     public RealExamCollection selectRealExamCollectionOpened() {
         RealExamCollection q = new RealExamCollection();
         q.setExamCollectionState(RealExamCollection.State.OPENED);
@@ -531,4 +552,48 @@ public class RealExamCollectionService extends Ele6RYBaseService {
         // 
         deleteRealExamCollectionByExamCollectionId(id);
     }
+
+    /**
+     * 每10min运行一次。
+     */
+    public void scheduledStudentMiddleRead() {
+        l.info("scheduledStudentMiddleRead");
+        studentMiddleRead();
+    }
+
+    /**
+     * 考试、训练 进行中间读取。
+     * 练习 不进行中间读取。
+     * 学员答题的托底保障执行。
+     */
+    public void studentMiddleRead() {
+        l.info("studentMiddleReadRealExam execute now = {}", new Date());
+        //
+        RealExamCollection rec = selectRealExamCollectionOpenedNotSelfExercise();
+        if (rec == null ||
+                rec.getExamCollectionId() == null ||
+                rec.getExamCollectionId() == 0L) {
+            l.info("考试集合不匹配。不需要中间读取,rec = {}", rec);
+            return;
+        }
+        l.info("中间读取,rec = {}", rec);
+        {
+            RealExam reQ = new RealExam();
+            reQ.setExamCollectionId(rec.getExamCollectionId());
+            List<RealExam> reList = realExamService.selectRealExamList(reQ);
+            for (RealExam re : reList) {
+                // 答题并且不超时的考试,进行中间读取
+                if (
+                        re != null && re.getExamId() != null && re.getExamId() != 0L &&
+                                StringUtils.equals(re.getExamStatus(), RealExam.State.ANSWERING) &&
+                                !realExamService.checkRealExamIsTimeout(re.getExamId())
+                ) {
+                    l.info("middle read examId = {}", re.getExamId());
+                    commSendService.readOneExamAtMiddle(re);
+                } else {
+                    l.info("skip examId = {}", re != null ? re.getExamId() : null);
+                }
+            }
+        }
+    }
 }

+ 27 - 51
ruoyi-sim/src/main/java/com/ruoyi/sim/service/impl/RealExamService.java

@@ -296,7 +296,7 @@ public class RealExamService {
         } else {
             l.info("type EXERCISE,没有打开的考试,校验正确");
         }
-        // Check:针对练习(自主练习),进行特殊检查。
+        // Check:针对练习(old叫自主练习),进行特殊检查。
         if (StringUtils.equals(RealExamCollection.Type.SELF_EXERCISE, examCollectionType)) {
             // 已经open的考试。
             if (realExamCollectionService.existOpenedByType(RealExamCollection.Type.EXAM)) {
@@ -319,6 +319,12 @@ public class RealExamService {
             }
         }
         RealExam re = selectRealExamByExamId(examId);
+        // 执行到开始考试,肯定已经登录了。
+        {
+            // 学员Id
+            Long userId = re.getUserId();
+            studentLoginSuccess(userId, studentBindIp);
+        }
         // check:考试状态
         if (StringUtils.equals(re.getExamStatus(), RealExam.State.SUBMITTED) ||
                 StringUtils.equals(re.getExamStatus(), RealExam.State.CALCULATING_SCORE) ||
@@ -422,10 +428,10 @@ public class RealExamService {
         // Step:重新查询。已经确定simId和simState了。
         {
             // 修改exam表对应examId的一条数据,填充并锁定seat_id和sim_id值。
-            // 设置上seatId和simId
             re = selectRealExamByExamId(examId);
             seat = seatService.uniqueByBindIp(studentBindIp);
             l.debug("seat = {}", seat);
+            // 设置上seatId和simId
             re.setSeatId(seat.getSeatId());
             re.setSimId(seat.getCurrentSimId());
             updateRealExam(re);
@@ -508,7 +514,6 @@ public class RealExamService {
         }
     }
 
-
     public AjaxResult studentRefreshSimState(final String studentBindIp) {
         Seat seat = seatService.uniqueByBindIp(studentBindIp);
         // Check:Seat有效性。
@@ -520,7 +525,6 @@ public class RealExamService {
         return commCheckService.checkOneSeatState(seat, true);
     }
 
-
     public AjaxResult checkExamId(final Long examId) {
         // Check:检查 examId 是否正确存在
         if (!exist(examId)) {
@@ -549,36 +553,6 @@ public class RealExamService {
         return AjaxResult.success(vo);
     }
 
-    public void studentMiddleReadRealExam() {
-        l.info("studentMiddleReadRealExam now = {}", new Date());
-        //
-        RealExamCollection rec = realExamCollectionService.selectRealExamCollectionOpened();
-        if (rec == null ||
-                rec.getExamCollectionId() == null ||
-                rec.getExamCollectionId() == 0L ||
-                !StringUtils.equals(rec.getExamCollectionType(), RealExamCollection.Type.EXAM)) {
-            l.info("考试集合不匹配。不需要中间读取,rec = {}", rec);
-            return;
-        }
-        {
-            RealExam reQ = new RealExam();
-            reQ.setExamCollectionId(rec.getExamCollectionId());
-            List<RealExam> reList = selectRealExamList(reQ);
-            for (RealExam re : reList) {
-                // 答题并且不超时的考试,进行中间读取
-                if (
-                        re != null && re.getExamId() != null && re.getExamId() != 0L &&
-                                StringUtils.equals(re.getExamStatus(), RealExam.State.ANSWERING) &&
-                                !checkRealExamIsTimeout(re.getExamId())
-                ) {
-                    commSendService.readOneExamAtMiddle(re);
-                } else {
-                    l.info("skip examId = {}", re != null ? re.getExamId() : null);
-                }
-            }
-        }
-    }
-
     /**
      * 10分钟延长时间。
      */
@@ -764,6 +738,9 @@ public class RealExamService {
     }
 
     /**
+     * 仅仅针对先打开考试集合,后登录的情况有效。
+     * 表 mx_real_exam 中写入 seat_id,修改exam_status
+     * <p>
      * [学员]登录成功后调用
      *
      * @param userId
@@ -771,32 +748,31 @@ public class RealExamService {
      * @return
      */
     public AjaxResult studentLoginSuccess(final Long userId, final String ip) {
-        RealExamCollection rec = realExamCollectionService.selectRealExamCollectionOpened();
-        if (rec == null || rec.getExamCollectionId() == 0L) {
-            return AjaxResult.success("没有打开的考试集合");
-        }
+        l.info("studentLoginSuccess userId = {},ip = {}", userId, ip);
         RealExam q = new RealExam();
         q.setUserId(userId);
-        q.setExamCollectionId(rec.getExamCollectionId());
+        q.setExamStatus(RealExam.State.NOT_LOGGED_IN);
         List<RealExam> list = selectRealExamList(q);
         if (list.isEmpty()) {
             return AjaxResult.success("没有学生考试数据");
         }
-        if (list.size() == 1) {
-            RealExam re = list.get(0);
-            if (StringUtils.equals(re.getExamStatus(), RealExam.State.NOT_LOGGED_IN) ||
-                    StringUtils.equals(re.getExamStatus(), RealExam.State.LOGGED_IN)) {
-                Seat s = seatService.uniqueByBindIp(ip);
-                if (s == null || s.getSeatId() == 0L) {
-                    return AjaxResult.success("没有座次信息");
+        for (RealExam re : list) {
+            RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
+            // 如果考试集合状态是OPENED。
+            if (StringUtils.equals(rec.getExamCollectionState(), RealExamCollection.State.OPENED)) {
+                if (StringUtils.equalsAny(re.getExamStatus(), RealExam.State.NOT_LOGGED_IN, RealExam.State.LOGGED_IN)) {
+                    Seat seat = seatService.uniqueByBindIp(ip);
+                    if (seat == null || seat.getSeatId() == 0L) {
+                        return AjaxResult.error("没有座次信息");
+                    }
+                    re.setSeatId(seat.getSeatId());
+                    re.setExamStatus(RealExam.State.LOGGED_IN);
+                    updateRealExam(re);
+                    return AjaxResult.success("成功");
                 }
-                re.setSeatId(s.getSeatId());
-                re.setExamStatus(RealExam.State.LOGGED_IN);
-                updateRealExam(re);
-                return AjaxResult.success("成功");
             }
         }
-        return AjaxResult.success("考试数据错误");
+        return AjaxResult.success("没有学生考试数据");
     }
 
     /**

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

@@ -1,6 +1,7 @@
 package com.ruoyi.sim.service.impl;
 
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 import java.util.Objects;
 
@@ -233,6 +234,85 @@ public class SimService {
         return updateSim(q);
     }
 
+    /**
+     * 充满量
+     * 单位:s
+     */
+    public static final Integer CHARGING_TOTAL = 300;
+
+    /**
+     * 充电步长量
+     * 单位:s
+     *
+     * @param simId
+     */
+    public static final Integer CHARGING_STEP = 30;
+
+    public static final Integer CHARGING_START = 0;
+
+
+    /**
+     * 充电count步长+1
+     *
+     * @param simId
+     * @return new value.
+     */
+    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
+    public Integer updateChargingCountPlusBySimId(final Long simId) {
+        l.info("updateChargingCountPlusBySimId simId = {}", simId);
+        Sim q = selectSimBySimId(simId);
+        if (Objects.isNull(q)) {
+            throw new IllegalArgumentException("simId");
+        }
+        if (q.getChargingCount() >= CHARGING_TOTAL) {// 超过上限。
+            return q.getChargingCount();
+        }
+        Integer countNew = q.getChargingCount() + CHARGING_STEP;
+        q.setChargingCount(countNew);
+        updateSim(q);
+        return countNew;
+    }
+
+    /**
+     * 充电count归零
+     *
+     * @param simId
+     * @return new value.
+     */
+    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
+    public Integer updateChargingCountResetBySimId(final Long simId) {
+        Sim q = selectSimBySimId(simId);
+        if (Objects.isNull(q)) {
+            throw new IllegalArgumentException("simId");
+        }
+        q.setChargingCount(CHARGING_START);
+        updateSim(q);
+        return CHARGING_START;
+    }
+
+    /**
+     * 是否充电完成
+     *
+     * @param simId
+     * @return
+     */
+    public Boolean isChargingCountFullBySimId(final Long simId) {
+        Sim q = selectSimBySimId(simId);
+        if (Objects.isNull(q)) {
+            throw new IllegalArgumentException("simId");
+        }
+        return (q.getChargingCount() >= CHARGING_TOTAL);
+    }
+
+    public Double getChargingCountPercentage(final Long simId) {
+        Sim q = selectSimBySimId(simId);
+        if (Objects.isNull(q)) {
+            throw new IllegalArgumentException("simId");
+        }
+        double p = q.getChargingCount() / ((double) CHARGING_TOTAL);
+        return Double.parseDouble(String.format("%.2f", p));
+    }
+
     public int updateSimStateBySimNum(final String simNum, final String simState) {
         return updateSimStateBySimId(uniqueBySimNum(simNum).getSimId(), simState);
     }

+ 6 - 0
ruoyi-sim/src/main/resources/mapper/sim/SimMapper.xml

@@ -13,6 +13,7 @@
         <result property="simNum" column="sim_num"/>
         <result property="lastSentTime" column="last_sent_time"/>
         <result property="lastReceivedTime" column="last_received_time"/>
+        <result property="chargingCount" column="charging_count"/>
         <result property="createBy" column="create_by"/>
         <result property="createTime" column="create_time"/>
         <result property="updateBy" column="update_by"/>
@@ -29,6 +30,7 @@
                sim_num,
                last_sent_time,
                last_received_time,
+               charging_count,
                create_by,
                create_time,
                update_by,
@@ -47,6 +49,7 @@
             <if test="simNum != null  and simNum != ''">and sim_num = #{simNum}</if>
             <if test="lastSentTime != null ">and last_sent_time = #{lastSentTime}</if>
             <if test="lastReceivedTime != null ">and last_received_time = #{lastReceivedTime}</if>
+            <if test="chargingCount != null ">and charging_count = #{chargingCount}</if>
         </where>
     </select>
 
@@ -68,6 +71,7 @@
             <if test="simNum != null and simNum != ''">sim_num,</if>
             <if test="lastSentTime != null">last_sent_time,</if>
             <if test="lastReceivedTime != null">last_received_time,</if>
+            <if test="chargingCount != null">charging_count,</if>
             <if test="createBy != null">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
@@ -82,6 +86,7 @@
             <if test="simNum != null and simNum != ''">#{simNum},</if>
             <if test="lastSentTime != null">#{lastSentTime},</if>
             <if test="lastReceivedTime != null">#{lastReceivedTime},</if>
+            <if test="chargingCount != null">#{charging_count},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
@@ -100,6 +105,7 @@
             <if test="simNum != null and simNum != ''">sim_num = #{simNum},</if>
             <if test="lastSentTime != null">last_sent_time = #{lastSentTime},</if>
             <if test="lastReceivedTime != null">last_received_time = #{lastReceivedTime},</if>
+            <if test="chargingCount != null">charging_count = #{chargingCount},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>