CommSendService.java 33 KB

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