[#561] [#912] Add Rxtx transport

This commit is contained in:
Luke Wood 2013-01-09 10:40:38 +00:00 committed by Norman Maurer
parent d5a7057c3f
commit c094abad7b
10 changed files with 771 additions and 0 deletions

View File

@ -75,6 +75,11 @@
<artifactId>netty-transport-udt</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-transport-rxtx</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,61 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.example.rxtx;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.oio.OioEventLoopGroup;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.transport.rxtx.RxtxChannel;
import io.netty.transport.rxtx.RxtxDeviceAddress;
/**
* Sends one message to a serial device
*/
public final class RxtxClient {
public static void main(String[] args) throws Exception {
Bootstrap b = new Bootstrap();
try {
b.group(new OioEventLoopGroup())
.channel(RxtxChannel.class)
.remoteAddress(new RxtxDeviceAddress("/dev/ttyUSB0"))
.handler(new ChannelInitializer<RxtxChannel>() {
@Override
public void initChannel(RxtxChannel ch) throws Exception {
ch.pipeline().addLast(
new LineBasedFrameDecoder(32768),
new StringEncoder(),
new StringDecoder(),
new RxtxClientHandler()
);
}
});
ChannelFuture f = b.connect().sync();
f.channel().closeFuture().sync();
} finally {
b.shutdown();
}
}
private RxtxClient() {
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.example.rxtx;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
public class RxtxClientHandler extends ChannelInboundMessageHandlerAdapter<String> {
@Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.write("AT\n");
}
@Override
protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception {
if ("OK".equals(msg)) {
System.out.println("Serial port responded to AT");
} else {
System.out.println("Serial port responded with not-OK: " + msg);
}
ctx.close();
}
}

View File

@ -79,6 +79,7 @@
<module>codec-http</module>
<module>codec-socks</module>
<module>transport</module>
<module>transport-rxtx</module>
<module>transport-sctp</module>
<module>transport-udt</module>
<module>handler</module>
@ -106,6 +107,12 @@
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>

54
transport-rxtx/pom.xml Normal file
View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2013 The Netty Project
~
~ The Netty Project licenses this file to you 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId>
<version>4.0.0.Beta1-SNAPSHOT</version>
</parent>
<artifactId>netty-transport-rxtx</artifactId>
<packaging>jar</packaging>
<name>Netty/RXTX Transport</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-buffer</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-codec</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>netty-transport</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.rxtx</groupId>
<artifactId>rxtx</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,178 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.transport.rxtx;
import static io.netty.transport.rxtx.RxtxChannelOptions.*;
import io.netty.buffer.BufType;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.socket.oio.AbstractOioByteChannel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.nio.channels.NotYetConnectedException;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
/**
* A channel to a serial device using the RXTX library.
*/
public class RxtxChannel extends AbstractOioByteChannel {
private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.BYTE, true);
private final ChannelConfig config;
private RxtxDeviceAddress deviceAddress;
private SerialPort serialPort;
private InputStream in;
private OutputStream out;
public RxtxChannel() {
super(null, null);
config = new RxtxChannelConfig(this);
}
@Override
public ChannelConfig config() {
return config;
}
@Override
public ChannelMetadata metadata() {
return METADATA;
}
@Override
public boolean isOpen() {
return true;
}
@Override
public boolean isActive() {
return in != null && out != null;
}
@Override
protected int available() {
try {
return in.available();
} catch (IOException e) {
return 0;
}
}
@Override
protected int doReadBytes(ByteBuf buf) throws Exception {
try {
return buf.writeBytes(in, buf.writableBytes());
} catch (SocketTimeoutException e) {
return 0;
}
}
@Override
protected void doWriteBytes(ByteBuf buf) throws Exception {
if (out == null) {
throw new NotYetConnectedException();
}
buf.readBytes(out, buf.readableBytes());
}
@Override
protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
RxtxDeviceAddress remote = (RxtxDeviceAddress) remoteAddress;
final CommPortIdentifier cpi =
CommPortIdentifier.getPortIdentifier(remote.getDeviceAddress());
final CommPort commPort = cpi.open(getClass().getName(), 1000);
deviceAddress = remote;
serialPort = (SerialPort) commPort;
serialPort.setSerialPortParams(
config().getOption(BAUD_RATE),
config().getOption(DATA_BITS).getValue(),
config().getOption(STOP_BITS).getValue(),
config().getOption(PARITY_BIT).getValue()
);
serialPort.setDTR(config().getOption(DTR));
serialPort.setRTS(config().getOption(RTS));
out = serialPort.getOutputStream();
in = serialPort.getInputStream();
}
@Override
protected SocketAddress localAddress0() {
return null;
}
@Override
protected SocketAddress remoteAddress0() {
return deviceAddress;
}
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
throw new UnsupportedOperationException();
}
@Override
protected void doDisconnect() throws Exception {
doClose();
}
@Override
protected void doClose() throws Exception {
IOException ex = null;
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
ex = e;
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
ex = e;
}
if (serialPort != null) {
serialPort.removeEventListener();
serialPort.close();
}
in = null;
out = null;
serialPort = null;
if (ex != null) {
throw ex;
}
}
}

