/*** * Copyright 2002-2010 jamod development team * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***/ package net.wimpi.modbus.io; import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.wimpi.modbus.Modbus; import net.wimpi.modbus.ModbusCoupler; 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; /** * Class that implements the Modbus/BIN transport * flavor. * * @author Dieter Wimberger * @version @version@ (@date@) */ public class ModbusBINTransport extends ModbusSerialTransport { private static final Logger logger = LoggerFactory.getLogger(ModbusBINTransport.class); private DataInputStream m_InputStream; // used to read from private ASCIIOutputStream m_OutputStream; // used to write to 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 /** * Constructs a new <tt>MobusBINTransport</tt> instance. */ public ModbusBINTransport() { }// constructor @Override public void close() throws IOException { IOUtils.closeQuietly(m_InputStream); IOUtils.closeQuietly(m_OutputStream); super.close(); }// close @Override public void writeMessage(ModbusMessage msg) throws ModbusIOException { try { int len; synchronized (m_ByteOut) { // write message to byte out msg.setHeadless(); msg.writeTo(m_ByteOut); byte[] buf = m_ByteOut.getBuffer(); len = m_ByteOut.size(); // write message m_OutputStream.write(FRAME_START); // FRAMESTART m_OutputStream.write(buf, 0, len); // PDU int[] crc = ModbusUtil.calculateCRC(buf, 0, len); // CRC m_OutputStream.write(crc[0]); m_OutputStream.write(crc[1]); m_OutputStream.write(FRAME_END); // FRAMEEND m_OutputStream.flush(); m_ByteOut.reset(); } // clears out the echoed message // for RS485 if (m_Echo) { // read back the echoed message readEcho(len + 4); } } catch (Exception ex) { throw new ModbusIOException("I/O failed to write"); } }// writeMessage @Override public ModbusRequest readRequest() throws ModbusIOException { boolean done = false; ModbusRequest request = null; int in = -1; try { do { // 1. Skip to FRAME_START while ((in = m_InputStream.read()) != FRAME_START) { ; } // 2. Read to FRAME_END synchronized (m_InBuffer) { m_ByteInOut.reset(); while ((in = m_InputStream.read()) != FRAME_END) { m_ByteInOut.writeByte(in); } // check CRC int[] crc = ModbusUtil.calculateCRC(m_InBuffer, 0, m_ByteInOut.size() - 2); if (!(m_InBuffer[m_ByteInOut.size() - 2] == crc[0] // low byte first && m_InBuffer[m_ByteInOut.size() - 1] == crc[1] // hibyte )) { continue; } m_ByteIn.reset(m_InBuffer, m_ByteInOut.size()); in = m_ByteIn.readUnsignedByte(); // check unit identifier if (in != ModbusCoupler.getReference().getUnitID()) { continue; } in = m_ByteIn.readUnsignedByte(); // create request request = ModbusRequest.createModbusRequest(in); request.setHeadless(); // read message m_ByteIn.reset(m_InBuffer, m_ByteInOut.size()); request.readFrom(m_ByteIn); } done = true; } while (!done); return request; } catch (Exception ex) { final String errMsg = "failed to read"; logger.debug("{}: {}", errMsg, ex.getMessage()); throw new ModbusIOException("I/O exception - " + errMsg); } }// readRequest @Override public ModbusResponse readResponse() throws ModbusIOException { boolean done = false; ModbusResponse response = null; int in = -1; setReceiveThreshold(1); try { do { // 1. Skip to FRAME_START while ((in = m_InputStream.read()) != FRAME_START) { // FIXME: handle EOF, similar to ASCIITransport } // 2. Read to FRAME_END synchronized (m_InBuffer) { m_ByteInOut.reset(); while ((in = m_InputStream.read()) != FRAME_END) { m_ByteInOut.writeByte(in); } // check CRC int[] crc = ModbusUtil.calculateCRC(m_InBuffer, 0, m_ByteInOut.size() - 2); if (!(m_InBuffer[m_ByteInOut.size() - 2] == crc[0] // low byte first && m_InBuffer[m_ByteInOut.size() - 1] == crc[1] // hibyte )) { continue; } m_ByteIn.reset(m_InBuffer, m_ByteInOut.size()); in = m_ByteIn.readUnsignedByte(); // check unit identifier if (in != ModbusCoupler.getReference().getUnitID()) { continue; } m_ByteIn.reset(m_InBuffer, m_ByteInOut.size()); in = m_ByteIn.readUnsignedByte(); // check unit identifier if (in != ModbusCoupler.getReference().getUnitID()) { continue; } in = m_ByteIn.readUnsignedByte(); // create request response = ModbusResponse.createModbusResponse(in); response.setHeadless(); // read message m_ByteIn.reset(m_InBuffer, m_ByteInOut.size()); response.readFrom(m_ByteIn); } done = true; } while (!done); return response; } catch (Exception ex) { final String errMsg = "failed to read"; logger.debug("{}: {}", errMsg, ex.getMessage()); throw new ModbusIOException("I/O exception - " + errMsg); } finally { m_CommPort.disableReceiveThreshold(); } }// readResponse /** * Prepares the input and output streams of this * <tt>ModbusASCIITransport</tt> instance. * The raw input stream will be wrapped into a * filtered <tt>DataInputStream</tt>. * * @param in the input stream to be used for reading. * @param out the output stream to be used for writing. * @throws java.io.IOException if an I\O related error occurs. */ @Override public void prepareStreams(InputStream in, OutputStream out) throws IOException { m_InputStream = new DataInputStream(new ASCIIInputStream(in)); m_OutputStream = new ASCIIOutputStream(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 /** * Defines a virtual number for the FRAME START token (COLON). */ public static final int FRAME_START = 1000; /** * Defines a virtual number for the FRAME_END token (CR LF). */ public static final int FRAME_END = 2000; /** * Defines the frame start token <tt>{</tt>. */ public static final int FRAME_START_TOKEN = 123; /** * Defines the frame end token <tt>}</tt>. */ public static final int FRAME_END_TOKEN = 125; }// class ModbusASCIITransport