//License
/***
* Java Modbus Library (jamod)
* Copyright (c) 2002-2004, jamod development team
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS
* IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
***/
package net.wimpi.modbus.io;
import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.ModbusIOException;
import net.wimpi.modbus.msg.ModbusMessage;
import net.wimpi.modbus.msg.ModbusRequest;
import net.wimpi.modbus.msg.ModbusResponse;
import net.wimpi.modbus.util.ModbusUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Class that implements the ModbusRTU transport
* flavor.
*
* @author John Charlton
* @author Dieter Wimberger
*
* @version 1.2rc1 (09/11/2004)
*/
public class ModbusRTUTransport
extends ModbusSerialTransport {
private InputStream m_InputStream; //wrap into filter input
private OutputStream m_OutputStream; //wrap into filter output
private byte[] m_InBuffer;
private BytesInputStream m_ByteIn; //to read message from
private BytesOutputStream m_ByteInOut; //to buffer message to
private BytesOutputStream m_ByteOut; //write frames
private byte[] lastRequest = null;
public void writeMessage(ModbusMessage msg) throws ModbusIOException {
try {
int len;
synchronized (m_ByteOut) {
// first clear any input from the receive buffer to prepare
// for the reply since RTU doesn't have message delimiters
clearInput();
//write message to byte out
m_ByteOut.reset();
msg.setHeadless();
msg.writeTo(m_ByteOut);
len = m_ByteOut.size();
int[] crc = ModbusUtil.calculateCRC(m_ByteOut.getBuffer(), 0, len);
m_ByteOut.writeByte(crc[0]);
m_ByteOut.writeByte(crc[1]);
//write message
len = m_ByteOut.size();
byte buf[] = m_ByteOut.getBuffer();
m_OutputStream.write(buf, 0, len); //PDU + CRC
m_OutputStream.flush();
if(Modbus.debug) System.out.println("Sent: " + ModbusUtil.toHex(buf, 0, len));
// clears out the echoed message
// for RS485
if (m_Echo) {
readEcho(len);
}
lastRequest = new byte[len];
System.arraycopy(buf, 0, lastRequest, 0, len);
}
} catch (Exception ex) {
throw new ModbusIOException("I/O failed to write");
}
}//writeMessage
//This is required for the slave that is not supported
public ModbusRequest readRequest() throws ModbusIOException {
throw new RuntimeException("Operation not supported.");
} //readRequest
/**
* Clear the input if characters are found in the input stream.
*
* @throws ModbusIOException
*/
public void clearInput() throws IOException {
if (m_InputStream.available() > 0) {
int len = m_InputStream.available();
byte buf[] = new byte[len];
m_InputStream.read(buf, 0, len);
if(Modbus.debug) System.out.println("Clear input: " +
ModbusUtil.toHex(buf, 0, len));
}
}//cleanInput
public ModbusResponse readResponse()
throws ModbusIOException {
boolean done = false;
ModbusResponse response = null;
int dlength = 0;
try {
do {
//1. read to function code, create request and read function specific bytes
synchronized (m_ByteIn) {
int uid = m_InputStream.read();
if (uid != -1) {
int fc = m_InputStream.read();
m_ByteInOut.reset();
m_ByteInOut.writeByte(uid);
m_ByteInOut.writeByte(fc);
//create response to acquire length of message
response = ModbusResponse.createModbusResponse(fc);
response.setHeadless();
// With Modbus RTU, there is no end frame. Either we assume
// the message is complete as is or we must do function
// specific processing to know the correct length. To avoid
// moving frame timing to the serial input functions, we set the
// timeout and to message specific parsing to read a response.
getResponse(fc, m_ByteInOut);
dlength = m_ByteInOut.size() - 2; // less the crc
if (Modbus.debug) System.out.println("Response: " +
ModbusUtil.toHex(m_ByteInOut.getBuffer(), 0, dlength + 2));
m_ByteIn.reset(m_InBuffer, dlength);
//check CRC
int[] crc = ModbusUtil.calculateCRC(m_InBuffer, 0, dlength); //does not include CRC
if (ModbusUtil.unsignedByteToInt(m_InBuffer[dlength]) != crc[0]
&& ModbusUtil.unsignedByteToInt(m_InBuffer[dlength + 1]) != crc[1]) {
throw new IOException("CRC Error in received frame: " + dlength + " bytes: " + ModbusUtil.toHex(m_ByteIn.getBuffer(), 0, dlength));
}
} else {
throw new IOException("Error reading response");
}
//read response
m_ByteIn.reset(m_InBuffer, dlength);
if (response != null) {
response.readFrom(m_ByteIn);
}
done = true;
}//synchronized
} while (!done);
return response;
} catch (Exception ex) {
System.err.println("Last request: " + ModbusUtil.toHex(lastRequest));
System.err.println(ex.getMessage());
throw new ModbusIOException("I/O exception - failed to read");
}
}//readResponse
/**
* Prepares the input and output streams of this
* <tt>ModbusRTUTransport</tt> instance.
*
* @param in the input stream to be read from.
* @param out the output stream to write to.
* @throws IOException if an I\O error occurs.
*/
public void prepareStreams(InputStream in, OutputStream out)
throws IOException {
m_InputStream = in; //new RTUInputStream(in);
m_OutputStream = out;
m_ByteOut = new BytesOutputStream(Modbus.MAX_MESSAGE_LENGTH);
m_InBuffer = new byte[Modbus.MAX_MESSAGE_LENGTH];
m_ByteIn = new BytesInputStream(m_InBuffer);
m_ByteInOut = new BytesOutputStream(m_InBuffer);
} //prepareStreams
public void close() throws IOException {
m_InputStream.close();
m_OutputStream.close();
}//close
private void getResponse(int fn, BytesOutputStream out)
throws IOException {
int bc = -1, bc2 = -1, bcw = -1;
int inpBytes = 0;
byte inpBuf[] = new byte[256];
try {
switch (fn) {
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x0C:
case 0x11: // report slave ID version and run/stop state
case 0x14: // read log entry (60000 memory reference)
case 0x15: // write log entry (60000 memory reference)
case 0x17:
// read the byte count;
bc = m_InputStream.read();
out.write(bc);
// now get the specified number of bytes and the 2 CRC bytes
setReceiveThreshold(bc+2);
inpBytes = m_InputStream.read(inpBuf, 0, bc+2);
out.write(inpBuf, 0, inpBytes);
m_CommPort.disableReceiveThreshold();
if (inpBytes != bc+2) {
System.out.println("Error: looking for " + (bc+2) +
" bytes, received " + inpBytes);
}
break;
case 0x05:
case 0x06:
case 0x0B:
case 0x0F:
case 0x10:
// read status: only the CRC remains after address and function code
setReceiveThreshold(6);
inpBytes = m_InputStream.read(inpBuf, 0, 6);
out.write(inpBuf, 0, inpBytes);
m_CommPort.disableReceiveThreshold();
break;
case 0x07:
case 0x08:
// read status: only the CRC remains after address and function code
setReceiveThreshold(3);
inpBytes = m_InputStream.read(inpBuf, 0, 3);
out.write(inpBuf, 0, inpBytes);
m_CommPort.disableReceiveThreshold();
break;
case 0x16:
// eight bytes in addition to the address and function codes
setReceiveThreshold(8);
inpBytes = m_InputStream.read(inpBuf, 0, 8);
out.write(inpBuf, 0, inpBytes);
m_CommPort.disableReceiveThreshold();
break;
case 0x18:
// read the byte count word
bc = m_InputStream.read();
out.write(bc);
bc2 = m_InputStream.read();
out.write(bc2);
bcw = ModbusUtil.makeWord(bc, bc2);
// now get the specified number of bytes and the 2 CRC bytes
setReceiveThreshold(bcw+2);
inpBytes = m_InputStream.read(inpBuf, 0, bcw + 2);
out.write(inpBuf, 0, inpBytes);
m_CommPort.disableReceiveThreshold();
break;
}
} catch (IOException e) {
m_CommPort.disableReceiveThreshold();
throw new IOException("getResponse serial port exception");
}
}//getResponse
} //ModbusRTUTransport