View File

@ -0,0 +1,317 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.transport.rxtx;
import static io.netty.transport.rxtx.RxtxChannelOptions.*;
import io.netty.channel.ChannelOption;
import io.netty.channel.DefaultChannelConfig;
import java.util.Map;
import gnu.io.SerialPort;
/**
* A configuration class for RXTX device connections.
*/
public class RxtxChannelConfig extends DefaultChannelConfig {
public enum Stopbits {
/**
* 1 stop bit will be sent at the end of every character
*/
STOPBITS_1(SerialPort.STOPBITS_1),
/**
* 2 stop bits will be sent at the end of every character
*/
STOPBITS_2(SerialPort.STOPBITS_2),
/**
* 1.5 stop bits will be sent at the end of every character
*/
STOPBITS_1_5(SerialPort.STOPBITS_1_5);
private final int value;
Stopbits(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Stopbits ofValue(int value) {
for (Stopbits stopbit : Stopbits.values()) {
if (stopbit.value == value) {
return stopbit;
}
}
throw new IllegalArgumentException("Unknown value for Stopbits: " + value + '.');
}
}
public enum Databits {
/**
* 5 data bits will be used for each character (ie. Baudot code)
*/
DATABITS_5(SerialPort.DATABITS_5),
/**
* 6 data bits will be used for each character
*/
DATABITS_6(SerialPort.DATABITS_6),
/**
* 7 data bits will be used for each character (ie. ASCII)
*/
DATABITS_7(SerialPort.DATABITS_7),
/**
* 8 data bits will be used for each character (ie. binary data)
*/
DATABITS_8(SerialPort.DATABITS_8);
private final int value;
Databits(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Databits ofValue(int value) {
for (Databits databit : Databits.values()) {
if (databit.value == value) {
return databit;
}
}
throw new IllegalArgumentException("Unknown value for Databits: " + value + '.');
}
}
public enum Paritybit {
/**
* No parity bit will be sent with each data character at all
*/
NONE(SerialPort.PARITY_NONE),
/**
* An odd parity bit will be sent with each data character, ie. will be set
* to 1 if the data character contains an even number of bits set to 1.
*/
ODD(SerialPort.PARITY_ODD),
/**
* An even parity bit will be sent with each data character, ie. will be set
* to 1 if the data character contains an odd number of bits set to 1.
*/
EVEN(SerialPort.PARITY_EVEN),
/**
* A mark parity bit (ie. always 1) will be sent with each data character
*/
MARK(SerialPort.PARITY_MARK),
/**
* A space parity bit (ie. always 0) will be sent with each data character
*/
SPACE(SerialPort.PARITY_SPACE);
private final int value;
Paritybit(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Paritybit ofValue(int value) {
for (Paritybit paritybit : Paritybit.values()) {
if (paritybit.value == value) {
return paritybit;
}
}
throw new IllegalArgumentException("Unknown value for paritybit: " + value + '.');
}
}
private volatile int baudrate = 115200;
private volatile boolean dtr;
private volatile boolean rts;
private volatile Stopbits stopbits = Stopbits.STOPBITS_1;
private volatile Databits databits = Databits.DATABITS_8;
private volatile Paritybit paritybit = Paritybit.NONE;
public RxtxChannelConfig(RxtxChannel channel) {
super(channel);
}
@Override
public Map<ChannelOption<?>, Object> getOptions() {
return getOptions(super.getOptions(),
BAUD_RATE, DTR, RTS, STOP_BITS, DATA_BITS, PARITY_BIT);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getOption(ChannelOption<T> option) {
if (option == BAUD_RATE) {
return (T) Integer.valueOf(getBaudrate());
}
if (option == DTR) {
return (T) Boolean.valueOf(isDtr());
}
if (option == RTS) {
return (T) Boolean.valueOf(isRts());
}
if (option == STOP_BITS) {
return (T) getStopbits();
}
if (option == DATA_BITS) {
return (T) getDatabits();
}
if (option == PARITY_BIT) {
return (T) getParitybit();
}
return super.getOption(option);
}
@Override
public <T> boolean setOption(ChannelOption<T> option, T value) {
validate(option, value);
if (option == BAUD_RATE) {
setBaudrate((Integer) value);
} else if (option == DTR) {
setDtr((Boolean) value);
} else if (option == RTS) {
setRts((Boolean) value);
} else if (option == STOP_BITS) {
setStopbits((Stopbits) value);
} else if (option == DATA_BITS) {
setDatabits((Databits) value);
} else if (option == PARITY_BIT) {
setParitybit((Paritybit) value);
} else {
return super.setOption(option, value);
}
return true;
}
/**
* Sets the baud rate (ie. bits per second) for communication with the serial device.
* The baud rate will include bits for framing (in the form of stop bits and parity),
* such that the effective data rate will be lower than this value.
*
* @param baudrate The baud rate (in bits per second)
*/
public void setBaudrate(final int baudrate) {
this.baudrate = baudrate;
}
/**
* Sets the number of stop bits to include at the end of every character to aid the
* serial device in synchronising with the data.
*
* @param stopbits The number of stop bits to use
*/
public void setStopbits(final Stopbits stopbits) {
this.stopbits = stopbits;
}
/**
* Sets the number of data bits to use to make up each character sent to the serial
* device.
*
* @param databits The number of data bits to use
*/
public void setDatabits(final Databits databits) {
this.databits = databits;
}
/**
* Sets the type of parity bit to be used when communicating with the serial device.
*
* @param paritybit The type of parity bit to be used
*/
public void setParitybit(final Paritybit paritybit) {
this.paritybit = paritybit;
}
/**
* @return The configured baud rate, defaulting to 115200 if unset
*/
public int getBaudrate() {
return baudrate;
}
/**
* @return The configured stop bits, defaulting to {@link Stopbits#STOPBITS_1} if unset
*/
public Stopbits getStopbits() {
return stopbits;
}
/**
* @return The configured data bits, defaulting to {@link Databits#DATABITS_8} if unset
*/
public Databits getDatabits() {
return databits;
}
/**
* @return The configured parity bit, defaulting to {@link Paritybit#NONE} if unset
*/
public Paritybit getParitybit() {
return paritybit;
}
/**
* @return true if the serial device should support the Data Terminal Ready signal
*/
public boolean isDtr() {
return dtr;
}
/**
* Sets whether the serial device supports the Data Terminal Ready signal, used for
* flow control
*
* @param dtr true if DTR is supported, false otherwise
*/
public void setDtr(final boolean dtr) {
this.dtr = dtr;
}
/**
* @return true if the serial device should support the Ready to Send signal
*/
public boolean isRts() {
return rts;
}
/**
* Sets whether the serial device supports the Request To Send signal, used for flow
* control
*
* @param rts true if RTS is supported, false otherwise
*/
public void setRts(final boolean rts) {
this.rts = rts;
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.transport.rxtx;
import io.netty.channel.ChannelOption;
import io.netty.transport.rxtx.RxtxChannelConfig.Databits;
import io.netty.transport.rxtx.RxtxChannelConfig.Paritybit;
import io.netty.transport.rxtx.RxtxChannelConfig.Stopbits;
/**
* Options for configuring a serial port connection
*/
public final class RxtxChannelOptions {
public static final ChannelOption<Integer> BAUD_RATE =
new ChannelOption<Integer>("BAUD_RATE");
public static final ChannelOption<Boolean> DTR =
new ChannelOption<Boolean>("DTR");
public static final ChannelOption<Boolean> RTS =
new ChannelOption<Boolean>("RTS");
public static final ChannelOption<Stopbits> STOP_BITS =
new ChannelOption<Stopbits>("STOP_BITS");
public static final ChannelOption<Databits> DATA_BITS =
new ChannelOption<Databits>("DATA_BITS");
public static final ChannelOption<Paritybit> PARITY_BIT =
new ChannelOption<Paritybit>("PARITY_BIT");
private RxtxChannelOptions() {
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.transport.rxtx;
import java.net.SocketAddress;
/**
* A {@link SocketAddress} subclass to wrap the serial port address of a RXTX
* device (e.g. COM1, /dev/ttyUSB0).
*/
public class RxtxDeviceAddress extends SocketAddress {
private static final long serialVersionUID = -2907820090993709523L;
private final String deviceAddress;
/**
* Creates a RxtxDeviceAddress representing the address of the serial port.
*
* @param deviceAddress the address of the device (e.g. COM1, /dev/ttyUSB0, ...)
*/
public RxtxDeviceAddress(String deviceAddress) {
this.deviceAddress = deviceAddress;
}
/**
* @return The serial port address of the device (e.g. COM1, /dev/ttyUSB0, ...)
*/
public String getDeviceAddress() {
return deviceAddress;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you 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.
*/
/**
* A serial and parallel port communication transport based on <a href="http://rxtx.qbang.org/">RXTX</a>.
*/
package io.netty.transport.rxtx;