/*
* 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.rtutcp;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
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.ModbusChannelGroup;
import org.openmuc.framework.driver.modbus.ModbusConnection;
import org.openmuc.framework.driver.modbus.rtutcp.bonino.ModbusRTUTCPTransaction;
import org.openmuc.framework.driver.modbus.rtutcp.bonino.RTUTCPMasterConnection;
import org.openmuc.framework.driver.modbus.tcp.ModbusTCPDeviceAddress;
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 net.wimpi.modbus.ModbusException;
import net.wimpi.modbus.ModbusIOException;
/**
* TODO
*/
public class ModbusRTUTCPConnection extends ModbusConnection {
private final static Logger logger = LoggerFactory.getLogger(ModbusRTUTCPConnection.class);
private RTUTCPMasterConnection connection;
private ModbusRTUTCPTransaction transaction;
public ModbusRTUTCPConnection(String deviceAddress, int timeoutInSeconds) {
super();
ModbusTCPDeviceAddress address = new ModbusTCPDeviceAddress(deviceAddress);
try {
connection = new RTUTCPMasterConnection(InetAddress.getByName(address.getIp()), address.getPort());
connect();
} catch (UnknownHostException e) {
throw new RuntimeException(e.getMessage());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
logger.info("Modbus Device: " + deviceAddress + " connected");
}
@Override
public void connect() throws Exception {
if (connection != null && !connection.isConnected()) {
connection.connect();
transaction = new ModbusRTUTCPTransaction(connection);
setTransaction(transaction);
if (!connection.isConnected()) {
throw new Exception("unable to connect");
}
}
}
@Override
public void disconnect() {
logger.info("Disconnect Modbus TCP device");
if (connection != null && connection.isConnected()) {
connection.close();
transaction = null;
}
}
@Override
public Object read(List<ChannelRecordContainer> containers, Object containerListHandle, String samplingGroup)
throws UnsupportedOperationException, ConnectionException {
// reads channels one by one
if (samplingGroup.isEmpty()) {
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 (ModbusIOException e) {
logger.error("Unable to read channel: " + container.getChannelAddress(), e);
disconnect();
throw new ConnectionException("Try to reconnect to solve ModbusIOException");
} catch (ModbusException e) {
logger.error("Unable to read channel: " + container.getChannelAddress(), e);
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));
}
}
}
// reads whole samplingGroup at once
else {
// TODO test channel group
logger.warn("Reading samplingGroup is not tested yet!");
readChannelGroupHighLevel(containers, containerListHandle, samplingGroup);
}
return null;
}
private Object readChannelGroupHighLevel(List<ChannelRecordContainer> containers, Object containerListHandle,
String samplingGroup) {
// NOTE: containerListHandle is null if something changed in configuration!!!
ModbusChannelGroup channelGroup = null;
// use existing channelGroup
if (containerListHandle != null) {
if (containerListHandle instanceof ModbusChannelGroup) {
channelGroup = (ModbusChannelGroup) containerListHandle;
}
}
// create new channelGroup
if (channelGroup == null) {
ArrayList<ModbusChannel> channelList = new ArrayList<>();
for (ChannelRecordContainer container : containers) {
channelList.add(getModbusChannel(container.getChannelAddress(), EAccess.READ));
}
channelGroup = new ModbusChannelGroup(samplingGroup, channelList);
}
// read all channels of the group
try {
readChannelGroup(channelGroup, containers);
} catch (ModbusException e) {
e.printStackTrace();
// set channel values and flag, otherwise the datamanager will throw a null pointer exception
// and the framework collapses.
setChannelsWithErrorFlag(containers);
}
// logger.debug("### readChannelGroup duration in ms = " + ((new Date().getTime()) - startTime));
return channelGroup;
}
// private ModbusChannel getModbusChannel(String channelAddress, EAccess access) {
//
// ModbusChannel modbusChannel = null;
//
// // check if the channel object already exists in the list
// if (modbusChannels.containsKey(channelAddress)) {
// modbusChannel = modbusChannels.get(channelAddress);
//
// // if the channel object exists the access flag might has to be updated
// // (this is case occurs when the channel is readable and writable)
// if (!modbusChannel.getAccessFlag().equals(access)) {
// modbusChannel.update(access);
// }
// }
// // create a new channel object
// else {
// modbusChannel = new ModbusChannel(channelAddress, access);
// modbusChannels.put(channelAddress, modbusChannel);
// }
//
// return modbusChannel;
//
// }
@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 List<ChannelScanInfo> scanForChannels(String settings)
throws UnsupportedOperationException, ArgumentSyntaxException, ScanException, ConnectionException {
throw new UnsupportedOperationException();
}
@Override
public void startListening(List<ChannelRecordContainer> containers, RecordsReceivedListener listener)
throws UnsupportedOperationException, ConnectionException {
throw new UnsupportedOperationException();
}
}