/*
* Copyright 2011-16 Fraunhofer ISE
*
* This file is part of OpenMUC.
* For more information visit http://www.openmuc.org
*
* OpenMUC is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenMUC is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenMUC. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openmuc.framework.driver.modbus.rtu;
import java.util.Enumeration;
import java.util.List;
import org.openmuc.framework.config.ArgumentSyntaxException;
import org.openmuc.framework.config.ChannelScanInfo;
import org.openmuc.framework.config.ScanException;
import org.openmuc.framework.data.Flag;
import org.openmuc.framework.data.Record;
import org.openmuc.framework.data.Value;
import org.openmuc.framework.driver.modbus.ModbusChannel;
import org.openmuc.framework.driver.modbus.ModbusChannel.EAccess;
import org.openmuc.framework.driver.modbus.ModbusConnection;
import org.openmuc.framework.driver.spi.ChannelRecordContainer;
import org.openmuc.framework.driver.spi.ChannelValueContainer;
import org.openmuc.framework.driver.spi.ConnectionException;
import org.openmuc.framework.driver.spi.RecordsReceivedListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.ModbusException;
import net.wimpi.modbus.io.ModbusSerialTransaction;
import net.wimpi.modbus.net.SerialConnection;
import net.wimpi.modbus.util.SerialParameters;
/**
*
* TODO
*
*/
public class ModbusRTUConnection extends ModbusConnection {
private final static Logger logger = LoggerFactory.getLogger(ModbusRTUConnection.class);
private static final int ENCODING = 1;
private static final int BAUDRATE = 2;
private static final int DATABITS = 3;
private static final int PARITY = 4;
private static final int STOPBITS = 5;
private static final int ECHO = 6;
private static final int FLOWCONTROL_IN = 7;
private static final int FLOWCONTEOL_OUT = 8;
private static final String SERIAL_ENCODING_RTU = "SERIAL_ENCODING_RTU";
private static final String ECHO_TRUE = "ECHO_TRUE";
private static final String ECHO_FALSE = "ECHO_FALSE";
private final SerialConnection connection;
private ModbusSerialTransaction transaction;
public ModbusRTUConnection(String deviceAddress, String[] settings, int timeout)
throws ModbusConfigurationException {
super();
SerialParameters params = setParameters(deviceAddress, settings, timeout);
connection = new SerialConnection(params);
try {
connect();
transaction = new ModbusSerialTransaction(connection);
transaction.setSerialConnection(connection);
setTransaction(transaction);
} catch (Exception e) {
e.printStackTrace();
throw new ModbusConfigurationException("Wrong Modbus RTU configuration. Check configuration file");
}
logger.info("Modbus Device: " + deviceAddress + " connected");
}
@Override
public void connect() throws Exception {
if (!connection.isOpen()) {
connection.open();
}
}
@Override
public void disconnect() {
if (connection.isOpen()) {
connection.close();
}
}
public void setReceiveTimeout(int timeout) {
connection.setReceiveTimeout(timeout);
}
private SerialParameters setParameters(String address, String[] settings, int timeout)
throws ModbusConfigurationException {
SerialParameters params = new SerialParameters();
checkIfAddressIsAvailbale(address);
params.setPortName(address);
if (settings.length == 9) {
setEncoding(params, settings[ENCODING]);
setBaudrate(params, settings[BAUDRATE]);
setDatabits(params, settings[DATABITS]);
setParity(params, settings[PARITY]);
setStopbits(params, settings[STOPBITS]);
setEcho(params, settings[ECHO]);
setFlowControlIn(params, settings[FLOWCONTROL_IN]);
setFlowControlOut(params, settings[FLOWCONTEOL_OUT]);
}
else {
throw new ModbusConfigurationException("Settings parameter missing. Specify all settings parameter");
}
return params;
}
/**
* Checks if the gnu.io.rxtx is able to find the specified address e.g. /dev/ttyUSB0
*
* @param address
* @throws ModbusConfigurationException
*/
private void checkIfAddressIsAvailbale(String address) throws ModbusConfigurationException {
Enumeration ports = CommPortIdentifier.getPortIdentifiers();
boolean result = false;
while (ports.hasMoreElements()) {
CommPortIdentifier cpi = (CommPortIdentifier) ports.nextElement();
if (cpi.getName().equalsIgnoreCase(address)) {
result = true;
break;
}
}
if (!result) {
String availablePorts = getAvailablePorts();
throw new ModbusConfigurationException("gnu.io.rxtx is unable to detect address: " + address
+ ". Available addresses are: '" + availablePorts + "'");
}
}
private String getAvailablePorts() {
String availablePorts = "";
Enumeration ports = CommPortIdentifier.getPortIdentifiers();
while (ports.hasMoreElements()) {
CommPortIdentifier cpi = (CommPortIdentifier) ports.nextElement();
availablePorts += cpi.getName() + "; ";
}
return availablePorts;
}
private void setFlowControlIn(SerialParameters params, String flowControlIn) throws ModbusConfigurationException {
if (flowControlIn.equalsIgnoreCase("FLOWCONTROL_NONE")) {
params.setFlowControlIn(SerialPort.FLOWCONTROL_NONE);
}
else if (flowControlIn.equalsIgnoreCase("FLOWCONTROL_RTSCTS_IN")) {
params.setFlowControlIn(SerialPort.FLOWCONTROL_RTSCTS_IN);
}
else if (flowControlIn.equalsIgnoreCase("FLOWCONTROL_XONXOFF_IN")) {
params.setFlowControlIn(SerialPort.FLOWCONTROL_XONXOFF_IN);
}
else {
throw new ModbusConfigurationException("Unknown flow control in setting. Check configuration file");
}
}
private void setFlowControlOut(SerialParameters params, String flowControlOut) throws ModbusConfigurationException {
if (flowControlOut.equalsIgnoreCase("FLOWCONTROL_NONE")) {
params.setFlowControlOut(SerialPort.FLOWCONTROL_NONE);
}
else if (flowControlOut.equalsIgnoreCase("FLOWCONTROL_RTSCTS_OUT")) {
params.setFlowControlOut(SerialPort.FLOWCONTROL_RTSCTS_OUT);
}
else if (flowControlOut.equalsIgnoreCase("FLOWCONTROL_XONXOFF_OUT")) {
params.setFlowControlOut(SerialPort.FLOWCONTROL_XONXOFF_OUT);
}
else {
throw new ModbusConfigurationException("Unknown flow control out setting. Check configuration file");
}
}
private void setEcho(SerialParameters params, String echo) throws ModbusConfigurationException {
if (echo.equalsIgnoreCase(ECHO_TRUE)) {
params.setEcho(true);
}
else if (echo.equalsIgnoreCase(ECHO_FALSE)) {
params.setEcho(false);
}
else {
throw new ModbusConfigurationException("Unknown echo setting. Check configuration file");
}
}
private void setStopbits(SerialParameters params, String stopbits) throws ModbusConfigurationException {
if (stopbits.equalsIgnoreCase("STOPBITS_1")) {
params.setStopbits(SerialPort.STOPBITS_1);
}
else if (stopbits.equalsIgnoreCase("STOPBITS_1_5")) {
params.setStopbits(SerialPort.STOPBITS_1_5);
}
else if (stopbits.equalsIgnoreCase("STOPBITS_2")) {
params.setStopbits(SerialPort.STOPBITS_2);
}
else {
throw new ModbusConfigurationException("Unknown stobit setting. Check configuration file");
}
}
private void setParity(SerialParameters params, String parity) throws ModbusConfigurationException {
if (parity.equalsIgnoreCase("PARITY_EVEN")) {
params.setParity(SerialPort.PARITY_EVEN);
}
else if (parity.equalsIgnoreCase("PARITY_MARK")) {
params.setParity(SerialPort.PARITY_MARK);
}
else if (parity.equalsIgnoreCase("PARITY_NONE")) {
params.setParity(SerialPort.PARITY_NONE);
}
else if (parity.equalsIgnoreCase("PARITY_ODD")) {
params.setParity(SerialPort.PARITY_ODD);
}
else if (parity.equalsIgnoreCase("PARITY_SPACE")) {
params.setParity(SerialPort.PARITY_SPACE);
}
else {
throw new ModbusConfigurationException("Unknown parity setting. Check configuration file");
}
}
private void setDatabits(SerialParameters params, String databits) throws ModbusConfigurationException {
if (databits.equalsIgnoreCase("DATABITS_5")) {
params.setDatabits(SerialPort.DATABITS_5);
}
else if (databits.equalsIgnoreCase("DATABITS_6")) {
params.setDatabits(SerialPort.DATABITS_6);
}
else if (databits.equalsIgnoreCase("DATABITS_7")) {
params.setDatabits(SerialPort.DATABITS_7);
}
else if (databits.equalsIgnoreCase("DATABITS_8")) {
params.setDatabits(SerialPort.DATABITS_8);
}
else {
throw new ModbusConfigurationException("Unknown databit setting. Check configuration file");
}
}
private void setBaudrate(SerialParameters params, String baudrate) {
params.setBaudRate(baudrate);
}
private void setEncoding(SerialParameters params, String encoding) throws ModbusConfigurationException {
if (encoding.equalsIgnoreCase(SERIAL_ENCODING_RTU)) {
params.setEncoding(Modbus.SERIAL_ENCODING_RTU);
}
else {
throw new ModbusConfigurationException("Unknown encoding setting. Check configuration file");
}
}
@Override
public Object read(List<ChannelRecordContainer> containers, Object containerListHandle, String samplingGroup)
throws UnsupportedOperationException, ConnectionException {
for (ChannelRecordContainer container : containers) {
long receiveTime = System.currentTimeMillis();
ModbusChannel channel = getModbusChannel(container.getChannelAddress(), EAccess.READ);
Value value;
try {
value = readChannel(channel);
container.setRecord(new Record(value, receiveTime));
} catch (ModbusException e) {
e.printStackTrace();
container.setRecord(new Record(Flag.DRIVER_ERROR_CHANNEL_NOT_ACCESSIBLE));
} catch (Exception e) {
// catch all possible exceptions and provide info about the channel
logger.error("Unable to read channel: " + container.getChannelAddress(), e);
container.setRecord(new Record(Flag.UNKNOWN_ERROR));
}
}
// logger.debug("### readChannels duration in ms = " + ((new Date().getTime()) - startTime));
return null;
}
@Override
public Object write(List<ChannelValueContainer> containers, Object containerListHandle)
throws UnsupportedOperationException, ConnectionException {
for (ChannelValueContainer container : containers) {
ModbusChannel modbusChannel = getModbusChannel(container.getChannelAddress(), EAccess.WRITE);
if (modbusChannel != null) {
try {
writeChannel(modbusChannel, container.getValue());
container.setFlag(Flag.VALID);
} catch (ModbusException modbusException) {
container.setFlag(Flag.UNKNOWN_ERROR);
modbusException.printStackTrace();
throw new ConnectionException(
"Unable to write data on channel address: " + container.getChannelAddress());
} catch (Exception e) {
container.setFlag(Flag.UNKNOWN_ERROR);
e.printStackTrace();
logger.error("Unable to write data on channel address: " + container.getChannelAddress());
}
}
else {
// TODO
container.setFlag(Flag.UNKNOWN_ERROR);
logger.error("Unable to write data on channel address: " + container.getChannelAddress()
+ "modbusChannel = null");
}
}
return null;
}
@Override
public void startListening(List<ChannelRecordContainer> containers, RecordsReceivedListener listener)
throws UnsupportedOperationException, ConnectionException {
throw new UnsupportedOperationException();
}
@Override
public List<ChannelScanInfo> scanForChannels(String settings)
throws UnsupportedOperationException, ArgumentSyntaxException, ScanException, ConnectionException {
throw new UnsupportedOperationException();
}
}