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