/*** * 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.net; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.TooManyListenersException; import org.apache.commons.lang.SystemUtils; import org.apache.commons.lang.builder.StandardToStringStyle; import org.apache.commons.lang.builder.ToStringBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gnu.io.CommPortIdentifier; import gnu.io.NoSuchPortException; import gnu.io.PortInUseException; import gnu.io.SerialPort; import gnu.io.SerialPortEvent; import gnu.io.SerialPortEventListener; import gnu.io.UnsupportedCommOperationException; import net.wimpi.modbus.Modbus; import net.wimpi.modbus.io.ModbusASCIITransport; import net.wimpi.modbus.io.ModbusBINTransport; import net.wimpi.modbus.io.ModbusRTUTransport; import net.wimpi.modbus.io.ModbusSerialTransport; import net.wimpi.modbus.io.ModbusTransport; import net.wimpi.modbus.util.SerialParameters; /** * Class that implements a serial connection which * can be used for master and slave implementations. * * @author Dieter Wimberger * @author John Charlton * @version @version@ (@date@) */ public class SerialConnection implements SerialPortEventListener, ModbusSlaveConnection { private static final Logger logger = LoggerFactory.getLogger(SerialConnection.class); private SerialParameters m_Parameters; private ModbusSerialTransport m_Transport; private CommPortIdentifier m_PortIdentifyer; private SerialPort m_SerialPort; private boolean m_Open; private InputStream m_SerialIn; private static StandardToStringStyle toStringStyle = new StandardToStringStyle(); static { toStringStyle.setUseShortClassName(true); } /** * Creates a SerialConnection object and initializes variables passed in * as params. * * @param parameters A SerialParameters object. */ public SerialConnection(SerialParameters parameters) { m_Parameters = parameters; m_Open = false; }// constructor /** * Returns the reference to the SerialPort instance. * * @return a reference to the <tt>SerialPort</tt>. */ public SerialPort getSerialPort() { return m_SerialPort; }// getSerialPort /** * Returns the <tt>ModbusTransport</tt> instance to be * used for receiving and sending messages. * * @return a <tt>ModbusTransport</tt> instance. */ public ModbusTransport getModbusTransport() { return m_Transport; }// getModbusTransport /** * Opens the communication port. * * @throws Exception if an error occurs. */ public void open() throws Exception { if (isOpen()) { return; } // If this is Linux then first of all we need to check that // device file exists. Otherwise call to m_PortIdentifyer.open() // method will crash JVM. // It is ugly patch but it is too late... if (SystemUtils.IS_OS_LINUX) { File portDevFile = new File(m_Parameters.getPortName()); if (!portDevFile.exists()) { throw new Exception("Modbus serial device " + m_Parameters.getPortName() + " doesn't exist!"); } } // 1. obtain a CommPortIdentifier instance try { m_PortIdentifyer = CommPortIdentifier.getPortIdentifier(m_Parameters.getPortName()); } catch (NoSuchPortException e) { final String errMsg = "Could not get port identifier, maybe insufficient permissions. " + e.getMessage(); logger.error(errMsg); throw new Exception(errMsg); } logger.trace("Got Port Identifier"); // 2. open the port, wait for given timeout try { m_SerialPort = m_PortIdentifyer.open("Modbus Serial Master", 30000); } catch (PortInUseException e) { String msg = "open port failed: " + e.getMessage(); logger.error(msg); throw new Exception(msg); } logger.trace("Got Serial Port"); // 3. set the parameters try { setConnectionParameters(); } catch (Exception e) { // ensure it is closed m_SerialPort.close(); logger.error("parameter setup failed: " + e.getMessage()); throw e; } setReceiveTimeout(m_Parameters.getReceiveTimeoutMillis()); if (Modbus.SERIAL_ENCODING_ASCII.equals(m_Parameters.getEncoding())) { m_Transport = new ModbusASCIITransport(); } else if (Modbus.SERIAL_ENCODING_RTU.equals(m_Parameters.getEncoding())) { m_Transport = new ModbusRTUTransport(); } else if (Modbus.SERIAL_ENCODING_BIN.equals(m_Parameters.getEncoding())) { m_Transport = new ModbusBINTransport(); } m_Transport.setEcho(m_Parameters.isEcho()); // Open the input and output streams for the connection. If they won't // open, close the port before throwing an exception. try { m_SerialIn = m_SerialPort.getInputStream(); m_Transport.setCommPort(m_SerialPort); // m_Transport.prepareStreams(m_SerialIn, // m_SerialPort.getOutputStream()); } catch (IOException e) { m_SerialPort.close(); String msg = "Error opening i/o streams: " + e.getMessage(); logger.error(msg); throw new Exception(msg); } logger.trace("i/o Streams prepared"); // Add this object as an event listener for the serial port. try { m_SerialPort.addEventListener(this); } catch (TooManyListenersException e) { m_SerialPort.close(); final String errMsg = "too many listeners added: " + e.getMessage(); logger.error(errMsg); throw new Exception(errMsg); } // Set notifyOnBreakInterrup to allow event driven break handling. m_SerialPort.notifyOnBreakInterrupt(true); m_Open = true; }// open public void setReceiveTimeout(int ms) { // Set receive timeout to allow breaking out of polling loop during // input handling. try { m_SerialPort.enableReceiveTimeout(ms); } catch (UnsupportedCommOperationException e) { logger.error("Failed to set receive timeout: {}", e.getMessage()); } }// setReceiveTimeout public SerialParameters getParameters() { return m_Parameters; } /** * Sets the connection parameters to the setting in the parameters object. * If set fails return the parameters object to origional settings and * throw exception. * * @throws Exception if the configured parameters cannot be set properly * on the port. */ protected void setConnectionParameters() throws Exception { // Save state of parameters before trying a set. int oldBaudRate = m_SerialPort.getBaudRate(); int oldDatabits = m_SerialPort.getDataBits(); int oldStopbits = m_SerialPort.getStopBits(); int oldParity = m_SerialPort.getParity(); int oldFlowControl = m_SerialPort.getFlowControlMode(); // Set connection parameters, if set fails return parameters object // to original state. try { m_SerialPort.setSerialPortParams(m_Parameters.getBaudRate(), m_Parameters.getDatabits(), m_Parameters.getStopbits(), m_Parameters.getParity()); } catch (UnsupportedCommOperationException e) { m_Parameters.setBaudRate(oldBaudRate); m_Parameters.setDatabits(oldDatabits); m_Parameters.setStopbits(oldStopbits); m_Parameters.setParity(oldParity); final String errMsg = "Unsupported parameter"; logger.debug("{} failed to set up one of [baudRate, dataBits, stopBits, parity]: {}", errMsg, e.getMessage()); throw new Exception(errMsg); } // Set flow control. try { m_SerialPort.setFlowControlMode(m_Parameters.getFlowControlIn() | m_Parameters.getFlowControlOut()); } catch (UnsupportedCommOperationException e) { final String errMsg = "Unsupported flow control"; logger.debug("{}: {}", errMsg, e.getMessage()); throw new Exception(errMsg); } }// setConnectionParameters /** * Close the port and clean up associated elements. */ public void close() { // If port is alread closed just return. if (!m_Open) { return; } try { m_Transport.close(); } catch (IOException e) { logger.error(e.getMessage()); } try { m_SerialIn.close(); } catch (IOException e) { logger.error(e.getMessage()); } m_Open = false; }// close /** * Reports the open status of the port. * * @return true if port is open, false if port is closed. */ public boolean isOpen() { return m_Open; }// isOpen @Override public void serialEvent(SerialPortEvent e) { // Determine type of event. switch (e.getEventType()) { // This event is ignored, the application reads directly from // the serial input stream case SerialPortEvent.DATA_AVAILABLE: /* * try { * int amount = m_SerialIn.available(); * while (amount > 0) { * try { * byte[] buffer = new byte[amount]; * if ((amount = m_SerialIn.read(buffer, 0, amount)) > 0) { * m_Pipe.write (buffer, 0, amount); * } * amount = m_SerialIn.available(); * } catch (IOException ex) { * logger.error("Error: Comm event read: {}", ex); * ex.printStackTrace(); * return; * } * } * } catch (Exception ex) { * //handle * * } */ break; case SerialPortEvent.BI: logger.debug("Serial port break detected"); break; default: logger.debug("Serial port event: {}", e.getEventType()); } }// serialEvent @Override public boolean connect() throws Exception { open(); return isOpen(); } @Override public void resetConnection() { close(); } @Override public boolean isConnected() { return isOpen(); } @Override public String toString() { return new ToStringBuilder(this, toStringStyle).append("portName", m_Parameters.getPortName()) .append("port", m_SerialPort).toString(); } }// class SerialConnection