public byte[] msgTransaction(byte[] msg) throws ModbusProtocolException { byte[] cmd = null; if (m_txMode == ModbusTransmissionMode.RTU_MODE) { cmd = new byte[msg.length + 2]; for (int i = 0; i < msg.length; i++) cmd[i] = msg[i]; // Add crc calculation to end of message int crc = Crc16.getCrc16(msg, msg.length, 0x0ffff); cmd[msg.length] = (byte) crc; cmd[msg.length + 1] = (byte) (crc >> 8); } else throw new ModbusProtocolException( ModbusProtocolErrorCode.METHOD_NOT_SUPPORTED, "Only RTU over TCP/IP supported"); // Check connection status and connect connect(); if (!connected) throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Cannot transact on closed socket"); // Send the message try { // flush input while (inputStream.available() > 0) inputStream.read(); // send all data outputStream.write(cmd, 0, cmd.length); outputStream.flush(); } catch (IOException e) { // Assume this means the socket is closed...make sure it is s_logger.error("Socket disconnect in send: " + e); disconnect(); throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Send failure: " + e.getMessage()); } // wait for and process response byte[] response = new byte[262]; // response buffer int respIndex = 0; int minimumLength = 5; // default minimum message length while (true) { while (respIndex < minimumLength) { try { socket.setSoTimeout(m_respTout); int resp = inputStream.read(response, respIndex, minimumLength - respIndex); if (resp > 0) { respIndex += resp; } else { s_logger.error("Socket disconnect in recv"); disconnect(); throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Recv failure"); } } catch (SocketTimeoutException e) { String failMsg = "Recv timeout"; s_logger.warn(failMsg); throw new ModbusProtocolException(ModbusProtocolErrorCode.TRANSACTION_FAILURE, failMsg); } catch (IOException e) { s_logger.error("Socket disconnect in recv: " + e); disconnect(); throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Recv failure"); } } // Check first for an Exception response if ((response[1] & 0x80) == 0x80) { if (Crc16.getCrc16(response, 5, 0xffff) == 0) throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Resp exception = " + Byte.toString(response[2])); } else { // then check for a valid message switch (response[1]) { case ModbusFunctionCodes.FORCE_SINGLE_COIL: case ModbusFunctionCodes.PRESET_SINGLE_REG: case ModbusFunctionCodes.FORCE_MULTIPLE_COILS: case ModbusFunctionCodes.PRESET_MULTIPLE_REGS: if (respIndex < 8) // wait for more data minimumLength = 8; else if (Crc16.getCrc16(response, 8, 0xffff) == 0) { byte[] ret = new byte[8]; for (int i = 0; i < 8; i++) ret[i] = response[i]; return ret; } break; case ModbusFunctionCodes.READ_COIL_STATUS: case ModbusFunctionCodes.READ_INPUT_STATUS: case ModbusFunctionCodes.READ_INPUT_REGS: case ModbusFunctionCodes.READ_HOLDING_REGS: int byteCnt = (response[2] & 0xff) + 5; if (respIndex < byteCnt) // wait for more data minimumLength = byteCnt; else if (Crc16.getCrc16(response, byteCnt, 0xffff) == 0) { byte[] ret = new byte[byteCnt]; for (int i = 0; i < byteCnt; i++) ret[i] = response[i]; return ret; } } } /* * if required length then must have failed, drop first byte and * try again */ if (respIndex >= minimumLength) throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Error in recv"); } }
/** * msgTransaction must be called with a byte array having two extra bytes for the CRC. It will * return a byte array of the response to the message. Validation will include checking the CRC * and verifying the command matches. */ public byte[] msgTransaction(byte[] msg) throws ModbusProtocolException { byte[] cmd = null; if (m_txMode == ModbusTransmissionMode.RTU_MODE) { cmd = new byte[msg.length + 2]; for (int i = 0; i < msg.length; i++) cmd[i] = msg[i]; // Add crc calculation to end of message int crc = Crc16.getCrc16(msg, msg.length, 0x0ffff); cmd[msg.length] = (byte) crc; cmd[msg.length + 1] = (byte) (crc >> 8); } else if (m_txMode == ModbusTransmissionMode.ASCII_MODE) { cmd = convertCommandToAscii(msg); } // Send the message try { synchronized (out) { synchronized (in) { // flush input if (m_serial485) switchRX(); while (in.available() > 0) in.read(); // send all data if (m_serial485) switchTX(); out.write(cmd, 0, cmd.length); out.flush(); // outputStream.waitAllSent(respTout); // wait for and process response if (m_serial485) switchRX(); byte[] response = new byte[262]; // response buffer int respIndex = 0; int minimumLength = 5; // default minimum message length if (m_txMode == ModbusTransmissionMode.ASCII_MODE) minimumLength = 11; int timeOut = m_respTout; for (int maxLoop = 0; maxLoop < 1000; maxLoop++) { boolean endFrame = false; // while (respIndex < minimumLength) { while (!endFrame) { long start = System.currentTimeMillis(); while (in.available() == 0) { try { Thread.sleep(5); // avoid a high cpu load } catch (InterruptedException e) { throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Thread interrupted"); } long elapsed = System.currentTimeMillis() - start; if (elapsed > timeOut) { String failMsg = "Recv timeout"; s_logger.warn( failMsg + " : " + elapsed + " minimumLength=" + minimumLength + " respIndex=" + respIndex); throw new ModbusProtocolException( ModbusProtocolErrorCode.RESPONSE_TIMEOUT, failMsg); } } // address byte must match first if (respIndex == 0) { if (m_txMode == ModbusTransmissionMode.ASCII_MODE) { if ((response[0] = (byte) in.read()) == ':') respIndex++; } else { if ((response[0] = (byte) in.read()) == msg[0]) respIndex++; } } else response[respIndex++] = (byte) in.read(); if (m_txMode == ModbusTransmissionMode.RTU_MODE) { timeOut = 100; // move to character timeout if (respIndex >= minimumLength) endFrame = true; } else { if ((response[respIndex - 1] == 10) && (response[respIndex - 2] == 13)) endFrame = true; } } // if ASCII mode convert response if (m_txMode == ModbusTransmissionMode.ASCII_MODE) { byte lrcRec = asciiLrcCalc(response, respIndex); response = convertAsciiResponseToBin(response, respIndex); byte lrcCalc = (byte) binLrcCalc(response); if (lrcRec != lrcCalc) throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Bad LRC"); } // Check first for an Exception response if ((response[1] & 0x80) == 0x80) { if ((m_txMode == ModbusTransmissionMode.ASCII_MODE) || (Crc16.getCrc16(response, 5, 0xffff) == 0)) throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Exception response = " + Byte.toString(response[2])); } else { // then check for a valid message switch (response[1]) { case ModbusFunctionCodes.FORCE_SINGLE_COIL: case ModbusFunctionCodes.PRESET_SINGLE_REG: case ModbusFunctionCodes.FORCE_MULTIPLE_COILS: case ModbusFunctionCodes.PRESET_MULTIPLE_REGS: if (respIndex < 8) // wait for more data minimumLength = 8; else if ((m_txMode == ModbusTransmissionMode.ASCII_MODE) || (Crc16.getCrc16(response, 8, 0xffff) == 0)) { byte[] ret = new byte[6]; for (int i = 0; i < 6; i++) ret[i] = response[i]; return ret; } break; case ModbusFunctionCodes.READ_COIL_STATUS: case ModbusFunctionCodes.READ_INPUT_STATUS: case ModbusFunctionCodes.READ_INPUT_REGS: case ModbusFunctionCodes.READ_HOLDING_REGS: int byteCnt; if ((m_txMode == ModbusTransmissionMode.ASCII_MODE)) byteCnt = (response[2] & 0xff) + 3; else byteCnt = (response[2] & 0xff) + 5; if (respIndex < byteCnt) // wait for more data minimumLength = byteCnt; else if ((m_txMode == ModbusTransmissionMode.ASCII_MODE) || (Crc16.getCrc16(response, byteCnt, 0xffff) == 0)) { byte[] ret = new byte[byteCnt]; for (int i = 0; i < byteCnt; i++) ret[i] = response[i]; return ret; } } } /* * if required length then must have failed, drop * first byte and try again */ if (respIndex >= minimumLength) { respIndex--; for (int i = 0; i < respIndex; i++) response[i] = response[i + 1]; minimumLength = 5; // reset minimum length } } } } } catch (IOException e) { // e.printStackTrace(); throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, e.getMessage()); } throw new ModbusProtocolException( ModbusProtocolErrorCode.TRANSACTION_FAILURE, "Too much activity on recv line"); }