123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568 |
- package com.ruoyi.sim.service.impl;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Objects;
- 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.domain.*;
- 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;
- import org.springframework.context.annotation.Lazy;
- import org.springframework.stereotype.Service;
- import com.ruoyi.sim.mapper.RealExamMapper;
- import org.springframework.transaction.annotation.Transactional;
- /**
- * 考试Service业务层处理
- *
- * @author tom
- * @date 2024-12-15
- */
- @Service
- public class RealExamService {
- @Autowired
- private RealExamMapper realExamMapper;
- /**
- * 查询考试
- *
- * @param examId 考试主键
- * @return 考试
- */
- public RealExam selectRealExamByExamId(Long examId) {
- return realExamMapper.selectRealExamByExamId(examId);
- }
- /**
- * 查询考试列表
- *
- * @param realExam 考试
- * @return 考试
- */
- public List<RealExam> selectRealExamList(RealExam realExam) {
- return realExamMapper.selectRealExamList(realExam);
- }
- /**
- * 新增考试
- *
- * @param realExam 考试
- * @return 结果
- */
- public int insertRealExam(RealExam realExam) {
- realExam.setCreateTime(DateUtils.getNowDate());
- return realExamMapper.insertRealExam(realExam);
- }
- /**
- * 修改考试
- *
- * @param realExam 考试
- * @return 结果
- */
- public int updateRealExam(RealExam realExam) {
- realExam.setUpdateTime(DateUtils.getNowDate());
- return realExamMapper.updateRealExam(realExam);
- }
- /**
- * 批量删除考试
- *
- * @param examIds 需要删除的考试主键
- * @return 结果
- */
- public int deleteRealExamByExamIds(Long[] examIds) {
- return realExamMapper.deleteRealExamByExamIds(examIds);
- }
- /**
- * 删除考试信息
- *
- * @param examId 考试主键
- * @return 结果
- */
- public int deleteRealExamByExamId(Long examId) {
- return realExamMapper.deleteRealExamByExamId(examId);
- }
- // -------------------------------- tom add --------------------------------
- private static final Logger l = LoggerFactory.getLogger(CommSendService.class);
- @Autowired
- private CommReceiveService commReceiveService;
- @Autowired
- private StudentService studentService;
- @Autowired
- private SimService simService;
- @Autowired
- private SeatService seatService;
- @Autowired
- private RealExamCollectionService realExamCollectionService;
- @Autowired
- private RealExamFaultService realExamFaultService;
- @Autowired
- @Lazy
- private CommSendService commSendService;
- @Autowired
- @Lazy
- private CommCheckService commCheckService;
- @Autowired
- private SimConfig simConfig;
- @Autowired
- private SocketService socketService;
- /**
- * examId 是否有效。
- *
- * @param examId
- * @return
- */
- public boolean exist(Long examId) {
- if (examId == null) {
- return false;
- }
- if (examId == 0) {
- return false;
- }
- RealExam re = selectRealExamByExamId(examId);
- if (re == null) {
- return false;
- }
- return true;
- }
- public List<RealExamVo> list(RealExam q) {
- List<RealExamVo> list = new ArrayList<>();
- realExamMapper.selectRealExamList(q).forEach(re -> {
- RealExamVo v = new RealExamVo();
- RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
- v.setRealExam(re);
- v.setRealExamCollection(rec);
- list.add(v);
- });
- return list;
- }
- public List<RealExam> listAllByStatus(String state) {
- RealExam q = new RealExam();
- q.setExamStatus(state);
- return selectRealExamList(q);
- }
- /**
- * 交卷自动修改关联状态
- *
- * @param examId
- * @param state
- * @return
- */
- @Transactional
- public int updateOneState(long examId, final String state) {
- if (false) {
- // php项目维护exam_status字段。java这边不操作。
- // 屏蔽
- return 0;
- }
- RealExam q = selectRealExamByExamId(examId);
- // todo:屏蔽
- if (false && RealExam.State.SUBMITTED.equals(state)) {
- // 关联故障list同步锁死。
- realExamFaultService.listAllType2State2and3ByExamId(q.getExamId())
- .forEach(ref -> {
- ref.setRefState(RealExamFault.State.FINISH);
- realExamFaultService.updateRealExamFault(ref);
- });
- }
- q.setExamStatus(state);
- return updateRealExam(q);
- }
- /**
- * [学生]进入考试。
- *
- * @return RealExam
- */
- public AjaxResult studentEnterRealExam(Long examId) {
- RealExam re = selectRealExamByExamId(examId);
- if (re == null) {
- AjaxResult.error("realExamId error!");
- }
- // todo:应该在登录位置实现
- // todo: temp
- updateOneState(examId, RealExam.State.LOGGED_IN);
- // todo: temp
- realExamFaultService.resetAllType2(examId);
- return AjaxResult.success(re);
- }
- /**
- * [轮询][学生]准备考试界面。
- *
- * @param realExamId
- * @return StudentRealExamPreVo
- */
- public AjaxResult studentLoopPrepareRealExam(Long realExamId) {
- l.info("studentLoopPrepareRealExam");
- // check
- if (realExamId == null || realExamId == 0) {
- // todo:
- }
- //
- RealExam re = selectRealExamByExamId(realExamId);
- if (re == null) {
- // todo:
- }
- // todo: 日期,不可进入考试。
- //
- // todo: 验证学生登录身份`
- Objects.requireNonNull(re);
- RealExamCollection collection =
- realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
- // check collection
- Sim sim = simService.selectSimBySimId(re.getSimId());
- // check sim
- Student student = studentService.selectStudentByUserId(re.getUserId());
- // check student
- Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
- // check seat
- StudentRealExamPreVo vo = new StudentRealExamPreVo();
- vo.setRealExam(re);
- vo.setRealExamCollection(collection);
- vo.setSim(sim);
- vo.setStudent(student);
- vo.setSeat(seat);
- // todo:多人请求同时进入的问题
- boolean next = studentPrepareRealExamCheck(vo);
- vo.setNext(next);
- if (!next) {
- // 执行模拟器通信,让模拟器准备好。
- // 异步执行
- commSendService.clearListFaultByRealExamAsync(re);
- }
- l.info("vo = {}", vo);
- return AjaxResult.success(vo);
- }
- public boolean studentPrepareRealExamCheck(StudentRealExamPreVo v) {
- if (v == null ||
- v.getRealExam() == null ||
- v.getRealExamCollection() == null ||
- v.getSim() == null ||
- v.getStudent() == null ||
- v.getSeat() == null) {
- return false;
- }
- // todo:在考试日期内。
- // check 一个模拟器的所有选中故障点位都下发成功,准备是否可以
- //
- // todo:??
- // 学生答题中可以再次进入。
- String examStatus = v.getRealExam().getExamStatus();
- String simStatus = v.getSim().getSimState();
- // 兜底
- {
- if (realExamFaultService.isAllType2StateXiaFa(v.getRealExam().getExamId())) {
- // updateOneState(v.getRealExam().getExamId(), RealExam.State.SIM_PREPARE_OK);
- }
- }
- // 模拟器不通信。
- if (!simConfig.isCommGlobal()) {
- return true;
- }
- if ((RealExam.State.SIM_PREPARE_OK.equals(examStatus) ||
- RealExam.State.ANSWERING.equals(examStatus)) &&
- Sim.State.ONLINE.equals(simStatus)
- ) {
- return true;
- }
- return false;
- }
- /**
- * [学生]开始考试
- *
- * @param examId
- * @param studentBindIp
- * @param type
- * @return
- */
- @Transactional
- public AjaxResult studentStartRealExam(final Long examId, final String studentBindIp, final String type) {
- l.info("studentStartRealExam = {}", examId);
- // todo: 暂时没有解决方案 检查 考试的sim和seat,是否正确对应。
- {
- // todo:delete
- // re.setSimId(getFakeSimId(re));
- // l.info("fake re = {}", re);
- }
- // check id data.
- // Step :检查参数有效性。
- {
- AjaxResult ar01 = checkExamId(examId);
- if (ar01.isError()) {
- return ar01;
- }
- }
- if (StringUtils.isBlank(studentBindIp)) {
- return AjaxResult.error("ip地址无效。");
- }
- RealExam re = selectRealExamByExamId(examId);
- 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());
- // 检查模拟器类型
- {
- String targetSimType = re.getSimType();
- AjaxResult ar = commCheckService.checkOneSimType(seat, true, targetSimType);
- if (ar.isError()) {
- return ar;
- }
- }
- // 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);
- if (arE2.isError()) {
- return arE2;
- }
- }
- // Step 8:清除对应一台模拟器 所有 真实的 故障部位故障。
- {
- commSendService.clearOneSimAllFaultByExam(re);
- }
- // Step 9:下发对应一台模拟器 出题选中的 故障位置故障。
- {
- commSendService.writeOneSimAllSelectFaultByExam(re);
- }
- // Step 10:读取对应一台模拟器 所有的 真实的 故障部位 电阻值代表值 作为出题值。
- // 修改关联状态
- {
- commSendService.readOneSimAllFaultFirstTimeByExam(re);
- }
- // Step 11:修改当前exam_id状态。
- if (realExamFaultService.isType2ExamPrepareStartOk(re.getExamId())) {
- updateOneState(re.getExamId(), RealExam.State.SIM_PREPARE_OK);
- updateOneState(re.getExamId(), RealExam.State.ANSWERING);
- // 修改真实考试开始时间。
- re.setStartTime(DateUtils.getNowDate());
- updateRealExam(re);
- return AjaxResult.success("开始考试成功!");
- } else {
- return AjaxResult.error("连接超时,请检查模拟器连接!");
- }
- }
- public AjaxResult checkExamId(final Long examId) {
- // check
- // 检查 examId 是否正确存在
- {
- if (!exist(examId)) {
- return AjaxResult.error("对应考试Id不存在!");
- }
- }
- 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不存在!");
- }
- }
- return AjaxResult.success();
- }
- /**
- * 根据考试集合获得sim_id
- *
- * @param exam
- * @return
- */
- @Deprecated
- public long getFakeSimId(RealExam exam) {
- {
- RealExamCollection coll = realExamCollectionService.selectRealExamCollectionByExamCollectionId(exam.getExamCollectionId());
- String simType = coll.getSimType();
- switch (simType) {
- case Sim.TYPE_0001 -> {
- return 11L;
- }
- case Sim.TYPE_0002 -> {
- return 12L;
- }
- case Sim.TYPE_0003 -> {
- return 31L;
- }
- }
- }
- return 0L;
- }
- /**
- * [轮询][学生]正在考试界面。
- *
- * @param realExamId
- * @return StudentRealExamIngVo
- */
- public AjaxResult studentLoopAnsweringRealExam(Long realExamId) {
- RealExam re = selectRealExamByExamId(realExamId);
- RealExamCollection rec = realExamCollectionService.selectRealExamCollectionByExamCollectionId(re.getExamCollectionId());
- StudentRealExamIngVo vo = new StudentRealExamIngVo();
- vo.setRealExam(re);
- long remaining = (re.getStartTime().getTime() + rec.getLimitDuration() * 60 * 1000) - DateUtils.getNowDate().getTime();
- vo.setRemainingMilliseconds(remaining);
- vo.setCompulsiveSubmit(remaining >= RealExam.EXAM_TIMEOUT_LIMIT);
- l.info("studentLoopAnsweringRealExam vo = {}", vo);
- return AjaxResult.success(vo);
- }
- /**
- * [学生]交卷考试
- *
- * @param examId
- * @return RealExam
- */
- @Transactional
- public AjaxResult studentSubmitRealExam(Long examId) {
- RealExam re = selectRealExamByExamId(examId);
- {
- // todo:delete
- // re.setSimId(getFakeSimId(re));
- }
- // check part.
- {
- AjaxResult arE1 = checkExamId(examId);
- if (arE1.isError()) {
- return arE1;
- }
- }
- // 检查一下模拟器状态。
- Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
- // 如果模拟器离线
- {
- // AjaxResult arE3 = commSendService.checkOneSimStateActive(seat);
- AjaxResult arE3 = commCheckService.checkOneSeatState(seat, true);
- if (arE3.isError()) {
- return arE3;
- }
- }
- // 最后读取一下模拟器电阻值。
- commSendService.readOneExamAtLast(re);
- if (realExamFaultService.isType2ExamPrepareSubmitOk(re.getExamId())) {
- re.setExamStatus(RealExam.State.SUBMITTED);
- // 修改真实考试结束时间。
- re.setEndTime(DateUtils.getNowDate());
- updateRealExam(re);
- return AjaxResult.success("成功交卷!");
- } else {
- return AjaxResult.error("失败交卷!");
- }
- }
- /**
- * [轮询][学生]结束考试界面。
- *
- * @param examId
- * @return StudentRealExamPostVo
- */
- public AjaxResult studentLoopPostRealExam(Long examId) {
- RealExam re = selectRealExamByExamId(examId);
- StudentRealExamPostVo vo = new StudentRealExamPostVo();
- {
- }
- vo.setRealExam(re);
- vo.setListPart1(realExamFaultService.getReportListPart1(examId));
- vo.setListPart2(realExamFaultService.getReportListPart2(examId));
- vo.setPart3(realExamFaultService.getReportPart3(examId));
- return AjaxResult.success(vo);
- }
- }
|