CommSendService.java 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. package com.ruoyi.sim.service.impl;
  2. import com.ruoyi.common.core.domain.AjaxResult;
  3. import com.ruoyi.common.utils.DateUtils;
  4. import com.ruoyi.sim.config.SimConfig;
  5. import com.ruoyi.sim.constant.CommConst;
  6. import com.ruoyi.sim.domain.*;
  7. import com.ruoyi.sim.domain.vo.ScanSeatVo;
  8. import com.ruoyi.sim.domain.vo.SimSocketParamVo;
  9. import org.apache.commons.lang3.StringUtils;
  10. import org.slf4j.LoggerFactory;
  11. import org.slf4j.Logger;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.scheduling.annotation.Async;
  14. import org.springframework.stereotype.Service;
  15. import org.springframework.transaction.annotation.Transactional;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.io.OutputStream;
  19. import java.net.Socket;
  20. import java.util.*;
  21. import static com.ruoyi.sim.constant.CommConst.*;
  22. /**
  23. * 硬件通信
  24. * send service.
  25. * "commSendService"
  26. */
  27. @Service
  28. public class CommSendService {
  29. private static final Logger l = LoggerFactory.getLogger(CommSendService.class);
  30. @Autowired
  31. private CommReceiveService simReceiveService;
  32. @Autowired
  33. private SeatService seatService;
  34. @Autowired
  35. private SimService simService;
  36. @Autowired
  37. private FaultService faultService;
  38. @Autowired
  39. private RealExamService realExamService;
  40. @Autowired
  41. private RealExamFaultService realExamFaultService;
  42. @Autowired
  43. private RealExamCollectionService realExamCollectionService;
  44. @Autowired
  45. private CommBuildService commBuildService;
  46. @Autowired
  47. private DebugFaultService debugFaultService;
  48. // private SocketOldService socketOldService;
  49. @Autowired
  50. private SocketService socketService;
  51. @Autowired
  52. private CommCheckService commCheckService;
  53. @Autowired
  54. CommReceiveService commReceiveService;
  55. @Autowired
  56. private SimConfig config;
  57. @Autowired
  58. private CommStrategy commStrategy;
  59. /**
  60. * 定时任务。
  61. */
  62. public void scheduledLoopRead() {
  63. readAll();
  64. }
  65. public void readAll() {
  66. l.info("readAll");
  67. List<RealExam> listRe = realExamService.listAllByStatus(RealExam.State.ANSWERING);
  68. listRe.forEach(e -> {
  69. if (e == null) {
  70. return;
  71. }
  72. List<RealExamFault> listRef = realExamFaultService.listAllType2State2and3ByExamId(e.getExamId());
  73. listRef.forEach(ref -> {
  74. RealExam re = realExamService.selectRealExamByExamId(ref.getExamId());
  75. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  76. Sim sim = simService.selectSimBySimId(e.getSimId());
  77. Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
  78. if (f != null && Fault.Type.REAL_GZBW.equals(f.getFaultType()) && Fault.State.ENABLE.equals(f.getFaultState())) {
  79. readOneSimOneFaultResistance(seat, sim, ref, f, null);
  80. }
  81. });
  82. });
  83. }
  84. /**
  85. * Async version.
  86. */
  87. @Async("tp-comm")
  88. public void readAllAsync() {
  89. readAll();
  90. }
  91. public void readOneExamAtLast(RealExam re) {
  92. l.info("readOneExamAtLast");
  93. List<RealExamFault> list = realExamFaultService.listAllType2State2and3ByExamId(re.getExamId());
  94. for (RealExamFault ref : list) {
  95. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  96. Sim sim = simService.selectSimBySimId(re.getSimId());
  97. Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
  98. readOneSimOneFaultResistance(seat, sim, ref, f, RealExamFault.State.FINISH);
  99. }
  100. // 计算扣分。
  101. // 最后都读取到,才算扣分。
  102. {
  103. // todo:暂时不算。
  104. // calculateScore(re.getExamId());
  105. }
  106. }
  107. public void readOneSimAtLastByDebug(Seat seat, Sim sim) {
  108. l.info("readOneSimAtLastByDebug");
  109. List<Fault> list = faultService.listType3(sim.getSimType());
  110. for (Fault f : list) {
  111. readOneSimOneFaultResistance(seat, sim, null, f, null);
  112. }
  113. }
  114. public AjaxResult debugReadAllFaultResistanceBySimNum(final Long seatId) {
  115. // check
  116. //
  117. // 打开socket
  118. {
  119. socketService.tryOpenAll();
  120. }
  121. //
  122. Seat seat = seatService.selectSeatBySeatId(seatId);
  123. Sim sim = getSimBySeatIdNewVer(seatId);
  124. {
  125. AjaxResult arE3 = checkOneSimStateActive(seat);
  126. if (arE3.isError()) {
  127. return arE3;
  128. }
  129. }
  130. sim = simService.uniqueBySimNum(sim.getSimNum());
  131. // todo: aj改造
  132. readOneSimAtLastByDebug(seat, sim);
  133. return AjaxResult.success("成功读取!");
  134. }
  135. @Async("tp-comm")
  136. public void readOneExamAtLastAsync(RealExam re) {
  137. l.info("readOneExamAtLastAsync");
  138. readOneExamAtLast(re);
  139. }
  140. /**
  141. * 计算减分(不包括超时)。汇总到deduction_total_score字段。
  142. *
  143. * @param realExamId
  144. */
  145. public void calculateScore(Long realExamId) {
  146. realExamService.updateOneState(realExamId, RealExam.State.CALCULATING_SCORE);
  147. int minus = 0;
  148. // 排除故障部分 + 维修报告选择部分
  149. realExamFaultService.calculateMinusByRealExamId(realExamId);
  150. // 超时部分
  151. int timeoutMinute = 0;
  152. //
  153. realExamService.updateOneState(realExamId, RealExam.State.GOT_REPORT);
  154. }
  155. /**
  156. * todo:????
  157. * 定时任务。
  158. */
  159. public void scheduledReadSim() {
  160. l.info("scheduledReadSim");
  161. if (!realExamCollectionService.existAtLeastOneOpened()) {
  162. l.info("没有open的考试集合");
  163. return;
  164. }
  165. readAll();
  166. //
  167. // AA010102010000000055
  168. // AA010102010000000055
  169. String simNum = "01";
  170. String simType = "0002";
  171. // for (int i = 0; i < 3; i++) {
  172. // debugReadSimType(simNum);
  173. // }
  174. // debugClearAllFault(simNum, simType);
  175. // debugWriteAllFault(simNum, simType);
  176. // for (int i = 0; i < 3; i++) {
  177. // debugReadAllFaultResistance(simNum, simType);
  178. // }
  179. // debugClearOneFault(simNum, "02");
  180. // boolean bRea = isReachable("123.112.16.165");
  181. // debugReadSimType(simNum);
  182. // l.info("bRea:" + bRea);
  183. // debugReadAllFaultResistance(simNum, simType);
  184. // debugWriteAllFault(simNum, simType);
  185. // for (int i = 0; i < 5; i++) {
  186. // debugReadAllFaultResistance(simNum, simType);
  187. // }
  188. // if (isSocketOk()) {
  189. // readAllAsync();
  190. // } else {
  191. // scheduledConnect();
  192. // }
  193. }
  194. /**
  195. * 连接情况 的 定时任务。
  196. * 执行频率: 5min
  197. */
  198. public void scheduledConnect() {
  199. l.info("scheduled####Connect 连接情况 的 定时任务");
  200. // if (!SimDebugConfig.SCHEDULED_CONNECT) {
  201. if (true) {
  202. l.info("连接情况 的 定时任务被禁用!");
  203. return;
  204. }
  205. // 暂时注释
  206. // if (!realExamCollectionService.existAtLeastOneOpened()) {
  207. // l.info("没有open的任何集合");
  208. // return;
  209. // }
  210. //
  211. {
  212. AjaxResult ar = commCheckService.checkRouterState(config.getRouterIp());
  213. if (ar.isError()) {
  214. return;
  215. }
  216. }
  217. //
  218. {
  219. seatService.listAllRs485Ip().forEach(rs485Ip -> {
  220. commCheckService.checkPingRs485State(rs485Ip);
  221. });
  222. }
  223. // SocketOldService实现
  224. // if (socketOldService.isCachedSocketOk()) {
  225. // checkAllSeatAndSimState();
  226. // }
  227. socketService.tryOpenAll();
  228. commCheckService.checkAllSeatAndSimState();
  229. }
  230. /**
  231. * 主动更新模拟器状态。
  232. * <p>
  233. * <p>
  234. * 主动查询一次模拟器状态。更新模拟器在线/离线状态;否则返回对应错误。
  235. * todo:需要重新考虑,不再使用。
  236. *
  237. * @param seat
  238. * @return
  239. */
  240. @Deprecated
  241. public AjaxResult checkOneSimStateActive(Seat seat) {
  242. // 这句可能会调整模拟器状态,后面需要重新查询。
  243. commCheckService.checkOneSeatState(seat, true);
  244. AjaxResult ar1 = commCheckService.checkOneSimOnlineState(seat, true);
  245. if (ar1.isError()) {
  246. return ar1;
  247. }
  248. // 重新最新模拟器。
  249. Sim sim = getSimBySeatIdNewVer(seat.getSeatId());
  250. // 如果模拟器离线
  251. if (sim != null && Sim.State.ONLINE.equals(sim.getSimState())) {
  252. return AjaxResult.success();
  253. } else {
  254. return AjaxResult.error("未连接模拟器,请检查连接!");
  255. }
  256. }
  257. @Async("tp-comm")
  258. public void checkAllSimStateAsync() {
  259. commCheckService.checkAllSeatAndSimState();
  260. }
  261. /**
  262. * 清除一个考试的,对应的某型号一台模拟器的,所有故障部位。
  263. *
  264. * @param re
  265. */
  266. public void clearOneSimAllFaultByExam(RealExam re) {
  267. l.info("clearOneSimAllFaultByExam = {}", re);
  268. // check
  269. // 更新Exam状态。
  270. {
  271. realExamService.updateOneState(re.getExamId(), RealExam.State.SIM_WRITING);
  272. }
  273. //
  274. List<RealExamFault> list = realExamFaultService.listAllType2InitStateByExamId(re.getExamId());
  275. if (list != null) {
  276. l.info("清除exam list = {}", list.size());
  277. }
  278. assert list != null;
  279. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  280. list.forEach(ref -> {
  281. Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
  282. if (faultService.isDisable(f.getFaultId())) {
  283. l.warn("故障 {} -被禁用", f.getName());
  284. throw new IllegalArgumentException("故障被禁用");
  285. }
  286. Sim sim = simService.selectSimBySimId(re.getSimId());
  287. // check
  288. //
  289. clearOneSimOneFault(seat, sim, ref, f);
  290. });
  291. }
  292. public void clearOneSimAllFaultBySim(Seat seat, Sim sim) {
  293. l.info("clearOneSimAllFaultBySim = {}", sim);
  294. faultService.listType3EnableBySimType(sim.getSimType()).forEach(f -> {
  295. clearOneSimOneFault(seat, sim, null, f);
  296. });
  297. }
  298. /**
  299. * Async version.
  300. *
  301. * @param re
  302. */
  303. @Async("tp-comm")
  304. public void clearListFaultByRealExamAsync(RealExam re) {
  305. clearOneSimAllFaultByExam(re);
  306. }
  307. public void clearAll() {
  308. // todo:
  309. // 根据Seat数据遍历
  310. seatService.listAllEnable().forEach(seat -> {
  311. Sim sim = getSimBySeatIdNewVer(seat.getSeatId());
  312. String simType = sim.getSimType();
  313. List<Fault> listF = faultService.listType3EnableBySimType(simType);
  314. listF.forEach(f -> {
  315. clearOneSimOneFault(seat, sim, null, f);
  316. });
  317. });
  318. }
  319. /**
  320. * debug读取模拟器类型序列号
  321. *
  322. * @param seatId
  323. * @return
  324. */
  325. public SimMsg debugReadSimType(final Long seatId) {
  326. Seat seat = seatService.selectSeatBySeatId(seatId);
  327. Sim sim = getSimBySeatIdNewVer(seatId);
  328. SimMsg sm = commBuildService.buildSendMsgReadSimType(sim.getSimNum());
  329. return send(sm, seat, sim, CommConst.RETRY_COUNT_0, commStrategy.getSleepShort());
  330. }
  331. /**
  332. * debug清除一个故障
  333. *
  334. * @param seatId
  335. * @param bindHardwareMsg
  336. * @return
  337. */
  338. public SimMsg debugClearOneFault(final Long seatId, final String bindHardwareMsg) {
  339. Seat seat = seatService.selectSeatBySeatId(seatId);
  340. Sim sim = getSimBySeatIdNewVer(seatId);
  341. SimMsg sm = commBuildService.buildSendMsgClearFault(sim.getSimNum(), bindHardwareMsg);
  342. return send(sm, seat, sim, RETRY_COUNT_CLEAR_ONE_FAULT, commStrategy.getSleepLong());
  343. }
  344. public AjaxResult debugClearAllOnlineSimAllFault() {
  345. l.info("debugClearAllOnlineSimAllFault");
  346. simService.listAllOnline().forEach(s -> {
  347. // AjaxResult ar = debugClearAllFaultBySeatId(0);// todo:尚未实现
  348. });
  349. debugFaultService.deleteAll();
  350. return AjaxResult.success("清除成功,清除所有在线模拟器所有故障!");
  351. }
  352. /**
  353. * debug清除所有故障
  354. *
  355. * @param seatId
  356. * @return
  357. */
  358. @Transactional
  359. public AjaxResult debugClearAllFaultBySeatId(final Long seatId) {
  360. {
  361. AjaxResult ar = debugCheckSeatId(seatId);
  362. if (ar.isError()) {
  363. return ar;
  364. }
  365. }
  366. debugFaultService.deleteAll();
  367. Seat seat = seatService.selectSeatBySeatId(seatId);
  368. Sim sim = getSimBySeatIdNewVer(seatId);
  369. if (sim == null) {
  370. return AjaxResult.error("清除失败,模拟器不存在!请检查模拟器线缆连接,模拟器线缆开关!");
  371. }
  372. sim = simService.selectSimBySimId(sim.getSimId());
  373. if (!Sim.State.ONLINE.equals(sim.getSimState())) {
  374. return AjaxResult.error("清除失败,模拟器尚未在线!");
  375. }
  376. List<SimMsg> list = new ArrayList<>();
  377. for (String b : getGZBWBySimType(sim.getSimType())) {
  378. SimMsg sm = debugClearOneFault(seatId, b);
  379. if (sm != null && !Objects.equals(sm.getResult(), SimMsg.Result.SUCCESS)) {
  380. AjaxResult.success("清除失败!");
  381. }
  382. list.add(sm);
  383. }
  384. return AjaxResult.success("清除成功,清除当前模拟器所有故障!");
  385. }
  386. /**
  387. * @param sim
  388. * @param reF 可以为空,表示不关联考试的,单独执行的。调试模式下为空。
  389. * @param f
  390. */
  391. public void clearOneSimOneFault(Seat seat, Sim sim, RealExamFault reF, Fault f) {
  392. l.info("clearOneSimOneFault 清除One故障:getSimNum = {},getBindHardwareMsg = {},fault.getName = {}", sim.getSimNum(), f.getBindHardwareMsg(), f.getName());
  393. // check todo:
  394. // step1
  395. SimMsg smS = commBuildService.buildSendMsgClearFault(sim.getSimNum(), f.getBindHardwareMsg());
  396. SimMsg smR = send(smS, seat, sim, RETRY_COUNT_CLEAR_ONE_FAULT, commStrategy.getSleepLong());
  397. if (reF != null) {
  398. simReceiveService.clearOneFault(smR, sim, reF, f);
  399. } else {
  400. l.info("reF == null");
  401. }
  402. // step2
  403. // 下发故障独立运行。下面屏蔽。
  404. // if (reF != null &&
  405. // realExamFaultService.isState(reF.getRefId(), RealExamFault.State.CLEARED)) {
  406. // if (reF.getFlag().equals(RealExamFault.Flag.YES)) {
  407. // writeOneFault(s, reF, f);
  408. // } else if (reF.getFlag().equals(RealExamFault.Flag.NO)) {
  409. // RealExamFault f1 = realExamFaultService.selectRealExamFaultByRefId(reF.getRefId());
  410. // f1.setRefState(RealExamFault.State.LOOP_READ);
  411. // }
  412. // }
  413. }
  414. /**
  415. * debug下发一个故障
  416. *
  417. * @param seatId
  418. * @param bindHardwareMsg
  419. * @return
  420. * @throws IOException
  421. */
  422. public SimMsg debugWriteOneFault(final Long seatId,
  423. final String bindHardwareMsg) {
  424. Seat seat = seatService.selectSeatBySeatId(seatId);
  425. Sim sim = getSimBySeatIdNewVer(seatId);
  426. SimMsg sm = commBuildService.buildSendMsgWriteFault(sim.getSimNum(), bindHardwareMsg);
  427. return send(sm, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, commStrategy.getSleepLong());
  428. }
  429. /**
  430. * debug下发所有故障
  431. *
  432. * @param seatId
  433. * @return
  434. * @throws IOException
  435. */
  436. public List<SimMsg> debugWriteAllFault(final Long seatId) {
  437. List<SimMsg> list = new ArrayList<>();
  438. Sim sim = getSimBySeatIdNewVer(seatId);
  439. String simType = sim.getSimType();
  440. for (String bind : getGZBWBySimType(simType)) {
  441. list.add(debugWriteOneFault(seatId, bind));
  442. }
  443. return list;
  444. }
  445. /**
  446. * todo:尚未实现
  447. * 实现方式类似 studentStartRealExam方法。
  448. *
  449. * @param seatId
  450. * @param faultIds
  451. * @param checkReplace 是否进行可换件检查
  452. * @return
  453. */
  454. @Transactional
  455. public AjaxResult debugWriteSelectedFaultBySimNum(final Long seatId, final String[] faultIds, final Boolean checkReplace) {
  456. // Check:seatId有效性
  457. {
  458. AjaxResult ar = debugCheckSeatId(seatId);
  459. if (ar.isError()) {
  460. return ar;
  461. }
  462. }
  463. // Check:faultIds有效性
  464. {
  465. if (faultIds == null || faultIds.length == 0) {
  466. return AjaxResult.error("下发故障数据为空!");
  467. }
  468. }
  469. // 删除调试表故障数据。
  470. debugFaultService.deleteAll();
  471. //
  472. Seat seat = seatService.selectSeatBySeatId(seatId);
  473. Sim sim = getSimBySeatIdNewVer(seatId);
  474. // check sim
  475. // 打开socket
  476. {
  477. socketService.tryOpenAll();
  478. }
  479. // Step 1 主动查询一次模拟器状态。
  480. {
  481. AjaxResult arE3 = checkOneSimStateActive(seat);
  482. if (arE3.isError()) {
  483. return arE3;
  484. }
  485. }
  486. sim = simService.uniqueBySimNum(sim.getSimNum());
  487. // Step 2
  488. // msg判断,是否含有"故障部位"字符串
  489. {
  490. if (checkReplace) {
  491. AjaxResult arE2 = readOneSimAllFaultCheck(seat, sim);
  492. if (arE2.isError()) {
  493. return arE2;
  494. }
  495. }
  496. }
  497. // Step 3 清除对应一台模拟器 所有故障部位故障。
  498. {
  499. clearOneSimAllFaultBySim(seat, sim);
  500. }
  501. // Step 4 下发对应一台模拟器 出题选中的 故障位置故障。
  502. {
  503. Fault[] faults = new Fault[faultIds.length];
  504. for (int i = 0; i < faultIds.length; i++) {
  505. faults[i] = faultService.selectFaultByFaultId(faultIds[i]);
  506. }
  507. writeOneSimAllSelectFaultByDebug(seat, sim, faults);
  508. }
  509. // Step 5 读取
  510. {
  511. readOneSimAllFaultFirstTimeBySim(seat, sim, faultIds);
  512. }
  513. return AjaxResult.success("下发故障流程执行成功!");
  514. }
  515. /**
  516. * debug校验seatId
  517. *
  518. * @param seatId
  519. * @return
  520. */
  521. public AjaxResult debugCheckSeatId(final Long seatId) {
  522. if (seatId == null || seatId <= 0) {
  523. return AjaxResult.error("座次信息不正确!");
  524. }
  525. Seat seat = seatService.selectSeatBySeatId(seatId);
  526. if (seat == null) {
  527. return AjaxResult.error("座次信息不存在!");
  528. }
  529. return AjaxResult.success();
  530. }
  531. /**
  532. * debug扫描所有座次模拟器
  533. *
  534. * @return
  535. */
  536. public AjaxResult debugScanAllSeat() {
  537. List<ScanSeatVo> list = new ArrayList<>();
  538. seatService.listAllEnable().forEach(seat -> {
  539. AjaxResult ar = commCheckService.checkOneSeatState(seat, true);
  540. String text = ar.get(AjaxResult.MSG_TAG).toString();
  541. // 新查询
  542. seat = seatService.selectSeatBySeatId(seat.getSeatId());
  543. Sim sim = simService.selectSimBySimId(seat.getCurrentSimId());
  544. if (sim != null) {
  545. list.add(new ScanSeatVo(seat.getSeatId(), sim.getSimId(), sim.getSimType(), text));
  546. } else {
  547. list.add(new ScanSeatVo(seat.getSeatId(), 0L, "", text));
  548. }
  549. });
  550. return AjaxResult.success(list);
  551. }
  552. private String[] getGZBWBySimType(String simType) {
  553. if (StringUtils.isBlank(simType)) {
  554. return new String[0];
  555. }
  556. switch (simType) {
  557. case Sim.TYPE_0001 -> {
  558. return TYPE_1_BIND_MSG;
  559. }
  560. case Sim.TYPE_0002 -> {
  561. return TYPE_2_BIND_MSG;
  562. }
  563. case Sim.TYPE_0003 -> {
  564. return TYPE_3_BIND_MSG;
  565. }
  566. default -> {
  567. return new String[0];
  568. }
  569. }
  570. }
  571. /**
  572. * 下发所有选中的真实的故障部位。
  573. *
  574. * @param re
  575. */
  576. public void writeOneSimAllSelectFaultByExam(RealExam re) {
  577. // 更新Exam状态。
  578. realExamService.updateOneState(re.getExamId(), RealExam.State.SIM_WRITING);
  579. List<RealExamFault> list = realExamFaultService.listAllType2FlagYesClearedStateByExamId(re.getExamId());
  580. for (RealExamFault ref : list) {
  581. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  582. Sim sim = simService.selectSimBySimId(re.getSimId());
  583. Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
  584. // 选中的故障才下发。
  585. writeOneSimOneFault(seat, sim, ref, f);
  586. }
  587. }
  588. public void writeOneSimAllSelectFaultByDebug(Seat seat, Sim sim, Fault[] faults) {
  589. for (Fault f : faults) {
  590. writeOneSimOneFault(seat, sim, null, f);
  591. }
  592. }
  593. public void writeOneSimOneFault(Seat seat, Sim sim, RealExamFault ref, Fault f) {
  594. l.info("下发故障:getSimId = {},fault.getName = {}", sim.getSimId(), f.getName());
  595. // todo:ref is null.
  596. // 下发故障
  597. SimMsg smA1 = commBuildService.buildSendMsgWriteFault(sim.getSimNum(), f.getBindHardwareMsg());
  598. SimMsg smA2 = send(smA1, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, commStrategy.getSleepLong());
  599. }
  600. /**
  601. * 开始考试前检查读取。
  602. *
  603. * @param sim
  604. * @return
  605. */
  606. public AjaxResult readOneSimAllFaultCheck(Seat seat, Sim sim) {
  607. Fault q = new Fault();
  608. q.setFaultType(Fault.Type.REAL_GZBW);
  609. q.setSimType(sim.getSimType());
  610. List<Fault> list = faultService.selectFaultList(q);
  611. List<Fault> listNG = new ArrayList<>();
  612. for (Fault f : list) {
  613. AjaxResult ar = readOneSimOneFaultCheck(seat, sim, f);
  614. if (ar.isError()) {
  615. listNG.add(f);
  616. l.info("故障部位[" + f.getBindHardwareMsg() + "][" + f.getReplaceName() + "]未正确安装;");
  617. } else {
  618. l.info("故障部位[" + f.getBindHardwareMsg() + "][" + f.getReplaceName() + "]安装ok;");
  619. }
  620. }
  621. if (listNG.isEmpty()) {
  622. return AjaxResult.success("所有故障部位检查没有问题。");
  623. }
  624. StringBuilder sbNG = new StringBuilder();
  625. for (Fault f : listNG) {
  626. sbNG.append("[" + f.getReplaceName() + "]可换件异常;<br>");
  627. }
  628. sbNG.append("请正确安装可换件,检查后重新开始考试!");
  629. return AjaxResult.error(sbNG.toString());
  630. }
  631. /**
  632. * 检查读取。
  633. *
  634. * @param sim
  635. * @param f
  636. * @return
  637. */
  638. public AjaxResult readOneSimOneFaultCheck(Seat seat, Sim sim, Fault f) {
  639. l.info("readOneSimOneFaultCheck sim = {},f = {}", sim, f);
  640. SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
  641. SimMsg sm2 = send(sm1, seat, sim, RETRY_COUNT_CHECK_ONE_FAULT, commStrategy.getSleepLong());
  642. return simReceiveService.getOneFaultCheck(sm2, sim, f);
  643. }
  644. /**
  645. * 第一次读取,作为出题值。
  646. *
  647. * @param re
  648. */
  649. public void readOneSimAllFaultFirstTimeByExam(RealExam re) {
  650. l.info("readOneSimAllFaultFirstTimeByExam re = {}", re);
  651. List<RealExamFault> list = realExamFaultService.listAllType2(re.getExamId());
  652. for (RealExamFault ref : list) {
  653. Seat seat = seatService.selectSeatBySeatId(re.getSeatId());
  654. Sim sim = simService.selectSimBySimId(re.getSimId());
  655. Fault f = faultService.selectFaultByFaultId(ref.getFaultId());
  656. readOneSimOneFaultFirstTime(seat, sim, ref, f, null);
  657. }
  658. }
  659. /**
  660. * 第一次读取,作为出题值。debug模式。
  661. *
  662. * @param sim
  663. * @param faultIds
  664. */
  665. public void readOneSimAllFaultFirstTimeBySim(Seat seat, Sim sim, final String[] faultIds) {
  666. l.info("readOneSimAllFaultFirstTimeBySim s = {}", sim);
  667. List<Fault> list = faultService.listType3(sim.getSimType());
  668. for (Fault f : list) {
  669. readOneSimOneFaultFirstTime(seat, sim, null, f, faultIds);
  670. }
  671. }
  672. /**
  673. * 第一次读取,作为出题值。
  674. *
  675. * @param sim
  676. * @param ref debug调试模式为空。可以为空。
  677. * @param f
  678. * @param faultIds debug调试模式为空。
  679. */
  680. public void readOneSimOneFaultFirstTime(Seat seat, Sim sim, RealExamFault ref, Fault f, String[] faultIds) {
  681. l.info("readOneSimOneFaultFirstTime");
  682. // 读取一次当前电阻代表值作为出题值。
  683. SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
  684. SimMsg sm2 = send(sm1, seat, sim, RETRY_COUNT_WRITE_ONE_FAULT, commStrategy.getSleepLong());
  685. simReceiveService.setFaultQuestionValue(sm2, sim, ref, f, faultIds);
  686. }
  687. /**
  688. * debug读取一个故障位置数据
  689. *
  690. * @param seatId
  691. * @param bindHardwareMsg
  692. * @return
  693. */
  694. public SimMsg debugReadOneFaultResistance(final Long seatId,
  695. final String bindHardwareMsg) {
  696. Seat seat = seatService.selectSeatBySeatId(seatId);
  697. Sim sim = getSimBySeatIdNewVer(seatId);
  698. SimMsg sm = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), bindHardwareMsg);
  699. return send(sm, seat, null, RETRY_COUNT_0, commStrategy.getSleepShort());
  700. }
  701. /**
  702. * debug读取全部故障位置数据
  703. *
  704. * @param seatId
  705. * @return
  706. */
  707. public List<SimMsg> debugReadAllFaultResistance(final Long seatId) {
  708. Sim sim = getSimBySeatIdNewVer(seatId);
  709. List<SimMsg> list = new ArrayList<>();
  710. String simType = simService.uniqueBySimNum(sim.getSimNum()).getSimType();
  711. for (String b : getGZBWBySimType(simType)) {
  712. list.add(debugReadOneFaultResistance(seatId, b));
  713. }
  714. return list;
  715. }
  716. /**
  717. * @param sim
  718. * @param reF
  719. * @param f
  720. * @param refState 中间轮询是null,交卷最后一次读取为finish状态。用来修改状态的。debug模式下执行为null。
  721. */
  722. public void readOneSimOneFaultResistance(Seat seat, Sim sim, RealExamFault reF, Fault f, String refState) {
  723. l.info("readOneSimOneFaultResistance");
  724. SimMsg sm1 = commBuildService.buildSendMsgReadFaultResistance(sim.getSimNum(), f.getBindHardwareMsg());
  725. SimMsg sm2 = null;
  726. if (reF != null && refState != null) {
  727. if (RealExamFault.State.FINISH.equals(refState)) { // 是否最后一次读取。
  728. sm2 = send(sm1, seat, sim, RETRY_COUNT_READ_ONE_RESISTANCE, commStrategy.getSleepShort());
  729. } else {
  730. sm2 = send(sm1, seat, sim, RETRY_COUNT_0, commStrategy.getSleepShort());
  731. }
  732. } else {
  733. sm2 = send(sm1, seat, sim, RETRY_COUNT_READ_ONE_RESISTANCE, commStrategy.getSleepShort());
  734. }
  735. simReceiveService.setFaultAnswerValue(sm2, sim, reF, f, refState);
  736. }
  737. /**
  738. * 最基本的通信方法。
  739. * send hex message
  740. * <p>
  741. * 去掉synchronized
  742. *
  743. * @param sm 发送
  744. * @param seat 不能为空
  745. * @param sim 可以为空!更新最后发送/接收时间 用。
  746. * @param retryTotalCount 重试次数
  747. * @param sleep 不使用传入0,不进行挂起。
  748. * @return
  749. */
  750. public SimMsg send(final SimMsg sm, final Seat seat, final Sim sim, final int retryTotalCount, final long sleep) {
  751. try {
  752. if (!config.isCommGlobal()) {
  753. l.warn("isCommGlobal == false [模拟器通信被禁用!]");
  754. return sm;
  755. }
  756. if (sm == null || sm.getSendMsg() == null || StringUtils.isBlank(sm.getSendMsg())) {
  757. throw new IllegalArgumentException("SimMsg IllegalArgument");
  758. }
  759. // sim
  760. if (seat == null) {
  761. throw new IllegalArgumentException("seat is null");
  762. }
  763. if (sleep < 0) {
  764. throw new IllegalArgumentException("SimMsg sleep");
  765. }
  766. // log.
  767. {
  768. l.info("####发送#### == Seat[{}],SimMsg[{}]", seat, sm);
  769. }
  770. // 如果没有打开socket,顺道打开。正好后面要sleep。
  771. // 不强制重开Socket。
  772. // 先进行Socket相关处理。
  773. SimSocketParamVo sspv = seat.toSimSocketParamVo();
  774. socketService.openOne(sspv, socketService.isNotOk(sspv));
  775. // Socket情况不正确,直接返回。
  776. if (socketService.isNotOk(sspv)) {
  777. sm.setResult(SimMsg.Result.SOCKET_CONNECT_EXCEPTION);
  778. return sm;
  779. }
  780. {
  781. // sleep挂起线程,追求顺序请求。
  782. // 大于0才挂起。
  783. if (sleep > 0 && socketService.get(sspv).getPreviousSendSleep() > 0L) {
  784. // 时间间隔挂起。
  785. Thread.sleep(socketService.get(sspv).getPreviousSendSleep());
  786. }
  787. }
  788. socketService.get(sspv).setPreviousSendSleep(sleep);
  789. Socket socket = socketService.get(sspv).getSocket();
  790. InputStream is = socket.getInputStream();
  791. OutputStream os = socket.getOutputStream();
  792. os.write(hexStrToByteArrs(sm.getSendMsg()));
  793. sm.setSendTime(DateUtils.getNowDate());
  794. if (sim != null) {
  795. simService.updateLastSentTime(sim);
  796. }
  797. byte[] buffer = new byte[LENGTH_24];
  798. int length = is.read(buffer);
  799. StringBuilder sbHex = new StringBuilder();
  800. for (int i = 0; i < length; i++) {
  801. sbHex.append(String.format("%02X", buffer[i]));
  802. }
  803. String receiveWith0 = sbHex.toString();
  804. // 原始带0的收到报文。
  805. sm.setReceiveOriginalMsg(receiveWith0);
  806. sm.setReceiveMsg(commReceiveService.removeRrefix0(receiveWith0));
  807. sm.setReceiveTime(DateUtils.getNowDate());
  808. // log.
  809. {
  810. AjaxResult ar = commReceiveService.checkReceiveMsg(sm.getReceiveMsg());
  811. if (ar.isError()) {
  812. // todo:
  813. l.warn("####接收错误#### = {}", sm);
  814. sm.setResult(SimMsg.Result.RECEIVE_CHECK_FAIL);
  815. return sm;
  816. } else {
  817. l.info("####接收成功#### = {}", sm);
  818. }
  819. }
  820. if (sim != null) {
  821. simService.updateLastReceivedTime(sim);
  822. }
  823. sm.setResult(SimMsg.Result.SUCCESS);
  824. // 最后返回报文实体。
  825. return sm;
  826. } catch (InterruptedException | IOException e) {
  827. // SocketTimeoutException
  828. l.error("SocketTimeoutException");
  829. e.printStackTrace();
  830. sm.setResult(SimMsg.Result.READ_TIMEOUT_EXCEPTION);
  831. if (sim != null) {
  832. l.info("fail sim.getSimId() = {}", sim.getSimId());
  833. }
  834. SimSocketParamVo sspv = seat.toSimSocketParamVo();
  835. // Socket失败计数
  836. socketService.failedPlus1(sspv);
  837. if (socketService.failedIsReachedMax(sspv, SocketService.SOCKET_CONNECT_RETRY_COUNT_LIMIT)) {
  838. // 达到重试次数上限,认为模拟器离线
  839. if (sim != null) {
  840. simService.updateSimStateBySimId(sim.getSimId(), Sim.State.OFFLINE);
  841. }
  842. socketService.failedReset0(sspv);
  843. }
  844. // 先考虑一台模拟器演示。
  845. // 进行重试 start
  846. if (sm.getRetryCount() == RETRY_COUNT_0) {
  847. l.warn("####RetryTotalCount不重试RETRY_COUNT_0#### = getSimMsgId = {}", sm.getSimMsgId());
  848. return sm;
  849. }
  850. if (sm.getRetryCount() < retryTotalCount) {
  851. sm.retryCountPlus1();
  852. send(sm, seat, sim, retryTotalCount, sleep);
  853. l.warn("####RetryTotalCount重试#### = {}", sm);
  854. } else {
  855. l.warn("####RetryTotalCount达到重试上限#### = {}", sm);
  856. }
  857. // 进行重试 end
  858. // 最后返回报文实体。
  859. return sm;
  860. }
  861. }
  862. /**
  863. * todo:
  864. *
  865. * @param sm
  866. * @param s
  867. * @param retryTotalCount
  868. * @param sleep
  869. * @return
  870. */
  871. public SimMsg sendRetry(final SimMsg sm, final Sim s, final int retryTotalCount, final long sleep) {
  872. return null;
  873. }
  874. /**
  875. * https://mvnrepository.com/artifact/com.infiniteautomation/modbus4j/3.0.3
  876. */
  877. public void test02() {
  878. byte[] data = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02}; // 示例数据
  879. // int crc = ModbusUtils.calculateCRC(data);
  880. // System.out.println("CRC-16/MODBUS 校验值: " + Integer.toHexString(crc));
  881. }
  882. public static byte[] hexStrToByteArrs(String hexString) {
  883. // if (StringUtils.isEmpty(hexString)) {
  884. // return null;
  885. // }
  886. hexString = hexString.replaceAll(" ", "");
  887. int len = hexString.length();
  888. int index = 0;
  889. byte[] bytes = new byte[len / 2];
  890. while (index < len) {
  891. String sub = hexString.substring(index, index + 2);
  892. bytes[index / 2] = (byte) Integer.parseInt(sub, 16);
  893. index += 2;
  894. }
  895. return bytes;
  896. }
  897. public String logBytesToString(byte[] bytes) {
  898. if (bytes == null || bytes.length == 0) {
  899. return "";
  900. }
  901. StringBuffer sbHex = new StringBuffer();
  902. byte[] buffer = new byte[bytes.length];
  903. for (int i = 0; i < bytes.length; i++) {
  904. sbHex.append(String.format("%02X", buffer[i]));
  905. }
  906. return sbHex.toString();
  907. }
  908. public static String bytesToHexV2(byte[] bytes) {
  909. StringBuilder hexString = new StringBuilder();
  910. for (byte b : bytes) {
  911. String hex = Integer.toHexString(0xFF & b); // 将字节转换为十六进制字符串
  912. if (hex.length() == 1) {
  913. hexString.append('0'); // 如果是单个字符,补零
  914. }
  915. hexString.append(hex);
  916. }
  917. return hexString.toString();
  918. }
  919. /**
  920. * checkOneSeatState 包装调用。
  921. *
  922. * @param seatId
  923. * @return 可能为空
  924. */
  925. public Sim getSimBySeatIdNewVer(Long seatId) {
  926. Seat seat = seatService.selectSeatBySeatId(seatId);
  927. // 执行在线监测。
  928. AjaxResult ar = commCheckService.checkOneSeatState(seat, true);
  929. if (ar != null) {
  930. return (Sim) ar.get(AjaxResult.DATA_TAG);
  931. } else {
  932. return null;
  933. }
  934. }
  935. /**
  936. * todo:
  937. *
  938. * @return
  939. */
  940. public AjaxResult debugResetAnything() {
  941. // Step:关闭所有Socket连接
  942. // Step:ping路由器,返回在线情况。
  943. // Step:ping教员端主机,返回在线情况。
  944. // Step:ping所有学员端主机,返回在线情况列表。
  945. // Step:ping所有RS485,返回在线情况列表。
  946. // Step:建立所有模拟器Socket,返回情况列表。
  947. // Step:有Socket的前提下,尝试查询所有模拟器连接情况,得到连接的模拟器 型号、序列号信息
  948. // Step:所有连接的模拟器,清除每一个模拟器的各个故障
  949. // Step:所有连接的模拟器,读取每一个模拟器的各个故障,是否处于考试准备ok状态
  950. // 删除debug表中所有数据。
  951. try {
  952. Thread.sleep(5000L);
  953. } catch (InterruptedException e) {
  954. throw new RuntimeException(e);
  955. }
  956. return AjaxResult.success("全部重置成功。todo:123456");
  957. }
  958. }