tuwien.auto.calimero.serial.LibraryAdapter Maven / Gradle / Ivy
Show all versions of calimero-core Show documentation
/*
Calimero 2 - A library for KNX network access
Copyright (c) 2006, 2018 B. Malinowsky
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Linking this library statically or dynamically with other modules is
making a combined work based on this library. Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under terms
of your choice, provided that you also meet, for each linked independent
module, the terms and conditions of the license of that module. An
independent module is a module which is not derived from or based on
this library. If you modify this library, you may extend this exception
to your version of the library, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from your
version.
*/
package tuwien.auto.calimero.serial;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.slf4j.Logger;
import tuwien.auto.calimero.KNXException;
/**
* Adapter to access a serial communication port using some serial I/O library.
*
* Subtypes of this class implementing the access to a specific library have to declare a public
* constructor expecting a String and an int
argument, i.e.,
* public MyAdapter(String portID, int baudrate)
for a class named "MyAdapter". The
* portID
argument identifies the communication port on which to open the connection,
* the baudrate
argument specifies the requested baud rate for communication.
* Invoking that constructor will open the serial port according the supplied arguments.
*
* After closing a library adapter, method behavior is undefined.
*
* @author B. Malinowsky
*/
public abstract class LibraryAdapter implements Closeable
{
/**
* The log service to use, supplied in the constructor; if a sub-class of LibraryAdapter does
* not use logger, it might be set to null.
*/
protected final Logger logger;
/** @return all available serial communication port identifiers. */
public static List getPortIdentifiers()
{
try {
final String ports = System.getProperty("microedition.commports");
if (ports != null)
return Arrays.asList(ports.split(",")).stream().collect(Collectors.toList());
}
catch (final SecurityException e) {}
if (SerialComAdapter.isAvailable()) {
final List ports = new ArrayList<>();
Arrays.asList(defaultPortPrefixes()).forEach(p -> IntStream.range(0, 20)
.filter(i -> SerialComAdapter.portExists(p + i)).forEach(i -> ports.add(p + i)));
return ports;
}
try {
final Class> c = Class.forName("tuwien.auto.calimero.serial.RxtxAdapter");
@SuppressWarnings("unchecked")
final List ports = (List) c.getMethod("getPortIdentifiers").invoke(null);
return ports;
}
catch (Exception | NoClassDefFoundError e) {}
return Collections.emptyList();
}
private static String[] defaultPortPrefixes()
{
return System.getProperty("os.name").toLowerCase().indexOf("windows") > -1 ? new String[] { "\\\\.\\COM" }
: new String[] { "/dev/ttyS", "/dev/ttyACM", "/dev/ttyUSB" };
}
/**
* Factory method to open a serial connection using one of the available library adapters.
*
* @param logger logger
* @param portId serial port identifier
* @param baudrate baudrate
* @param idleTimeout idle timeout in milliseconds
* @return adapter to access serial communication port, port resource is in open state
* @throws KNXException on failure to open or configure serial port, or no adapter available
*/
public static LibraryAdapter open(final Logger logger, final String portId, final int baudrate,
final int idleTimeout) throws KNXException
{
Throwable t = null;
// check for Java ME Embedded platform and available serial communication port,
// protocol support for communication ports is optional
if (CommConnectionAdapter.isAvailable()) {
logger.debug("open Java ME serial port connection (CommConnection) for {}", portId);
try {
return new CommConnectionAdapter(logger, portId, baudrate);
}
catch (final KNXException e) {
t = e;
}
}
// check internal support for serial port access
// protocol support available for Win 32/64 platforms
// (so we provide serial port access at least on platforms with ETS)
if (SerialComAdapter.isAvailable()) {
logger.debug("open Calimero native serial port connection (serialcom) for {}", portId);
SerialComAdapter conn = null;
try {
conn = new SerialComAdapter(logger, portId);
conn.setBaudRate(baudrate);
//final int idleTimeout = idleTimeout(conn.getBaudRate());
conn.setTimeouts(new SerialComAdapter.Timeouts(idleTimeout, 0, 250, 0, 0));
conn.setParity(SerialComAdapter.PARITY_EVEN);
conn.setControl(SerialComAdapter.STOPBITS, SerialComAdapter.ONE_STOPBIT);
conn.setControl(SerialComAdapter.DATABITS, 8);
conn.setControl(SerialComAdapter.FLOWCTRL, SerialComAdapter.FLOWCTRL_NONE);
logger.debug("setup serial port: baudrate " + conn.getBaudRate() + ", even parity, "
+ conn.getControl(SerialComAdapter.DATABITS) + " databits, "
+ conn.getControl(SerialComAdapter.STOPBITS) + " stopbits, timeouts: "
+ conn.getTimeouts());
return conn;
}
catch (final IOException e) {
if (conn != null)
conn.close();
t = e;
}
}
try {
final Class> c = Class.forName("tuwien.auto.calimero.serial.RxtxAdapter");
logger.debug("using rxtx library for serial port access");
final Class extends LibraryAdapter> adapter = LibraryAdapter.class;
return adapter.cast(c.getConstructors()[0]
.newInstance(new Object[] { logger, portId, Integer.valueOf(baudrate) }));
}
catch (final ClassNotFoundException e) {
logger.warn("no rxtx library adapter found");
}
catch (final Exception | NoClassDefFoundError e) {
t = e instanceof InvocationTargetException ? e.getCause() : e;
}
if (t instanceof KNXException)
throw (KNXException) t;
if (t != null)
throw new KNXException("failed to open serial port " + portId, t);
throw new KNXException("no serial adapter available to open " + portId);
}
/**
* Creates a new library adapter.
*
* @param logService the log service to use for this adapter
*/
protected LibraryAdapter(final Logger logService)
{
logger = logService;
}
/**
* Returns the output stream for the opened serial communication port.
*
* Subsequent invocations might return the same or a new stream object.
*
* @return the OutputStream object
*/
public abstract OutputStream getOutputStream();
/**
* Returns the input stream for the opened serial communication port.
*
* Subsequent invocations might return the same or a new stream object.
*
* @return the InputStream object
*/
public abstract InputStream getInputStream();
/**
* Sets a new baud rate for this connection.
*
* @param baudrate requested baud rate [Bit/s], 0 < baud rate
*/
public void setBaudRate(final int baudrate)
{
try {
invoke(this, "setBaudRate", new Object[] { Integer.valueOf(baudrate) });
}
catch (final Exception ignore) {}
}
/**
* Returns the currently used baud rate.
*
*
* @return baud rate in bit/s
*/
public int getBaudRate()
{
try {
return ((Integer) invoke(this, "getBaudRate", new Object[0])).intValue();
}
catch (final Exception ignore) {}
return 0;
}
@Override
public abstract void close();
/**
* Invokes method
name on object obj
with arguments args
.
*
* Arguments wrapped in an object of type Integer are replaced with the primitive int type when
* looking up the method name.
*
* @param obj object on which to invoke the method
* @param method method name
* @param args list of arguments
* @return the result of the invoked method
* @throws NoSuchMethodException if a matching method is not found
* @throws IllegalAccessException if method
is inaccessible
* @throws InvocationTargetException if the invoked method throws an exception
* @see Class#getMethod(String, Class[])
* @see Method#invoke(Object, Object[])
*/
protected Object invoke(final Object obj, final String method, final Object[] args)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
{
final Class>[] c = new Class>[args.length];
for (int i = 0; i < c.length; ++i) {
c[i] = args[i].getClass();
if (c[i] == Integer.class)
c[i] = int.class;
}
try {
if (obj instanceof Class)
return ((Class>) obj).getMethod(method, c).invoke(null, args);
return obj.getClass().getMethod(method, c).invoke(obj, args);
}
catch (final IllegalArgumentException e) {
throw new IllegalArgumentException("illegal argument on invoking "
+ obj.getClass().getName() + "." + method + ": " + e.getMessage());
}
}
}