/*
* 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 cameljamod.net.TCPMasterConnectionWrapper;
import cameljamod.net.UDPMasterConnectionWrapper;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.Map;
import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.net.TCPMasterConnection;
import net.wimpi.modbus.net.UDPMasterConnection;
import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.ResolveEndpointFailedException;
import org.apache.camel.RuntimeCamelException;
import org.apache.camel.impl.DefaultPollingEndpoint;
/**
* Endpoint for polling discrete inputs.
*
* @author Steven Swor
*/
public class JamodEndpoint extends DefaultPollingEndpoint {
/**
* Modbus Slave ID constant
*/
public static final String SLAVE_ID = "slaveId";
/**
* The connection.
*/
private volatile AbstractMasterConnectionWrapper conn;
/**
* The URI to the modbus device.
*/
private URI modbusURI;
/**
* The parameters.
*/
private Map<String, Object> parameters;
/**
* The component.
*/
private final JamodComponent component;
/**
* Creates a new JamodEndpoint.
*
* @param modbusURI the URI of the modbus device.
*/
public JamodEndpoint(final JamodComponent component, final URI modbusURI, final Map<String, Object> parameters) {
this.modbusURI = modbusURI;
this.parameters = parameters;
this.component = component;
}
/**
* Returns {@code null}.
*
* @return {@code null}
* @throws Exception never
*/
public ModbusProducer createProducer() throws Exception {
String dataType = JamodUriResolver.getDataTypeFromUri(modbusURI);
ModbusProducer producer;
if ("coils".equalsIgnoreCase(dataType)) {
producer = new DiscreteOutputsProducer(this);
} else if ("registers".equalsIgnoreCase(dataType)) {
producer = new RegistersProducer(this);
} else {
throw new IllegalArgumentException(MessageFormat.format("Unsupported data type: {0}", dataType));
}
producer.setReferenceAddress(JamodUriResolver.getReferenceFromUri(modbusURI));
int slaveId = component.getAndRemoveParameter(parameters, SLAVE_ID, Integer.class, 0);
producer.setSlaveId(slaveId);
return producer;
}
@Override
public Consumer createConsumer(final Processor processor) throws Exception {
String dataType = JamodUriResolver.getDataTypeFromUri(modbusURI);
ModbusPollingConsumer consumer;
if ("discreteInputs".equalsIgnoreCase(dataType)) {
consumer = new DiscreteInputsPollingConsumer(this, processor);
} else if ("coils".equalsIgnoreCase(dataType)) {
consumer = new DiscreteOutputsPollingConsumer(this, processor);
} else if ("registers".equalsIgnoreCase(dataType)) {
consumer = new RegistersPollingConsumer(this, processor);
} else if ("register".equalsIgnoreCase(dataType)) {
consumer = new RegisterPollingConsumer(this, processor);
} else if ("inputRegisters".equalsIgnoreCase(dataType)) {
consumer = new InputRegistersPollingConsumer(this, processor);
} else if ("inputRegister".equalsIgnoreCase(dataType)) {
consumer = new InputRegisterPollingConsumer(this, processor);
} else {
throw new IllegalArgumentException(MessageFormat.format("Unsupported data type: {0}", dataType));
}
consumer.setReferenceAddress(JamodUriResolver.getReferenceFromUri(modbusURI));
int delay = component.getAndRemoveParameter(parameters, "delay", Integer.class, Integer.valueOf(500));
consumer.setDelay(delay);
int initialDelay = component.getAndRemoveParameter(parameters, "initialDelay", Integer.class, Integer.valueOf(500));
consumer.setInitialDelay(initialDelay);
int count = component.getAndRemoveParameter(parameters, "count", Integer.class, Integer.valueOf(1));
consumer.setCount(count);
boolean changesOnly = component.getAndRemoveParameter(parameters, "changesOnly", Boolean.class, Boolean.FALSE);
consumer.setChangesOnly(changesOnly);
int slaveId = component.getAndRemoveParameter(parameters, SLAVE_ID, Integer.class, 0);
consumer.setSlaveId(slaveId);
return consumer;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public boolean isLenientProperties() {
return true;
}
@Override
protected String createEndpointUri() {
StringBuilder sb = new StringBuilder(modbusURI.toString());
return sb.toString();
}
/**
* Gets the Modbus connection.
*
* @return the connection
*/
public AbstractMasterConnectionWrapper getConnection() {
if (conn == null) {
synchronized (this) {
if (conn == null) {
conn = createConnection();
}
}
}
return conn;
}
@Override
protected void doStop() throws Exception {
super.doStop();
getConnection().close();
}
/**
* Creates the Modbus connection.
*
* @return the connection
*/
protected AbstractMasterConnectionWrapper createConnection() {
AbstractMasterConnectionWrapper result = null;
InetAddress addr = resolveHostAddress(modbusURI);
if (isTCP(modbusURI)) {
result = new TCPMasterConnectionWrapper(createTCPMasterConnection(addr));
result.setTimeout(1000);
} else if (isUDP(modbusURI)) {
result = new UDPMasterConnectionWrapper(createUDPMasterConnection(addr));
} else {
throw new ResolveEndpointFailedException(modbusURI.toString());
}
int port = modbusURI.getPort();
if (port == -1) {
port = Modbus.DEFAULT_PORT;
}
result.setPort(port);
return result;
}
/**
* Creates a new TCP master connection.
*
* @param addr the address of the modbus device
* @return a new TCP master connection
*/
protected TCPMasterConnection createTCPMasterConnection(final InetAddress addr) {
return new TCPMasterConnection(addr);
}
/**
* Creates a new UDP master connection.
*
* @param addr the address of the modbus device
* @return a new UDP master connection
*/
protected UDPMasterConnection createUDPMasterConnection(final InetAddress addr) {
return new UDPMasterConnection(addr);
}
/**
* Determines if a URI represents a TCP connection.
*
* @param uri the URI to check
* @return {@code true} if the URI scheme is {@code tcp}, otherwise
* {@code false}.
*/
public static boolean isTCP(final URI uri) {
return "tcp".equalsIgnoreCase(uri.getScheme());
}
/**
* Determines if a URI represents a UDP connection.
*
* @param uri the URI to check
* @return {@code true} if the URI scheme is {@code udp}, otherwise
* {@code false}.
*/
public static boolean isUDP(final URI uri) {
return "udp".equalsIgnoreCase(uri.getScheme());
}
/**
* Resolves a modbus device's address from a URI.
*
* @param uri the URI
* @return an {@link java.net.InetAddress} for the URI's host
*/
protected static InetAddress resolveHostAddress(final URI uri) {
String host = uri.getHost();
try {
return InetAddress.getByName(host);
} catch (UnknownHostException ex) {
throw new RuntimeCamelException(ex);
}
}
}