/*
* Copyright 2012 Steven Swor.
*
* 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 cameljamod;
import cameljamod.net.AbstractMasterConnectionWrapper;
import java.util.Arrays;
import net.wimpi.modbus.io.ModbusTransaction;
import net.wimpi.modbus.msg.ModbusRequest;
import net.wimpi.modbus.msg.ModbusResponse;
import net.wimpi.modbus.procimg.InputRegister;
import net.wimpi.modbus.procimg.Register;
import net.wimpi.modbus.util.BitVector;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.impl.DefaultScheduledPollConsumer;
/**
* Parent class for camel consumers which poll modbus devices.
*
* @author Steven Swor
*/
public abstract class ModbusPollingConsumer<RequestType extends ModbusRequest, ResponseType extends ModbusResponse, BodyType> extends DefaultScheduledPollConsumer {
/**
* The endpoint.
*/
private final JamodEndpoint endpoint;
/**
* The reference address of the first value to read.
*/
private int referenceAddress;
/**
* The number of values to read.
*/
private int count;
/**
* Only send messages when the polled value changes.
*/
private boolean changesOnly;
/**
* The last polled value.
*/
private BodyType lastPolledValue;
/**
* The Modbus Slave ID
*/
private int slaveId = 0;
/**
* Creates a new ModbusPollingConsumer.
*
* @param endpoint the endpoint
* @param processor the processor
*/
public ModbusPollingConsumer(JamodEndpoint endpoint, Processor processor) {
super(endpoint, processor);
this.endpoint = endpoint;
this.lastPolledValue = null;
}
/**
* Gets the number of values to read.
*
* @return the number of values to read
*/
public int getCount() {
return count;
}
/**
* Sets the number of values to read.
*
* @param count the number of values to read
*/
public void setCount(int count) {
this.count = count;
}
/**
* Gets the reference address.
*
* @return the reference address
*/
public int getReferenceAddress() {
return referenceAddress;
}
/**
* Sets the reference address.
*
* @param referenceAddress the reference address
*/
public void setReferenceAddress(int referenceAddress) {
this.referenceAddress = referenceAddress;
}
/**
* Determines whether to send messages every time or only when the value
* changes.
* @return {@code true} if messages will only be sent when the value
* changes, {@code false} if messages will be sent every polling interval.
*/
public boolean isChangesOnly() {
return changesOnly;
}
/**
* Sets whether to send messages every time or only when the value changes.
* @param changesOnly {@code true} if messages will only be sent when the
* value changes, {@code false} if messages will be sent every polling
* interval.
*/
public void setChangesOnly(boolean changesOnly) {
this.changesOnly = changesOnly;
}
/**
* Gets the Modbus Slave ID.
*
* @return the Slave ID
*/
public int getSlaveId() {
return slaveId;
}
/**
* Sets the Modbus Slave ID.
*
* @param slaveId the Slave ID
*/
public void setSlaveId(int slaveId) {
this.slaveId = slaveId;
}
@Override
protected int poll() throws Exception {
Exchange exchange = endpoint.createExchange();
AbstractMasterConnectionWrapper connectionWrapper = endpoint.getConnection();
//create a transaction and execute
RequestType request = createRequest();
request.setUnitID(slaveId);
ModbusTransaction transaction = connectionWrapper.createTransaction();
transaction.setRequest(request);
transaction.execute();
ResponseType response = (ResponseType) transaction.getResponse();
BodyType currentValue = getBodyFromResponse(response);
BodyType tmp = lastPolledValue;
lastPolledValue = currentValue;
if (!isChangesOnly() || valueHasChanged(tmp, currentValue)) {
Message message = exchange.getIn();
message.setBody(currentValue);
getProcessor().process(exchange);
return 1;
}else{
return 0;
}
}
/**
* Determines if a polled value has changed.
* @param oldValue the old value.
* @param newValue the new value.
* @return whether or not the polled value has changed
*/
protected abstract boolean valueHasChanged(BodyType oldValue, BodyType newValue);
/**
* Creates a new request to send to the modbus device.
*
* @return a new request to send to the modbus device
*/
protected abstract RequestType createRequest();
/**
* Gets the body from the response.
*
* @param response the modbus response
* @return the body of the modbus response
*/
protected abstract BodyType getBodyFromResponse(final ResponseType response);
protected boolean valueHasChanged(BitVector oldValue, BitVector newValue) {
if (oldValue==null) {
if (newValue!=null) {
return true;
}
}else{
if (newValue==null) {
return true;
}else{
if (oldValue.size()!=newValue.size()) {
return true;
}
return !Arrays.equals(oldValue.getBytes(), newValue.getBytes());
}
}
return false;
}
protected boolean valueHasChanged(InputRegister[] oldValue, InputRegister[] newValue) {
if (oldValue == null) {
if (newValue != null) {
// old was null, new is non-null
return true;
}
} else {
if (newValue==null) {
// old was non-null, new is null
return true;
}else if (oldValue.length!=newValue.length) {
// different lengths
return true;
}else{
// Check for changes, register-by-register and byte-by-byte
for (int i=0; i < oldValue.length; i++) {
if (!Arrays.equals(oldValue[i].toBytes(), newValue[i].toBytes())) {
return true;
}
}
}
}
return false;
}
protected boolean valueHasChanged(final Register[] oldValue, final Register[] newValue) {
return valueHasChanged((InputRegister[])oldValue, (InputRegister[])newValue);
}
}