RealExamService.java 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. package com.ruoyi.sim.service.impl;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Objects;
  5. import com.ruoyi.common.core.domain.AjaxResult;
  6. import com.ruoyi.common.utils.DateUtils;
  7. import com.ruoyi.sim.config.SimConfig;
  8. import com.ruoyi.sim.config.SimDebugConfig;
  9. import com.ruoyi.sim.domain.*;
  10. import com.ruoyi.sim.domain.vo.RealExamVo;
  11. import com.ruoyi.sim.domain.vo.StudentRealExamIngVo;
  12. import com.ruoyi.sim.domain.vo.StudentRealExamPostVo;
  13. import com.ruoyi.sim.domain.vo.StudentRealExamPreVo;
  14. import org.apache.commons.lang3.StringUtils;
  15. import org.slf4j.Logger;
  16. import org.slf4j.LoggerFactory;
  17. import org.springframework.beans.factory.annotation.Autowired;
  18. import org.springframework.context.annotation.Lazy;
  19. import org.springframework.stereotype.Service;
  20. import com.ruoyi.sim.mapper.RealExamMapper;
  21. import org.springframework.transaction.annotation.Transactional;
  22. /**
  23. * 考试Service业务层处理
  24. *
  25. * @author tom
  26. * @date 2024-12-15
  27. */
  28. @Service
  29. public class RealExamService {
  30. @Autowired
  31. private RealExamMapper realExamMapper;
  32. /**
  33. * 查询考试
  34. *
  35. * @param examId 考试主键
  36. * @return 考试
  37. */
  38. public RealExam selectRealExamByExamId(Long examId) {
  39. return realExamMapper.selectRealExamByExamId(examId);
  40. }
  41. /**
  42. * 查询考试列表
  43. *
  44. * @param realExam 考试
  45. * @return 考试
  46. */
  47. public List<RealExam> selectRealExamList(RealExam realExam) {
  48. return realExamMapper.selectRealExamList(realExam);
  49. }
  50. /**
  51. * 新增考试
  52. *
  53. * @param realExam 考试
  54. * @return 结果
  55. */
  56. public int insertRealExam(RealExam realExam) {
  57. realExam.setCreateTime(DateUtils.getNowDate());
  58. return realExamMapper.insertRealExam(realExam);
  59. }
  60. /**
  61. * 修改考试
  62. *
  63. * @param realExam 考试
  64. * @return 结果
  65. */
  66. public int updateRealExam(RealExam realExam) {
  67. realExam.setUpdateTime(DateUtils.getNowDate());
  68. return realExamMapper.updateRealExam(realExam);
  69. }
  70. /**
  71. * 批量删除考试
  72. *
  73. * @param examIds 需要删除的考试主键
  74. * @return 结果
  75. */
  76. public int deleteRealExamByExamIds(Long[] examIds) {
  77. return realExamMapper.deleteRealExamByExamIds(examIds);
  78. }
  79. /**
  80. * 删除考试信息
  81. *
  82. * @param examId 考试主键
  83. * @return 结果
  84. */
  85. public int deleteRealExamByExamId(Long examId) {
  86. return realExamMapper.deleteRealExamByExamId(examId);
  87. }
  88. // -------------------------------- tom add --------------------------------
  89. private static final Logger l = LoggerFactory.getLogger(CommSendService.class);
  90. @Autowired
  91. private CommReceiveService commReceiveService;
  92. @Autowired
  93. private StudentService studentService;
  94. @Autowired
  95. private SimService simService;
  96. @Autowired
  97. private SeatService seatService;
  98. @Autowired
  99. private RealExamCollectionService realExamCollectionService;
  100. @Autowired
  101. private RealExamFaultService realExamFaultService;
  102. @Autowired
  103. @Lazy
  104. private CommSendService commSendService;
  105. @Autowired
  106. @Lazy
  107. private CommCheckService commCheckService;
  108. @Autowired
  109. private SimConfig simConfig;
  110. @Autowired
  111. private SocketService socketService;
  112. /**
  113. * examId 是否有效。
  114. *
  115. * @param examId
  116. * @return
  117. */
  118. public boolean exist(Long examId) {
  119. if (examId == null) {
  120. return false;
  121. }
  122. if (examId == 0) {
  123. return false;
  124. }
  125. RealExam re = selectRealExamByExamId(examId);
  126. if (re == null) {
  127. return false;
  128. }
  129. return true;
  130. }
  131. public List<RealExamVo> list(RealExam q) {
  132. List<RealExamVo> list = new ArrayList<>();
  133. realExamMapper.selectRealExamList(q).forEach(re -> {
  134. RealExamVo v = new RealExamVo();
  135. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  136. v.setRealExam(re);
  137. v.setRealExamCollection(rec);
  138. list.add(v);
  139. });
  140. return list;
  141. }
  142. public List<RealExam> listAllByStatus(String state) {
  143. RealExam q = new RealExam();
  144. q.setExamStatus(state);
  145. return selectRealExamList(q);
  146. }
  147. /**
  148. * 交卷自动修改关联状态
  149. *
  150. * @param examId
  151. * @param state
  152. * @return
  153. */
  154. public int updateOneState(long examId, final String state) {
  155. RealExam q = selectRealExamByExamId(examId);
  156. // todo:屏蔽
  157. if (false && RealExam.State.SUBMITTED.equals(state)) {
  158. // 关联故障list同步锁死。
  159. realExamFaultService.listAllType2State2and3ByExamId(q.getExamId())
  160. .forEach(ref -> {
  161. ref.setRefState(RealExamFault.State.FINISH);
  162. realExamFaultService.updateRealExamFault(ref);
  163. });
  164. }
  165. q.setExamStatus(state);
  166. return updateRealExam(q);
  167. }
  168. /**
  169. * [学生]进入考试。
  170. *
  171. * @return RealExam
  172. */
  173. public AjaxResult studentEnterRealExam(Long examId) {
  174. RealExam re = selectRealExamByExamId(examId);
  175. if (re == null) {
  176. AjaxResult.error("realExamId error!");
  177. }
  178. // todo:应该在登录位置实现
  179. // todo: temp
  180. updateOneState(examId, RealExam.State.LOGGED_IN);
  181. // todo: temp
  182. realExamFaultService.resetAllType2(examId);
  183. return AjaxResult.success(re);
  184. }
  185. /**
  186. * [轮询][学生]准备考试界面。
  187. *
  188. * @param realExamId
  189. * @return StudentRealExamPreVo
  190. */
  191. public AjaxResult studentLoopPrepareRealExam(Long realExamId) {
  192. l.info("studentLoopPrepareRealExam");
  193. // check
  194. if (realExamId == null || realExamId == 0) {
  195. // todo:
  196. }
  197. //
  198. RealExam re = selectRealExamByExamId(realExamId);
  199. if (re == null) {
  200. // todo:
  201. }
  202. // todo: 日期,不可进入考试。
  203. //
  204. // todo: 验证学生登录身份`
  205. Objects.requireNonNull(re);
  206. RealExamCollection collection =
  207. realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  208. // check collection
  209. Sim sim = simService.selectSimBySimId(re.getSimId());
  210. // check sim
  211. Student student = studentService.selectStudentByUserId(re.getUserId());
  212. // check student
  213. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  214. // check seat
  215. StudentRealExamPreVo vo = new StudentRealExamPreVo();
  216. vo.setRealExam(re);
  217. vo.setRealExamCollection(collection);
  218. vo.setSim(sim);
  219. vo.setStudent(student);
  220. vo.setSeat(seat);
  221. // todo:多人请求同时进入的问题
  222. boolean next = studentPrepareRealExamCheck(vo);
  223. vo.setNext(next);
  224. if (!next) {
  225. // 执行模拟器通信,让模拟器准备好。
  226. // 异步执行
  227. commSendService.clearListFaultByRealExamAsync(re);
  228. }
  229. l.info("vo = {}", vo);
  230. return AjaxResult.success(vo);
  231. }
  232. public boolean studentPrepareRealExamCheck(StudentRealExamPreVo v) {
  233. if (v == null ||
  234. v.getRealExam() == null ||
  235. v.getRealExamCollection() == null ||
  236. v.getSim() == null ||
  237. v.getStudent() == null ||
  238. v.getSeat() == null) {
  239. return false;
  240. }
  241. // todo:在考试日期内。
  242. // check 一个模拟器的所有选中故障点位都下发成功,准备是否可以
  243. //
  244. // todo:??
  245. // 学生答题中可以再次进入。
  246. String examStatus = v.getRealExam().getExamStatus();
  247. String simStatus = v.getSim().getSimState();
  248. // 兜底
  249. {
  250. if (realExamFaultService.isAllType2StateXiaFa(v.getRealExam().getExamId())) {
  251. // updateOneState(v.getRealExam().getExamId(), RealExam.State.SIM_PREPARE_OK);
  252. }
  253. }
  254. // 模拟器不通信。
  255. if (!simConfig.isCommGlobal()) {
  256. return true;
  257. }
  258. if ((RealExam.State.SIM_PREPARE_OK.equals(examStatus) ||
  259. RealExam.State.ANSWERING.equals(examStatus)) &&
  260. Sim.State.ONLINE.equals(simStatus)
  261. ) {
  262. return true;
  263. }
  264. return false;
  265. }
  266. /**
  267. * [学生]开始考试、训练、练习
  268. *
  269. * @param examId 考试Id
  270. * @param studentBindIp 考试学员IP
  271. * @param examCollectionType 考试集合类型
  272. * @return
  273. */
  274. public AjaxResult studentStartRealExam(final Long examId, final String studentBindIp, final String examCollectionType) {
  275. // Check:针对训练,进行特殊检查。
  276. if (StringUtils.equals(RealExamCollection.Type.EXERCISE, examCollectionType)) {
  277. // 已经open的考试。
  278. if (realExamCollectionService.existOpenedByType(RealExamCollection.Type.EXAM)) {
  279. return AjaxResult.error("存在打开的考试,无法开启训练!<br/>请向教员说明情况。");
  280. }
  281. }
  282. // Check:针对练习(自主练习),进行特殊检查。
  283. if (StringUtils.equals(RealExamCollection.Type.SELF_EXERCISE, examCollectionType)) {
  284. // 已经open的考试。
  285. if (realExamCollectionService.existOpenedByType(RealExamCollection.Type.EXAM)) {
  286. return AjaxResult.error("存在打开的考试,无法开启练习!<br/>请向教员说明情况。");
  287. }
  288. // 已经open的训练。
  289. if (realExamCollectionService.existOpenedByType(RealExamCollection.Type.EXERCISE)) {
  290. return AjaxResult.error("存在打开的训练,无法开启练习!<br/>请向教员说明情况。");
  291. }
  292. }
  293. // Check:检查参数examId有效性
  294. {
  295. AjaxResult ar = checkExamId(examId);
  296. if (ar.isError()) {
  297. return ar;
  298. }
  299. }
  300. RealExam re = selectRealExamByExamId(examId);
  301. // check:考试状态
  302. if (StringUtils.equals(re.getExamStatus(), RealExam.State.SUBMITTED) ||
  303. StringUtils.equals(re.getExamStatus(), RealExam.State.CALCULATING_SCORE) ||
  304. StringUtils.equals(re.getExamStatus(), RealExam.State.GOT_REPORT)) {
  305. return AjaxResult.error("已经交卷,禁止重复开始考试!");
  306. }
  307. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  308. // Check:考试集合数据有效性。
  309. if (rec == null) {
  310. return AjaxResult.error("考试集合数据异常!");
  311. }
  312. if (!StringUtils.equals(rec.getExamCollectionState(), RealExamCollection.State.OPENED)) {
  313. return AjaxResult.error("教师端对应考试/训练尚未打开!<br/>请向教员说明情况。");
  314. }
  315. // Check:检查参数examCollectionType有效性
  316. if (!StringUtils.equals(examCollectionType, rec.getExamCollectionType())) {
  317. return AjaxResult.error("考试集合类型不对应!");
  318. }
  319. // Check:检查参数studentBindIp有效性
  320. if (StringUtils.isBlank(studentBindIp)) {
  321. return AjaxResult.error("IP地址无效");
  322. }
  323. Seat seat = seatService.uniqueByBindIp(studentBindIp);
  324. if (seat == null) {
  325. return AjaxResult.error("没有IP对应座次数据!");
  326. }
  327. // Check:ping通 路由器。
  328. {
  329. AjaxResult ar = commCheckService.checkRouterState(simConfig.getRouterIp());
  330. if (ar.isError()) {
  331. return ar;
  332. }
  333. }
  334. // Check:ping通 学员端电脑。
  335. if (false) {
  336. AjaxResult ar = commCheckService.checkPingStudentPcState(studentBindIp);
  337. if (ar.isError()) {
  338. return ar;
  339. }
  340. }
  341. // Check:ping通 RS485。
  342. {
  343. AjaxResult ar = commCheckService.checkPingRs485State(seat.getSeatRs485Ip());
  344. if (ar.isError()) {
  345. // todo:重复
  346. // 更新SimId
  347. seatService.updateCurrentSimIdBySeatNum(seat.getSeatNum(), Seat.ID_0);
  348. // 更新SocketState
  349. seatService.updateSocketStateBySeatNum(seat.getSeatNum(), Seat.SocketState.OFFLINE);
  350. return ar;
  351. } else {
  352. // Ping通不代表在线,Socket连接建立表示在线。
  353. }
  354. }
  355. // Check:如果有缓存Socket并且可用,使用缓存Socket,检查并建立Socket连接;否则返回对应错误。
  356. {
  357. AjaxResult ar = socketService.openOne(seat.toSimSocketParamVo());
  358. if (ar.isError()) {
  359. return ar;
  360. }
  361. }
  362. // Check:发送通用询问指令,询问是连接的哪种型号的哪一台模拟器;否则返回对应错误。
  363. {
  364. AjaxResult ar = commCheckService.checkOneSeatState(seat, true);
  365. l.debug("ar = {}", ar);
  366. // 没有连接模拟器。
  367. if (ar.get(AjaxResult.DATA_TAG) == null ||
  368. !StringUtils.equals(((Sim) ar.get(AjaxResult.DATA_TAG)).getSimState(), Sim.State.ONLINE)) {
  369. return AjaxResult.error((String) ar.get(AjaxResult.MSG_TAG));
  370. }
  371. // 其他的异常情况。
  372. if (ar.isError()) {
  373. return ar;
  374. }
  375. }
  376. // Step:正式开始考试。锁定 座次 和 模拟器。
  377. // Step:重新查询。已经确定simId和simState了。
  378. {
  379. // 修改exam表对应examId的一条数据,填充并锁定seat_id和sim_id值。
  380. // 设置上seatId和simId
  381. re = selectRealExamByExamId(examId);
  382. seat = seatService.uniqueByBindIp(studentBindIp);
  383. l.debug("seat = {}", seat);
  384. re.setSeatId(seat.getSeatId());
  385. re.setSimId(seat.getCurrentSimId());
  386. updateRealExam(re);
  387. }
  388. // Check: seat_id 和 current_sim_id
  389. {
  390. RealExam re0001 = selectRealExamByExamId(examId);
  391. if (re0001 == null ||
  392. re0001.getSeatId() == null ||
  393. re0001.getSeatId() == 0L ||
  394. re0001.getSimId() == null ||
  395. re0001.getSimId() == 0) {
  396. return AjaxResult.error("开始考试异常!<br/>请刷新页面重试!");
  397. }
  398. }
  399. // Step:查询模拟器在线状态,纯DB查询。
  400. {
  401. AjaxResult ar = commCheckService.checkOneSimOnlineState(seat.getCurrentSimId());
  402. if (ar.isError()) {
  403. return ar;
  404. }
  405. }
  406. Sim sim = simService.selectSimBySimId(re.getSimId());
  407. // Check:检查模拟器类型
  408. final String targetSimType = re.getSimType();
  409. if (false) {
  410. AjaxResult ar = commCheckService.checkOneSimType(seat, true, targetSimType);
  411. if (ar.isError()) {
  412. return ar;
  413. }
  414. }
  415. // Step:可换件检查,读取对应一台模拟器 所有故障部位值。
  416. // 检查模拟器所有的 真实的 故障部位 是否异常 或者 空值。特殊的故障部位要单独判断。
  417. if (SimDebugConfig.CHECK_REPLACE_EMPTY) {
  418. AjaxResult ar = commSendService.readOneSimAllFaultCheck(seat, sim);
  419. if (ar.isError()) {
  420. return ar;
  421. }
  422. }
  423. // Step:清除对应一台模拟器 所有 真实的 故障部位故障。
  424. {
  425. commSendService.clearOneSimAllFaultByExam(re);
  426. }
  427. // Step:下发对应一台模拟器 出题选中的 故障位置故障。
  428. {
  429. commSendService.writeOneSimAllSelectFaultByExam(re);
  430. }
  431. // Step:读取对应一台模拟器 所有的 真实的 故障部位 电阻值代表值 作为出题值。
  432. {
  433. commSendService.readOneSimAllFaultFirstTimeByExam(re);
  434. }
  435. // Step:修改当前exam_id的考试状态。
  436. // 修改关联状态
  437. if (realExamFaultService.isType2ExamPrepareStartOk(re.getExamId())) {
  438. updateOneState(re.getExamId(), RealExam.State.SIM_PREPARE_OK);
  439. updateOneState(re.getExamId(), RealExam.State.ANSWERING);
  440. // 修改真实考试开始时间。
  441. re.setStartTime(DateUtils.getNowDate());
  442. updateRealExam(re);
  443. return AjaxResult.success("开始考试成功!");
  444. } else {
  445. return AjaxResult.error("开始考试失败,<br/>请重新尝试开始考试!");
  446. }
  447. }
  448. public AjaxResult studentRefreshSimState(final String studentBindIp) {
  449. Seat seat = seatService.uniqueByBindIp(studentBindIp);
  450. // Check:Seat有效性。
  451. {
  452. if (seat == null) {
  453. return AjaxResult.error("没有IP对应座次数据!");
  454. }
  455. }
  456. return commCheckService.checkOneSeatState(seat, true);
  457. }
  458. public AjaxResult checkExamId(final Long examId) {
  459. // Check:检查 examId 是否正确存在
  460. if (!exist(examId)) {
  461. return AjaxResult.error("对应考试Id不存在!");
  462. } else {
  463. return AjaxResult.success();
  464. }
  465. }
  466. /**
  467. * [轮询][学生]正在考试界面。
  468. *
  469. * @param realExamId
  470. * @return StudentRealExamIngVo
  471. */
  472. public AjaxResult studentLoopAnsweringRealExam(Long realExamId) {
  473. RealExam re = selectRealExamByExamId(realExamId);
  474. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  475. StudentRealExamIngVo vo = new StudentRealExamIngVo();
  476. vo.setRealExam(re);
  477. long remaining = (re.getStartTime().getTime() + rec.getLimitDuration() * 60 * 1000) - DateUtils.getNowDate().getTime();
  478. vo.setRemainingMilliseconds(remaining);
  479. vo.setCompulsiveSubmit(remaining >= RealExam.EXAM_TIMEOUT_LIMIT);
  480. l.info("studentLoopAnsweringRealExam vo = {}", vo);
  481. return AjaxResult.success(vo);
  482. }
  483. public static final Long DURATION_10_MIN = 1000L * 60 * 10;
  484. /**
  485. * [学生]交卷考试
  486. *
  487. * @param examId
  488. * @return RealExam
  489. */
  490. @Transactional
  491. public AjaxResult studentSubmitRealExam(final Long examId, final String studentBindIp, final String examCollectionType) {
  492. // Check:检查参数examId有效性
  493. {
  494. AjaxResult ar = checkExamId(examId);
  495. if (ar.isError()) {
  496. return ar;
  497. }
  498. }
  499. RealExam re = selectRealExamByExamId(examId);
  500. if (re == null ||
  501. re.getExamId() == 0L ||
  502. re.getSimId() == null ||
  503. re.getSimId() == 0L ||
  504. re.getExamCollectionId() == null ||
  505. re.getExamCollectionId() == 0L) {
  506. l.debug("RealExam = {}", re);
  507. return AjaxResult.error("交卷数据错误!");
  508. }
  509. if (re.getStartTime() == null) {
  510. return AjaxResult.error("考试开始时间异常!");
  511. }
  512. // check:考试状态
  513. if (StringUtils.equals(re.getExamStatus(), RealExam.State.SUBMITTED)) {
  514. return AjaxResult.error("已经交卷,禁止重复交卷,<br/>请刷新自动结束考试!");
  515. }
  516. RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
  517. // 允许考试时长,毫秒
  518. Long millisecondsAllowed = rec.getLimitDuration() * 60 * 1000 + DURATION_10_MIN;
  519. // Check:已经超时的交卷。
  520. if (DateUtils.getNowDate().getTime() > re.getStartTime().getTime() + millisecondsAllowed) {
  521. // 修改考试状态
  522. re.setExamStatus(RealExam.State.SUBMITTED);
  523. // 修改真实考试结束时间。
  524. re.setEndTime(DateUtils.getNowDate());
  525. updateRealExam(re);
  526. return AjaxResult.success("考试时间已经超时,自动结束考试!");
  527. }
  528. // Check:检查参数studentBindIp有效性
  529. if (StringUtils.isBlank(studentBindIp)) {
  530. return AjaxResult.error("IP地址无效!");
  531. }
  532. // 现在交卷的座次
  533. Seat seatNow = seatService.uniqueByBindIp(studentBindIp);
  534. // 开始考试的座次
  535. Seat seatStart = seatService.selectSeatBySeatId(re.getSeatId());
  536. if (seatNow == null || seatStart == null) {
  537. return AjaxResult.error("没有IP对应座次数据!");
  538. }
  539. // Check:防止换座位交卷。
  540. if (!Objects.equals(seatStart.getSeatId(), seatNow.getSeatId())) {
  541. return AjaxResult.error("没有在原始座次上交卷,请回到原座次[" + seatStart.getSeatNum() + "]上进行交卷!");
  542. }
  543. // Check:检查参数examCollectionType有效性
  544. if (!StringUtils.equals(examCollectionType, rec.getExamCollectionType())) {
  545. return AjaxResult.error("考试集合类型不对应!");
  546. }
  547. // Check:ping通 RS485。
  548. {
  549. AjaxResult ar = commCheckService.checkPingRs485State(seatStart.getSeatRs485Ip());
  550. if (ar.isError()) {
  551. // todo:重复
  552. // 更新SimId
  553. seatService.updateCurrentSimIdBySeatNum(seatStart.getSeatNum(), Seat.ID_0);
  554. // 更新SocketState
  555. seatService.updateSocketStateBySeatNum(seatStart.getSeatNum(), Seat.SocketState.OFFLINE);
  556. return ar;
  557. } else {
  558. // Ping通不代表在线,Socket连接建立表示在线。
  559. }
  560. }
  561. // Check:如果有缓存Socket并且可用,使用缓存Socket,检查并建立Socket连接;否则返回对应错误。
  562. {
  563. AjaxResult ar = socketService.openOne(seatStart.toSimSocketParamVo());
  564. if (ar.isError()) {
  565. return ar;
  566. }
  567. }
  568. // Check:发送通用询问指令,询问是连接的哪种型号的哪一台模拟器;否则返回对应错误。
  569. {
  570. AjaxResult ar = commCheckService.checkOneSeatState(seatStart, true);
  571. if (ar.isError()) {
  572. return ar;
  573. }
  574. }
  575. // Step:查询模拟器在线状态,纯DB查询。
  576. {
  577. AjaxResult ar = commCheckService.checkOneSimOnlineState(seatStart.getCurrentSimId());
  578. if (ar.isError()) {
  579. return ar;
  580. }
  581. }
  582. // Check:检查是否是出题值使用的模拟器。防止换机器交卷。
  583. re = selectRealExamByExamId(examId);
  584. if (!Objects.equals(re.getSimId(), seatNow.getCurrentSimId())) {
  585. return AjaxResult.error("没有使用原始模拟器交卷,请使用模拟器[" +
  586. simService.selectSimBySimId(re.getSimId()).getSimNum() +
  587. "]进行交卷!");
  588. }
  589. // Check:检查考试状态
  590. if (StringUtils.equals(re.getExamStatus(), RealExam.State.SUBMITTED)) {
  591. return AjaxResult.success("已经成功交卷,跳过交卷!");
  592. }
  593. // todo:检查一下模拟器状态。
  594. // Check:检查换学生端交卷的情况。
  595. //
  596. // Step:最后读取一下模拟器电阻值。
  597. commSendService.readOneExamAtLast(re);
  598. // Step:
  599. if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) {
  600. re.setExamStatus(RealExam.State.SUBMITTED);
  601. // 修改真实考试结束时间。
  602. re.setEndTime(DateUtils.getNowDate());
  603. updateRealExam(re);
  604. return AjaxResult.success("交卷成功!");
  605. } else {
  606. return AjaxResult.error("交卷失败!考试数据不完整。");
  607. }
  608. }
  609. /**
  610. * [轮询][学生]结束考试界面。
  611. *
  612. * @param examId
  613. * @return StudentRealExamPostVo
  614. */
  615. public AjaxResult studentLoopPostRealExam(Long examId) {
  616. RealExam re = selectRealExamByExamId(examId);
  617. StudentRealExamPostVo vo = new StudentRealExamPostVo();
  618. {
  619. }
  620. vo.setRealExam(re);
  621. vo.setListPart1(realExamFaultService.getReportListPart1(examId));
  622. vo.setListPart2(realExamFaultService.getReportListPart2(examId));
  623. vo.setPart3(realExamFaultService.getReportPart3(examId));
  624. return AjaxResult.success(vo);
  625. }
  626. /**
  627. * [学员]登录成功后调用
  628. *
  629. * @param userId
  630. * @param ip
  631. * @return
  632. */
  633. public AjaxResult studentLoginSuccess(final Long userId, final String ip) {
  634. return AjaxResult.success();
  635. }
  636. }