/*
* 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;
import java.util.Hashtable;
import java.util.List;
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.EAccess;
import org.openmuc.framework.driver.spi.ChannelRecordContainer;
import org.openmuc.framework.driver.spi.Connection;
import net.wimpi.modbus.ModbusException;
import net.wimpi.modbus.ModbusIOException;
import net.wimpi.modbus.ModbusSlaveException;
import net.wimpi.modbus.io.ModbusTransaction;
import net.wimpi.modbus.msg.ReadCoilsRequest;
import net.wimpi.modbus.msg.ReadCoilsResponse;
import net.wimpi.modbus.msg.ReadInputDiscretesRequest;
import net.wimpi.modbus.msg.ReadInputDiscretesResponse;
import net.wimpi.modbus.msg.ReadInputRegistersRequest;
import net.wimpi.modbus.msg.ReadInputRegistersResponse;
import net.wimpi.modbus.msg.ReadMultipleRegistersRequest;
import net.wimpi.modbus.msg.ReadMultipleRegistersResponse;
import net.wimpi.modbus.msg.WriteCoilRequest;
import net.wimpi.modbus.msg.WriteMultipleCoilsRequest;
import net.wimpi.modbus.msg.WriteMultipleRegistersRequest;
import net.wimpi.modbus.msg.WriteSingleRegisterRequest;
import net.wimpi.modbus.procimg.InputRegister;
import net.wimpi.modbus.procimg.Register;
import net.wimpi.modbus.procimg.SimpleRegister;
import net.wimpi.modbus.util.BitVector;
public abstract class ModbusConnection implements Connection {
private final ReadCoilsRequest readCoilsRequest;
private final ReadInputDiscretesRequest readInputDiscretesRequest;
private final WriteCoilRequest writeCoilRequest;
private final WriteMultipleCoilsRequest writeMultipleCoilsRequest;
private final ReadInputRegistersRequest readInputRegistersRequest;
private final ReadMultipleRegistersRequest readHoldingRegisterRequest;
private final WriteSingleRegisterRequest writeSingleRegisterRequest;
private final WriteMultipleRegistersRequest writeMultipleRegistersRequest;
private ModbusTransaction transaction;
private final ModbusDriverUtil util;
// List do manage Channel Objects to avoid to check the syntax of each channel address for every read or write
private final Hashtable<String, ModbusChannel> modbusChannels;
public abstract void connect() throws Exception;
@Override
public abstract void disconnect();
public ModbusConnection() {
transaction = null;
util = new ModbusDriverUtil();
modbusChannels = new Hashtable<>();
readCoilsRequest = new ReadCoilsRequest();
readInputDiscretesRequest = new ReadInputDiscretesRequest();
readInputRegistersRequest = new ReadInputRegistersRequest();
readHoldingRegisterRequest = new ReadMultipleRegistersRequest();
writeCoilRequest = new WriteCoilRequest();
writeMultipleCoilsRequest = new WriteMultipleCoilsRequest();
writeSingleRegisterRequest = new WriteSingleRegisterRequest();
writeMultipleRegistersRequest = new WriteMultipleRegistersRequest();
}
public void setTransaction(ModbusTransaction transaction) {
this.transaction = transaction;
}
public Value readChannel(ModbusChannel channel) throws ModbusException {
Value value = null;
switch (channel.getFunctionCode()) {
case FC_01_READ_COILS:
value = util.getBitVectorsValue(readCoils(channel));
break;
case FC_02_READ_DISCRETE_INPUTS:
value = util.getBitVectorsValue(readDiscreteInputs(channel));
break;
case FC_03_READ_HOLDING_REGISTERS:
value = util.getRegistersValue(readHoldingRegisters(channel), channel.getDatatype());
break;
case FC_04_READ_INPUT_REGISTERS:
value = util.getRegistersValue(readInputRegisters(channel), channel.getDatatype());
break;
default:
throw new RuntimeException("FunctionCode " + channel.getFunctionCode() + " not supported yet");
}
return value;
}
public void readChannelGroup(ModbusChannelGroup channelGroup, List<ChannelRecordContainer> containers)
throws ModbusException {
switch (channelGroup.getFunctionCode()) {
case FC_01_READ_COILS:
BitVector coils = readCoils(channelGroup);
channelGroup.setChannelValues(coils, containers);
break;
case FC_02_READ_DISCRETE_INPUTS:
BitVector discretInput = readDiscreteInputs(channelGroup);
channelGroup.setChannelValues(discretInput, containers);
break;
case FC_03_READ_HOLDING_REGISTERS:
Register[] registers = readHoldingRegisters(channelGroup);
channelGroup.setChannelValues(registers, containers);
break;
case FC_04_READ_INPUT_REGISTERS:
InputRegister[] inputRegisters = readInputRegisters(channelGroup);
channelGroup.setChannelValues(inputRegisters, containers);
break;
default:
throw new RuntimeException("FunctionCode " + channelGroup.getFunctionCode() + " not supported yet");
}
}
public void writeChannel(ModbusChannel channel, Value value) throws ModbusException, RuntimeException {
switch (channel.getFunctionCode()) {
case FC_05_WRITE_SINGLE_COIL:
writeSingleCoil(channel, value.asBoolean());
break;
case FC_15_WRITE_MULITPLE_COILS:
writeMultipleCoils(channel, util.getBitVectorFromByteArray(value));
break;
case FC_06_WRITE_SINGLE_REGISTER:
writeSingleRegister(channel, new SimpleRegister(value.asShort()));
break;
case FC_16_WRITE_MULTIPLE_REGISTERS:
writeMultipleRegisters(channel, util.valueToRegisters(value, channel.getDatatype()));
break;
default:
throw new RuntimeException("FunctionCode " + channel.getFunctionCode().toString() + " not supported yet");
}
}
public void setChannelsWithErrorFlag(List<ChannelRecordContainer> containers) {
for (ChannelRecordContainer container : containers) {
container.setRecord(new Record(null, null, Flag.DRIVER_ERROR_CHANNEL_TEMPORARILY_NOT_ACCESSIBLE));
}
}
protected 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;
}
private synchronized BitVector readCoils(int startAddress, int count, int unitID) throws ModbusException {
readCoilsRequest.setReference(startAddress);
readCoilsRequest.setBitCount(count);
readCoilsRequest.setUnitID(unitID);
transaction.setRequest(readCoilsRequest);
transaction.execute();
BitVector bitvector = ((ReadCoilsResponse) transaction.getResponse()).getCoils();
bitvector.forceSize(count);
return bitvector;
}
public BitVector readCoils(ModbusChannel channel) throws ModbusException {
return readCoils(channel.getStartAddress(), channel.getCount(), channel.getUnitId());
}
public BitVector readCoils(ModbusChannelGroup channelGroup) throws ModbusException {
return readCoils(channelGroup.getStartAddress(), channelGroup.getCount(), channelGroup.getUnitId());
}
private synchronized BitVector readDiscreteInputs(int startAddress, int count, int unitID) throws ModbusException {
readInputDiscretesRequest.setReference(startAddress);
readInputDiscretesRequest.setBitCount(count);
readInputDiscretesRequest.setUnitID(unitID);
transaction.setRequest(readInputDiscretesRequest);
transaction.execute();
BitVector bitvector = ((ReadInputDiscretesResponse) transaction.getResponse()).getDiscretes();
bitvector.forceSize(count);
return bitvector;
}
public BitVector readDiscreteInputs(ModbusChannel channel) throws ModbusException {
return readDiscreteInputs(channel.getStartAddress(), channel.getCount(), channel.getUnitId());
}
public BitVector readDiscreteInputs(ModbusChannelGroup channelGroup) throws ModbusException {
return readDiscreteInputs(channelGroup.getStartAddress(), channelGroup.getCount(), channelGroup.getUnitId());
}
private synchronized Register[] readHoldingRegisters(int startAddress, int count, int unitID)
throws ModbusException {
readHoldingRegisterRequest.setReference(startAddress);
readHoldingRegisterRequest.setWordCount(count);
readHoldingRegisterRequest.setUnitID(unitID);
transaction.setRequest(readHoldingRegisterRequest);
transaction.execute();
return ((ReadMultipleRegistersResponse) transaction.getResponse()).getRegisters();
}
public Register[] readHoldingRegisters(ModbusChannel channel) throws ModbusException {
return readHoldingRegisters(channel.getStartAddress(), channel.getCount(), channel.getUnitId());
}
public Register[] readHoldingRegisters(ModbusChannelGroup channelGroup) throws ModbusException {
return readHoldingRegisters(channelGroup.getStartAddress(), channelGroup.getCount(), channelGroup.getUnitId());
}
/**
* Read InputRegisters
*
*/
private synchronized InputRegister[] readInputRegisters(int startAddress, int count, int unitID)
throws ModbusIOException, ModbusSlaveException, ModbusException {
readInputRegistersRequest.setReference(startAddress);
readInputRegistersRequest.setWordCount(count);
readInputRegistersRequest.setUnitID(unitID);
transaction.setRequest(readInputRegistersRequest);
transaction.execute();
InputRegister[] registers = ((ReadInputRegistersResponse) transaction.getResponse()).getRegisters();
return registers;
}
/**
* Read InputRegisters for a channel
*
* @param channel
* Modbus channel
* @return input register array
* @throws ModbusException
* if an modbus error occurs
*/
public InputRegister[] readInputRegisters(ModbusChannel channel) throws ModbusException {
return readInputRegisters(channel.getStartAddress(), channel.getCount(), channel.getUnitId());
}
/**
* Read InputRegisters for a channelGroup
*
* @param channelGroup
* modbus channel group
* @return the input register array
* @throws ModbusException
* if an modbus error occurs
*/
public InputRegister[] readInputRegisters(ModbusChannelGroup channelGroup) throws ModbusException {
return readInputRegisters(channelGroup.getStartAddress(), channelGroup.getCount(), channelGroup.getUnitId());
}
public synchronized void writeSingleCoil(ModbusChannel channel, boolean state) throws ModbusException {
writeCoilRequest.setReference(channel.getStartAddress());
writeCoilRequest.setCoil(state);
writeCoilRequest.setUnitID(channel.getUnitId());
transaction.setRequest(writeCoilRequest);
transaction.execute();
}
public synchronized void writeMultipleCoils(ModbusChannel channel, BitVector coils) throws ModbusException {
writeMultipleCoilsRequest.setReference(channel.getStartAddress());
writeMultipleCoilsRequest.setCoils(coils);
writeMultipleCoilsRequest.setUnitID(channel.getUnitId());
transaction.setRequest(writeMultipleCoilsRequest);
transaction.execute();
}
public synchronized void writeSingleRegister(ModbusChannel channel, Register register) throws ModbusException {
writeSingleRegisterRequest.setReference(channel.getStartAddress());
writeSingleRegisterRequest.setRegister(register);
writeSingleRegisterRequest.setUnitID(channel.getUnitId());
transaction.setRequest(writeSingleRegisterRequest);
transaction.execute();
}
public synchronized void writeMultipleRegisters(ModbusChannel channel, Register[] registers)
throws ModbusException {
writeMultipleRegistersRequest.setReference(channel.getStartAddress());
writeMultipleRegistersRequest.setRegisters(registers);
writeMultipleRegistersRequest.setUnitID(channel.getUnitId());
transaction.setRequest(writeMultipleRegistersRequest);
transaction.execute();
}
}