package com.ruoyi.sim.service.impl; import com.ruoyi.sim.config.SimConfig; import com.ruoyi.sim.domain.*; import org.apache.commons.lang3.StringUtils; import org.slf4j.LoggerFactory; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.List; import java.util.Objects; import static com.ruoyi.sim.service.impl.CommSendService.Const.*; /** * 硬件通信 * send service. */ @Service public class CommSendService { interface Const { String ROUTER_IP = "127.0.0.1"; String IP = "123.112.16.165"; int PORT = 8899; /** * 报文长度 */ int MSG_LENGTH = 20; String PREFIX = "AA"; String SUFFIX = "55"; /** * orientation */ String ORN_SEND = "01"; /** * orientation */ String ORN_RECEIVE = "02"; String CMD_DATA_PLACE_HOLDER = "00000000"; /** * 故障下发 */ String CMD_SET_FAULT = "01"; /** * 故障清清除 */ String CMD_CLEAR_FAULT = "02"; /** * 设备类型读取 */ String CMD_READ_TYPE = "03"; /** * 状态读取 RESISTANCE电阻值 */ String CMD_READ_FAULT_RESISTANCE = "03"; String CMD_ID_GET_SN = "B1"; int LENGTH_2 = 2; int LENGTH_8 = 8; int LENGTH_20 = 20; } private static final Logger l = LoggerFactory.getLogger(CommSendService.class); /** * 缓存的Socket连接。 */ private Socket cachedSocket = null; @Autowired private CommReceiveService simReceiveService; @Autowired private SimService simService; @Autowired private FaultService faultService; @Autowired private RealExamService realExamService; @Autowired private RealExamFaultService realExamFaultService; @Autowired private SimConfig sConfig; /** * 初始化方法,项目启动后自动运行。 */ public void init() { // try { if (!isReachable(ROUTER_IP)) { // todo:ping 不通。 } if (!isReachable(IP)) { // todo:ping 不通。 } openSocket(); // checkAllSimState(); } catch (IOException e) { throw new RuntimeException(e); } } /** * 定时任务。 */ public void scheduledLoopRead() { readAll(); } public void readAll() { List listRE = realExamService.listAllStatus(RealExam.STATE_ANSWERING); listRE.forEach(e -> { if (e == null) { return; } List listREF = realExamFaultService.listAllType2YesLoopReadState(e.getExamId()); listREF.forEach(ref -> { Sim s = simService.selectSimBySimId(e.getSimId()); Fault f = faultService.selectFaultByFaultId(ref.getFaultId()); if (f != null && f.getFaultType().equals(Fault.TYPE_3) && f.getFaultState().equals(Fault.STATE_ENABLE) ) readOneFaultResistance(s, ref, f); }); }); } /** * Async version. */ @Async("tp-comm") public void readAllAsync() { readAll(); } public void readOneExamAtLast(RealExam re) { List listREF = realExamFaultService.listAllType2YesLoopReadState(re.getExamId()); listREF.forEach(ref -> { Sim s = simService.selectSimBySimId(re.getSimId()); Fault f = faultService.selectFaultByFaultId(ref.getFaultId()); readOneFaultResistance(s, ref, f); }); realExamService.updateOneState(re, RealExam.STATE_SUBMITTED); } /** * 定时任务。 */ public void scheduledCheckAllSimState() { checkAllSimState(); } /** * 查找所有没有被手动禁用,并order by sim_num的模拟器列表。检查所有模拟器状态。 */ public void checkAllSimState() { List list = simService.listAllEnable(); list.forEach(s -> { checkOneSimState(s); }); } @Async() public void checkAllSimStateAsync() { } public void checkOneSimState(Sim s) { l.info(s.toString()); // check todo: if (Objects.isNull(s)) { return; } // try { SimMsg sm = new SimMsg(); String sendMsg = buildSendMsgReadSimType(s.getSimNum()); sm.setSendMsg(sendMsg); String receiveMsg = send(sendMsg, s); sm.setReceiveMsg(receiveMsg); simReceiveService.checkOneSimState(sm, s); } catch (IOException e) { throw new RuntimeException(e); } } /** * 清除一个考试的,对应的某型号一台模拟器的,所有设备故障。 * * @param re */ public void clearListFaultByRealExam(RealExam re) { // check if (Objects.isNull(re)) { } // List list = realExamFaultService.listAllType2InitStateByExamId(re.getExamId()); list.forEach(ref -> { Fault f = faultService.selectFaultByFaultId(ref.getFaultId()); if (faultService.isDisable(f.getFaultId())) { l.warn("故障{}-Disable", ref.getFaultId()); return; } l.info("f.toString() = " + f.toString()); Sim s = simService.selectSimBySimId(re.getSimId()); l.info("s.toString() = " + s.toString()); // check if (Objects.isNull(f)) { } clearOneFault(s, ref, f); }); } /** * Async version. * * @param re */ @Async("tp-comm") public void clearListFaultByRealExamAsync(RealExam re) { clearListFaultByRealExam(re); } public void clearAll() { // todo: simService.listAllEnable().forEach(s -> { String simType = s.getSimType(); List listF = faultService.listAllType3EnableBySimType(simType); listF.forEach(f -> { clearOneFault(s, null, f); }); }); } /** * @param s * @param reF 可以为空,表示不关联考试的。 * @param f */ public void clearOneFault(Sim s, RealExamFault reF, Fault f) { // check todo: // try { // step1 SimMsg sm1 = new SimMsg(); String sendMsg1 = buildSendMsgClearFault(s.getSimNum(), f.getBindHardwareMsg()); sm1.setSendMsg(sendMsg1); String receiveMsg1 = send(sendMsg1, s); sm1.setReceiveMsg(receiveMsg1); simReceiveService.clearOneFault(sm1, s, reF, f); // step2 if (reF != null && realExamFaultService.isState(reF.getRefId(), RealExamFault.REF_STATE_CLEARED)) { writeOneFault(s, reF, f); } } catch (SocketTimeoutException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } } public void writeOneFault(Sim s, RealExamFault ref, Fault f) { try { // 下发故障 SimMsg sm1 = new SimMsg(); String sendMsg1 = buildSendMsgWriteFault(s.getSimNum(), f.getBindHardwareMsg()); sm1.setSendMsg(sendMsg1); String receiveMsg1 = send(sendMsg1, s); sm1.setReceiveMsg(receiveMsg1); // todo: // 读取一次当前电阻代表值作为出题值。 SimMsg sm2 = new SimMsg(); String sendMsg2 = buildSendMsgReadFaultResistance(s.getSimNum(), f.getBindHardwareMsg()); sm2.setSendMsg(sendMsg2); String receiveMsg2 = send(sendMsg2, s); sm2.setReceiveMsg(receiveMsg2); simReceiveService.setFaultQuestionValue(sm2, s, ref, f); // 修改关联状态。 { RealExamFault f1 = realExamFaultService.selectRealExamFaultByRefId(ref.getRefId()); f1.setRefState(RealExamFault.REF_STATE_LOOP_READ); realExamFaultService.updateRealExamFault(f1); } } catch (IOException e) { throw new RuntimeException(e); } } public void readOneFaultResistance(Sim s, RealExamFault reF, Fault f) { try { SimMsg sm = new SimMsg(); String sendMsg = buildSendMsgReadFaultResistance(s.getSimNum(), "03"); sm.setSendMsg(sendMsg); String receiveMsg = send(sendMsg, s); sm.setReceiveMsg(receiveMsg); simReceiveService.setFaultAnswerValue(sm, s, reF, f); } catch (IOException e) { throw new RuntimeException(e); } } public void test() { try { { String sendMsg = buildSendMsgReadFaultResistance("01", "04"); send(sendMsg, null); } { String sendMsg = buildSendMsgReadFaultResistance("01", "05"); send(sendMsg, null); } { String sendMsg = buildSendMsgReadFaultResistance("01", "06"); send(sendMsg, null); } } catch (IOException e) { throw new RuntimeException(e); } } /** * 设备类型读取 * * @param simNum sim.sim_num */ public String buildSendMsgReadSimType(final String simNum) { return buildSendMsg(simNum, CMD_READ_TYPE, CMD_ID_GET_SN); } /** * 故障下发 * * @param simNum sim.sim_num * @param bindHardwareMsg fault.bind_hardware_msg */ public String buildSendMsgWriteFault(final String simNum, final String bindHardwareMsg) { return buildSendMsg(simNum, CMD_SET_FAULT, bindHardwareMsg); } /** * 状态读取 * * @param simNum sim.sim_num * @param bindHardwareMsg fault.bind_hardware_msg */ public String buildSendMsgReadFaultResistance(final String simNum, final String bindHardwareMsg) { return buildSendMsg(simNum, CMD_READ_FAULT_RESISTANCE, bindHardwareMsg); } /** * 故障清清除 * * @param simNum sim.sim_num * @param bindHardwareMsg fault.bind_hardware_msg */ public String buildSendMsgClearFault(final String simNum, final String bindHardwareMsg) { return buildSendMsg(simNum, CMD_CLEAR_FAULT, bindHardwareMsg); } public String buildSendMsg(final String simNum, final String cmd, final String cmdId) { return buildSendMsg(simNum, cmd, cmdId, CMD_DATA_PLACE_HOLDER); } public String buildSendMsg(final String simNum, final String cmd, final String cmdId, final String data) { if (StringUtils.isEmpty(simNum) || StringUtils.isEmpty(cmd) || StringUtils.isEmpty(cmdId) || StringUtils.isEmpty(data)) { throw new IllegalArgumentException("buildSendMsg isEmpty"); } if (simNum.length() != LENGTH_2) { throw new IllegalArgumentException("buildSendMsg length error"); } if (cmd.length() != LENGTH_2) { throw new IllegalArgumentException("buildSendMsg length error"); } if (cmdId.length() != LENGTH_2) { throw new IllegalArgumentException("buildSendMsg length error"); } if (data.length() != LENGTH_8) { throw new IllegalArgumentException("buildSendMsg length error"); } StringBuffer m = new StringBuffer(); m.append(PREFIX); m.append(simNum); m.append(ORN_SEND); m.append(cmd); m.append(cmdId); m.append(data); m.append(SUFFIX); final String mFinal = m.toString(); if (mFinal.length() != LENGTH_20) { throw new IllegalArgumentException("buildSendMsg length error"); } return mFinal; } /** * send hex message * * @param sendMsg * @param s 可以为空,更新最后发送/接收时间 用。 * @return */ public synchronized String send(final String sendMsg, final Sim s) throws IOException { l.info("sendMsg = " + sendMsg); String receiveMsg = null; if (cachedSocket == null) { openSocket(); } InputStream is = cachedSocket.getInputStream(); OutputStream os = cachedSocket.getOutputStream(); os.write(hexStrToByteArrs(sendMsg)); if (s != null) { simService.updateLastSentTime(s); } byte[] buffer = new byte[1024]; int length = is.read(buffer); StringBuffer sbHex = new StringBuffer(); for (int i = 0; i < length; i++) { sbHex.append(String.format("%02X", buffer[i])); } receiveMsg = sbHex.toString(); l.info("receiveMsg = {}", receiveMsg); if (!checkReceiveMsg(receiveMsg)) { // todo: l.warn("checkReceiveMsg fail receiveMsg = {}", receiveMsg); return ""; } if (s != null) { simService.updateLastReceivedTime(s); } return receiveMsg; } public void openSocket() throws IOException { if (cachedSocket == null) { cachedSocket = new Socket(IP, PORT); // setSoTimeout // cachedSocket.setSoTimeout(1000); } } /** * todo:异常精细处理 * * @throws IOException */ public void closeSocket() throws IOException { if (cachedSocket != null) { cachedSocket.close(); } cachedSocket = null; } public byte[] hexStrToByteArrs(String hexString) { // if (StringUtils.isEmpty(hexString)) { // return null; // } hexString = hexString.replaceAll(" ", ""); int len = hexString.length(); int index = 0; byte[] bytes = new byte[len / 2]; while (index < len) { String sub = hexString.substring(index, index + 2); bytes[index / 2] = (byte) Integer.parseInt(sub, 16); index += 2; } return bytes; } /** * ping * * @param ipV4 * @return * @throws IOException */ public boolean isReachable(String ipV4) throws IOException { InetAddress ia = InetAddress.getByName(ipV4); return ia.isReachable(sConfig.getGatewayReachableTimeout()); } /** * check receiveMsg * * @param receiveMsg * @return */ public boolean checkReceiveMsg(String receiveMsg) { if (StringUtils.isEmpty(receiveMsg)) { return false; } if (receiveMsg.length() != CommSendService.Const.LENGTH_20) { return false; } final String orn = StringUtils.substring(receiveMsg, 4, 6); if (!CommSendService.Const.ORN_RECEIVE.equals(orn)) { return false; } if (!StringUtils.startsWith(receiveMsg, CommSendService.Const.PREFIX)) { return false; } if (!StringUtils.endsWith(receiveMsg, CommSendService.Const.SUFFIX)) { return false; } return true; } }