diff --git a/README.md b/README.md index a7bbcb45..f0ac8bef 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ Removal of partially implemented code to streamline the lib for just serial port Full Eclipse integration, for testing application code against sources. +RFC 2217 support provided by incorporating the jvser library (see http://github.com/archiecobbs/jvser) + ##And a bunch of bug fixes## Fixed the memory access error that causes OSX to crash the JVM when serial.close() is called diff --git a/nrjavaserial/pom.xml b/nrjavaserial/pom.xml index 5702d92e..322f03eb 100644 --- a/nrjavaserial/pom.xml +++ b/nrjavaserial/pom.xml @@ -39,12 +39,23 @@ + + + commons-net + commons-net + 3.3 + + + + + src/main/resources + src/main/c/resources/ - **/.svn/ + **/.svn/ diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/AbstractComPortCommandSwitch.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/AbstractComPortCommandSwitch.java new file mode 100644 index 00000000..b04a1d90 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/AbstractComPortCommandSwitch.java @@ -0,0 +1,185 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: AbstractComPortCommandSwitch.java 6 2010-11-20 23:37:06Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +/** + * Adapter class for {@link ComPortCommandSwitch} implementations. + * + * @see ComPortCommandSwitch + * @see ComPortCommand#visit + */ +public class AbstractComPortCommandSwitch implements ComPortCommandSwitch { + + /** + * Visit method invoked by {@link SignatureCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseSignature(SignatureCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link BaudRateCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseBaudRate(BaudRateCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link DataSizeCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseDataSize(DataSizeCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link ParityCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseParity(ParityCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link StopSizeCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseStopSize(StopSizeCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link ControlCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseControl(ControlCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link NotifyLineStateCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseNotifyLineState(NotifyLineStateCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link NotifyModemStateCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseNotifyModemState(NotifyModemStateCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link FlowControlSuspendCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseFlowControlSuspend(FlowControlSuspendCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link FlowControlResumeCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseFlowControlResume(FlowControlResumeCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link LineStateMaskCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseLineStateMask(LineStateMaskCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link ModemStateMaskCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void caseModemStateMask(ModemStateMaskCommand command) { + caseDefault(command); + } + + /** + * Visit method invoked by {@link PurgeDataCommand} instances. + * + *

+ * The implementation in {@link AbstractComPortCommandSwitch} delegates to {@link #caseDefault}. + *

+ */ + @Override + public void casePurgeData(PurgeDataCommand command) { + caseDefault(command); + } + + /** + * Default handler. + * + *

+ * All other methods in {@link AbstractComPortCommandSwitch} delegate to this method; + * the implementation in {@link AbstractComPortCommandSwitch} does nothing. + *

+ */ + protected void caseDefault(ComPortCommand command) { + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/BaudRateCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/BaudRateCommand.java new file mode 100644 index 00000000..954021ee --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/BaudRateCommand.java @@ -0,0 +1,78 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: BaudRateCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_BAUDRATE; + +/** + * RFC 2217 {@code SET-BAUDRATE} command. + * + * @see RFC 2217 + */ +public class BaudRateCommand extends ComPortCommand { + + private int baudRate; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length that is too short or too long + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_BAUDRATE} (client or server) + */ + public BaudRateCommand(int[] bytes) { + super("SET-BAUDRATE", SET_BAUDRATE, bytes); + this.baudRate = ((bytes[2] & 0xff) << 24) | ((bytes[3] & 0xff) << 16) | ((bytes[4] & 0xff) << 8) | (bytes[5] & 0xff); + } + + /** + * Encoding constructor. + * + * @param baudRate baud rate + * @param client true for the client-to-server command, false for the server-to-client command + */ + public BaudRateCommand(boolean client, int baudRate) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_BAUDRATE : SET_BAUDRATE + SERVER_OFFSET, + (baudRate >> 24) & 0xff, + (baudRate >> 16) & 0xff, + (baudRate >> 8) & 0xff, + baudRate & 0xff, + }); + } + + @Override + public String toString() { + return this.getName() + " " + this.baudRate; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseBaudRate(this); + } + + public int getBaudRate() { + return this.baudRate; + } + + @Override + int getMinPayloadLength() { + return 4; + } + + @Override + int getMaxPayloadLength() { + return 4; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommand.java new file mode 100644 index 00000000..e96a2f9b --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommand.java @@ -0,0 +1,142 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ComPortCommand.java 47 2011-12-07 15:56:28Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import java.util.Arrays; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * Superclass for RFC 2217 commands. + * + *

+ * Instances of this class (and all subclasses) are immutable. + *

+ * + * @see RFC 2217 + */ +public abstract class ComPortCommand { + + final String name; + final int[] bytes; + + /** + * Constructor. + * + * @param name human readable name of this command + * @param command required {@code COM-PORT-OPTION} command byte value (must be the client-to-server value) + * @param bytes encoded command starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length that is too short or too long + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code command} is greater than or equal to {@link RFC2217#SERVER_OFFSET} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@code command} + * or {@code command} + {@link RFC2217#SERVER_OFFSET} + */ + protected ComPortCommand(String name, int command, int[] bytes) { + this.name = name; + int minLength = 2 + this.getMinPayloadLength(); + int maxLength = 2 + this.getMaxPayloadLength(); + if (bytes.length < minLength || bytes.length > maxLength) { + throw new IllegalArgumentException("command " + command + " length = " + + bytes.length + " is not in the range " + minLength + ".." + maxLength); + } + this.bytes = bytes.clone(); // maintain immutability + if (this.bytes[0] != COM_PORT_OPTION) + throw new IllegalArgumentException("not a COM-PORT-OPTION"); + if (command >= SERVER_OFFSET) + throw new IllegalArgumentException("invalid command " + command); + if (this.getCommand() != command && this.getCommand() != command + SERVER_OFFSET) + throw new IllegalArgumentException("not a " + name + " option"); + } + + /** + * Determine if this command is client-to-server or server-to-client. + * + * @return true if this command is sent from the server to the client, false for the opposite + */ + public final boolean isServerCommand() { + return this.getCommand() >= SERVER_OFFSET; + } + + /** + * Get the encoding of this instance. + * + * @return encoding starting with {@code COM-PORT-OPTION} + */ + public final int[] getBytes() { + return this.bytes.clone(); // maintain immutability + } + + /** + * Get the command byte. + * + * @return RFC 2217-defined byte value for this command + */ + public final int getCommand() { + return this.bytes[1] & 0xff; + } + + /** + * Get the human-readable name of this option. + */ + public String getName() { + return this.name + (this.isServerCommand() ? "[S]" : "[C]"); + } + + /** + * Get the human-readable description of this option. + */ + @Override + public abstract String toString(); + + /** + * Apply visitor pattern. + * + * @param sw visitor switch handler + */ + public abstract void visit(ComPortCommandSwitch sw); + + /** + * Get the option payload as bytes. + */ + byte[] getPayload() { + byte[] buf = new byte[this.bytes.length - 2]; + for (int i = 2; i < bytes.length; i++) + buf[i - 2] = (byte)this.bytes[i]; + return buf; + } + + /** + * Get minimum required length of the payload of this command. + */ + abstract int getMinPayloadLength(); + + /** + * Get maximum required length of the payload of this command. + */ + abstract int getMaxPayloadLength(); + + @Override + public boolean equals(Object obj) { + if (obj == null || obj.getClass() != getClass()) + return false; + ComPortCommand that = (ComPortCommand)obj; + return Arrays.equals(this.bytes, that.bytes); + } + + @Override + public int hashCode() { + int hash = 0; + for (int i = 0; i < this.bytes.length; i++) + hash = hash * 37 + this.bytes[i]; + return hash; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommandSwitch.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommandSwitch.java new file mode 100644 index 00000000..0eff667a --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortCommandSwitch.java @@ -0,0 +1,43 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ComPortCommandSwitch.java 6 2010-11-20 23:37:06Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +/** + * Visitor pattern interface for {@link ComPortCommand} classes. + * + * @see ComPortCommand#visit + */ +public interface ComPortCommandSwitch { + + void caseSignature(SignatureCommand command); + + void caseBaudRate(BaudRateCommand command); + + void caseDataSize(DataSizeCommand command); + + void caseParity(ParityCommand command); + + void caseStopSize(StopSizeCommand command); + + void caseControl(ControlCommand command); + + void caseNotifyLineState(NotifyLineStateCommand command); + + void caseNotifyModemState(NotifyModemStateCommand command); + + void caseFlowControlSuspend(FlowControlSuspendCommand command); + + void caseFlowControlResume(FlowControlResumeCommand command); + + void caseLineStateMask(LineStateMaskCommand command); + + void caseModemStateMask(ModemStateMaskCommand command); + + void casePurgeData(PurgeDataCommand command); +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortOptionHandler.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortOptionHandler.java new file mode 100644 index 00000000..acf1b709 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ComPortOptionHandler.java @@ -0,0 +1,63 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ComPortOptionHandler.java 46 2011-10-16 23:06:39Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import org.apache.commons.net.telnet.TelnetOptionHandler; + +/** + * RFC 2217 telnet COM-PORT-OPTION. + * + * @see RFC 2217 + */ +public class ComPortOptionHandler extends TelnetOptionHandler { + + private final TelnetSerialPort port; + + protected ComPortOptionHandler(TelnetSerialPort telnetSerialPort) { + super(RFC2217.COM_PORT_OPTION, true, false, true, false); + if (telnetSerialPort == null) + throw new IllegalArgumentException("null telnetSerialPort"); + this.port = telnetSerialPort; + } + + @Override + public int[] answerSubnegotiation(int[] data, int length) { + + // Copy data into buffer of the correct size + if (data.length != length) { + int[] data2 = new int[length]; + System.arraycopy(data, 0, data2, 0, length); + data = data2; + } + + // Decode option + ComPortCommand command; + try { + command = RFC2217.decodeComPortCommand(data); + } catch (IllegalArgumentException e) { + System.err.println(this.port.getName() + ": rec'd invalid COM-PORT-OPTION command: " + e.getMessage()); + return null; + } + + // Notify port + this.port.handleCommand(command); + return null; + } + + @Override + public int[] startSubnegotiationLocal() { + this.port.startSubnegotiation(); + return null; + } + + @Override + public int[] startSubnegotiationRemote() { + return null; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ControlCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ControlCommand.java new file mode 100644 index 00000000..50756c36 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ControlCommand.java @@ -0,0 +1,188 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ControlCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.CONTROL_BREAK_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_BREAK_ON; +import static gnu.io.rfc2217.RFC2217.CONTROL_BREAK_REQUEST; +import static gnu.io.rfc2217.RFC2217.CONTROL_DTR_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_DTR_ON; +import static gnu.io.rfc2217.RFC2217.CONTROL_DTR_REQUEST; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_DTR; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_HARDWARE; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_NONE; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_REQUEST; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_XON_XOFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_DCD; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_DSR; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_HARDWARE; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_NONE; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_REQUEST; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_XON_XOFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_RTS_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_RTS_ON; +import static gnu.io.rfc2217.RFC2217.CONTROL_RTS_REQUEST; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_CONTROL; + +/** + * RFC 2217 {@code SET-CONTROL} command. + * + * @see RFC 2217 + */ +public class ControlCommand extends ComPortCommand { + + private int control; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_CONTROL} (client or server) + * @throws IllegalArgumentException if {@code bytes[2]} is not a valid RFC 2217 control value + */ + public ControlCommand(int[] bytes) { + super("SET-CONTROL", SET_CONTROL, bytes); + this.control = bytes[2]; + switch (this.control) { + case CONTROL_OUTBOUND_FLOW_REQUEST: + case CONTROL_OUTBOUND_FLOW_NONE: + case CONTROL_OUTBOUND_FLOW_XON_XOFF: + case CONTROL_OUTBOUND_FLOW_HARDWARE: + case CONTROL_BREAK_REQUEST: + case CONTROL_BREAK_ON: + case CONTROL_BREAK_OFF: + case CONTROL_DTR_REQUEST: + case CONTROL_DTR_ON: + case CONTROL_DTR_OFF: + case CONTROL_RTS_REQUEST: + case CONTROL_RTS_ON: + case CONTROL_RTS_OFF: + case CONTROL_INBOUND_FLOW_REQUEST: + case CONTROL_INBOUND_FLOW_NONE: + case CONTROL_INBOUND_FLOW_XON_XOFF: + case CONTROL_INBOUND_FLOW_HARDWARE: + case CONTROL_OUTBOUND_FLOW_DCD: + case CONTROL_INBOUND_FLOW_DTR: + case CONTROL_OUTBOUND_FLOW_DSR: + break; + default: + throw new IllegalArgumentException("invalid control value " + this.control); + } + } + + /** + * Encoding constructor. + * + * @param command control command + * @param client true for the client-to-server command, false for the server-to-client command + * @throws IllegalArgumentException if {@code command} is not a valid RFC 2217 control value + */ + public ControlCommand(boolean client, int command) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_CONTROL : SET_CONTROL + SERVER_OFFSET, + command + }); + } + + @Override + public String toString() { + String desc; + switch (this.control) { + case CONTROL_OUTBOUND_FLOW_REQUEST: + desc = "OUTBOUND-FLOW-REQUEST"; + break; + case CONTROL_OUTBOUND_FLOW_NONE: + desc = "OUTBOUND-FLOW-NONE"; + break; + case CONTROL_OUTBOUND_FLOW_XON_XOFF: + desc = "OUTBOUND-FLOW-XON-XOFF"; + break; + case CONTROL_OUTBOUND_FLOW_HARDWARE: + desc = "OUTBOUND-FLOW-HARDWARE"; + break; + case CONTROL_BREAK_REQUEST: + desc = "OUTBOUND-BREAK-REQUEST"; + break; + case CONTROL_BREAK_ON: + desc = "OUTBOUND-BREAK-ON"; + break; + case CONTROL_BREAK_OFF: + desc = "OUTBOUND-BREAK-OFF"; + break; + case CONTROL_DTR_REQUEST: + desc = "OUTBOUND-DTR-REQUEST"; + break; + case CONTROL_DTR_ON: + desc = "OUTBOUND-DTR-ON"; + break; + case CONTROL_DTR_OFF: + desc = "OUTBOUND-DTR-OFF"; + break; + case CONTROL_RTS_REQUEST: + desc = "OUTBOUND-RTS-REQUEST"; + break; + case CONTROL_RTS_ON: + desc = "OUTBOUND-RTS-ON"; + break; + case CONTROL_RTS_OFF: + desc = "OUTBOUND-RTS-OFF"; + break; + case CONTROL_INBOUND_FLOW_REQUEST: + desc = "INBOUND-FLOW-REQUEST"; + break; + case CONTROL_INBOUND_FLOW_NONE: + desc = "INBOUND-FLOW-NONE"; + break; + case CONTROL_INBOUND_FLOW_XON_XOFF: + desc = "INBOUND-FLOW-XON-OFF"; + break; + case CONTROL_INBOUND_FLOW_HARDWARE: + desc = "INBOUND-FLOW-HARDWARE"; + break; + case CONTROL_OUTBOUND_FLOW_DCD: + desc = "OUTBOUND-FLOW-DCD"; + break; + case CONTROL_INBOUND_FLOW_DTR: + desc = "INBOUND-FLOW-DTR"; + break; + case CONTROL_OUTBOUND_FLOW_DSR: + desc = "OUTBOUND-FLOW-DSR"; + break; + default: + desc = "?"; + break; + } + return this.getName() + " " + desc; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseControl(this); + } + + public int getControl() { + return this.control; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/DataSizeCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/DataSizeCommand.java new file mode 100644 index 00000000..9af103c7 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/DataSizeCommand.java @@ -0,0 +1,113 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: DataSizeCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.DATASIZE_5; +import static gnu.io.rfc2217.RFC2217.DATASIZE_6; +import static gnu.io.rfc2217.RFC2217.DATASIZE_7; +import static gnu.io.rfc2217.RFC2217.DATASIZE_8; +import static gnu.io.rfc2217.RFC2217.DATASIZE_REQUEST; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_DATASIZE; + +/** + * RFC 2217 {@code SET-DATASIZE} command. + * + * @see RFC 2217 + */ +public class DataSizeCommand extends ComPortCommand { + + private int dataSize; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_DATASIZE} (client or server) + * @throws IllegalArgumentException if {@code bytes[2]} is not a valid RFC 2217 data size value + */ + public DataSizeCommand(int[] bytes) { + super("SET-DATASIZE", SET_DATASIZE, bytes); + this.dataSize = bytes[2]; + switch (this.dataSize) { + case DATASIZE_REQUEST: + case DATASIZE_5: + case DATASIZE_6: + case DATASIZE_7: + case DATASIZE_8: + break; + default: + throw new IllegalArgumentException("invalid data size value " + this.dataSize); + } + } + + /** + * Encoding constructor. + * + * @param dataSize data size value + * @param client true for the client-to-server command, false for the server-to-client command + * @throws IllegalArgumentException if {@code dataSize} is not a valid RFC 2217 data size value + */ + public DataSizeCommand(boolean client, int dataSize) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_DATASIZE : SET_DATASIZE + SERVER_OFFSET, + dataSize + }); + } + + @Override + public String toString() { + String desc; + switch (this.dataSize) { + case DATASIZE_REQUEST: + desc = "REQUEST"; + break; + case DATASIZE_5: + desc = "5"; + break; + case DATASIZE_6: + desc = "6"; + break; + case DATASIZE_7: + desc = "7"; + break; + case DATASIZE_8: + desc = "8"; + break; + default: + desc = "?"; + break; + } + return this.getName() + " " + desc; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseDataSize(this); + } + + public int getDataSize() { + return this.dataSize; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlResumeCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlResumeCommand.java new file mode 100644 index 00000000..153e8493 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlResumeCommand.java @@ -0,0 +1,66 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: FlowControlResumeCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.FLOWCONTROL_RESUME; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * RFC 2217 {@code FLOWCONTROL-RESUME} command. + * + * @see RFC 2217 + */ +public class FlowControlResumeCommand extends ComPortCommand { + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#FLOWCONTROL_RESUME} (client or server) + */ + public FlowControlResumeCommand(int[] bytes) { + super("FLOWCONTROL-RESUME", FLOWCONTROL_RESUME, bytes); + } + + /** + * Encoding constructor. + * + * @param client true for the client-to-server command, false for the server-to-client command + */ + public FlowControlResumeCommand(boolean client) { + this(new int[] { + COM_PORT_OPTION, + client ? FLOWCONTROL_RESUME : FLOWCONTROL_RESUME + SERVER_OFFSET, + }); + } + + @Override + public String toString() { + return this.getName(); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseFlowControlResume(this); + } + + @Override + int getMinPayloadLength() { + return 0; + } + + @Override + int getMaxPayloadLength() { + return 0; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlSuspendCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlSuspendCommand.java new file mode 100644 index 00000000..558fe9c4 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/FlowControlSuspendCommand.java @@ -0,0 +1,66 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: FlowControlSuspendCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.FLOWCONTROL_SUSPEND; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * RFC 2217 {@code FLOWCONTROL-SUSPEND} command. + * + * @see RFC 2217 + */ +public class FlowControlSuspendCommand extends ComPortCommand { + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#FLOWCONTROL_SUSPEND} (client or server) + */ + public FlowControlSuspendCommand(int[] bytes) { + super("FLOWCONTROL-SUSPEND", FLOWCONTROL_SUSPEND, bytes); + } + + /** + * Encoding constructor. + * + * @param client true for the client-to-server command, false for the server-to-client command + */ + public FlowControlSuspendCommand(boolean client) { + this(new int[] { + COM_PORT_OPTION, + client ? FLOWCONTROL_SUSPEND : FLOWCONTROL_SUSPEND + SERVER_OFFSET, + }); + } + + @Override + public String toString() { + return this.getName(); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseFlowControlSuspend(this); + } + + @Override + int getMinPayloadLength() { + return 0; + } + + @Override + int getMaxPayloadLength() { + return 0; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/LineStateMaskCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/LineStateMaskCommand.java new file mode 100644 index 00000000..d1a03443 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/LineStateMaskCommand.java @@ -0,0 +1,75 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: LineStateMaskCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_LINESTATE_MASK; + +/** + * RFC 2217 {@code SET-LINESTATE-MASK} command. + * + * @see RFC 2217 + */ +public class LineStateMaskCommand extends ComPortCommand { + + private int lineStateMask; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_LINESTATE_MASK} (client or server) + */ + public LineStateMaskCommand(int[] bytes) { + super("SET-LINESTATE-MASK", SET_LINESTATE_MASK, bytes); + this.lineStateMask = bytes[2]; + } + + /** + * Encoding constructor. + * + * @param lineStateMask line state mask value + * @param client true for the client-to-server command, false for the server-to-client command + */ + public LineStateMaskCommand(boolean client, int lineStateMask) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_LINESTATE_MASK : SET_LINESTATE_MASK + SERVER_OFFSET, + lineStateMask + }); + } + + @Override + public String toString() { + return this.getName() + " " + Util.decodeBits(this.lineStateMask, Util.LINE_STATE_BITS); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseLineStateMask(this); + } + + public int getLineStateMask() { + return this.lineStateMask; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ModemStateMaskCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ModemStateMaskCommand.java new file mode 100644 index 00000000..820b248e --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ModemStateMaskCommand.java @@ -0,0 +1,75 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ModemStateMaskCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_MODEMSTATE_MASK; + +/** + * RFC 2217 {@code SET-MODEMSTATE-MASK} command. + * + * @see RFC 2217 + */ +public class ModemStateMaskCommand extends ComPortCommand { + + private int modemStateMask; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_MODEMSTATE_MASK} (client or server) + */ + public ModemStateMaskCommand(int[] bytes) { + super("SET-MODEMSTATE-MASK", SET_MODEMSTATE_MASK, bytes); + this.modemStateMask = bytes[2]; + } + + /** + * Encoding constructor. + * + * @param modemStateMask modem state mask value + * @param client true for the client-to-server command, false for the server-to-client command + */ + public ModemStateMaskCommand(boolean client, int modemStateMask) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_MODEMSTATE_MASK : SET_MODEMSTATE_MASK + SERVER_OFFSET, + modemStateMask + }); + } + + @Override + public String toString() { + return this.getName() + " " + Util.decodeBits(this.modemStateMask, Util.MODEM_STATE_BITS); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseModemStateMask(this); + } + + public int getModemStateMask() { + return this.modemStateMask; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyLineStateCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyLineStateCommand.java new file mode 100644 index 00000000..4609b723 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyLineStateCommand.java @@ -0,0 +1,75 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: NotifyLineStateCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.NOTIFY_LINESTATE; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * RFC 2217 {@code NOTIFY-LINESTATE} command. + * + * @see RFC 2217 + */ +public class NotifyLineStateCommand extends ComPortCommand { + + private int lineState; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#NOTIFY_LINESTATE} (client or server) + */ + public NotifyLineStateCommand(int[] bytes) { + super("NOTIFY-LINESTATE", NOTIFY_LINESTATE, bytes); + this.lineState = bytes[2]; + } + + /** + * Encoding constructor. + * + * @param lineState line state value + * @param client true for the client-to-server command, false for the server-to-client command + */ + public NotifyLineStateCommand(boolean client, int lineState) { + this(new int[] { + COM_PORT_OPTION, + client ? NOTIFY_LINESTATE : NOTIFY_LINESTATE + SERVER_OFFSET, + lineState + }); + } + + @Override + public String toString() { + return this.getName() + " " + Util.decodeBits(this.lineState, Util.LINE_STATE_BITS); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseNotifyLineState(this); + } + + public int getLineState() { + return this.lineState; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyModemStateCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyModemStateCommand.java new file mode 100644 index 00000000..801c15f8 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/NotifyModemStateCommand.java @@ -0,0 +1,75 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: NotifyModemStateCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.NOTIFY_MODEMSTATE; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * RFC 2217 {@code NOTIFY-MODEMSTATE} command. + * + * @see RFC 2217 + */ +public class NotifyModemStateCommand extends ComPortCommand { + + private int modemState; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#NOTIFY_MODEMSTATE} (client or server) + */ + public NotifyModemStateCommand(int[] bytes) { + super("NOTIFY-MODEMSTATE", NOTIFY_MODEMSTATE, bytes); + this.modemState = bytes[2]; + } + + /** + * Encoding constructor. + * + * @param modemState modem state value + * @param client true for the client-to-server command, false for the server-to-client command + */ + public NotifyModemStateCommand(boolean client, int modemState) { + this(new int[] { + COM_PORT_OPTION, + client ? NOTIFY_MODEMSTATE : NOTIFY_MODEMSTATE + SERVER_OFFSET, + modemState + }); + } + + @Override + public String toString() { + return this.getName() + " " + Util.decodeBits(this.modemState, Util.MODEM_STATE_BITS); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseNotifyModemState(this); + } + + public int getModemState() { + return this.modemState; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/ParityCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/ParityCommand.java new file mode 100644 index 00000000..c611ca16 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/ParityCommand.java @@ -0,0 +1,118 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: ParityCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.PARITY_EVEN; +import static gnu.io.rfc2217.RFC2217.PARITY_MARK; +import static gnu.io.rfc2217.RFC2217.PARITY_NONE; +import static gnu.io.rfc2217.RFC2217.PARITY_ODD; +import static gnu.io.rfc2217.RFC2217.PARITY_REQUEST; +import static gnu.io.rfc2217.RFC2217.PARITY_SPACE; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_PARITY; + +/** + * RFC 2217 {@code SET-PARITY} command. + * + * @see RFC 2217 + */ +public class ParityCommand extends ComPortCommand { + + private int parity; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_PARITY} (client or server) + * @throws IllegalArgumentException if {@code bytes[2]} is not a valid RFC 2217 parity value + */ + public ParityCommand(int[] bytes) { + super("SET-PARITY", SET_PARITY, bytes); + this.parity = bytes[2]; + switch (this.parity) { + case PARITY_REQUEST: + case PARITY_NONE: + case PARITY_ODD: + case PARITY_EVEN: + case PARITY_MARK: + case PARITY_SPACE: + break; + default: + throw new IllegalArgumentException("invalid parity value " + this.parity); + } + } + + /** + * Encoding constructor. + * + * @param parity parity value + * @param client true for the client-to-server command, false for the server-to-client command + * @throws IllegalArgumentException if {@code parity} is not a valid RFC 2217 parity value + */ + public ParityCommand(boolean client, int parity) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_PARITY : SET_PARITY + SERVER_OFFSET, + parity + }); + } + + @Override + public String toString() { + String desc; + switch (this.parity) { + case PARITY_REQUEST: + desc = "REQUEST"; + break; + case PARITY_NONE: + desc = "NONE"; + break; + case PARITY_ODD: + desc = "ODD"; + break; + case PARITY_EVEN: + desc = "EVEN"; + break; + case PARITY_MARK: + desc = "MARK"; + break; + case PARITY_SPACE: + desc = "SPACE"; + break; + default: + desc = "?"; + break; + } + return this.getName() + " " + desc; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseParity(this); + } + + public int getParity() { + return this.parity; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/PurgeDataCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/PurgeDataCommand.java new file mode 100644 index 00000000..98b85deb --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/PurgeDataCommand.java @@ -0,0 +1,107 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: PurgeDataCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.PURGE_DATA; +import static gnu.io.rfc2217.RFC2217.PURGE_DATA_BOTH_DATA_BUFFERS; +import static gnu.io.rfc2217.RFC2217.PURGE_DATA_RECEIVE_DATA_BUFFER; +import static gnu.io.rfc2217.RFC2217.PURGE_DATA_TRANSMIT_DATA_BUFFER; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; + +/** + * RFC 2217 {@code PURGE-DATA} command. + * + * @see RFC 2217 + */ +public class PurgeDataCommand extends ComPortCommand { + + private int purgeData; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#PURGE_DATA} (client or server) + * @throws IllegalArgumentException if {@code bytes[2]} is not a valid RFC 2217 purge data value + */ + public PurgeDataCommand(int[] bytes) { + super("PURGE-DATA", PURGE_DATA, bytes); + this.purgeData = bytes[2]; + switch (this.purgeData) { + case PURGE_DATA_RECEIVE_DATA_BUFFER: + case PURGE_DATA_TRANSMIT_DATA_BUFFER: + case PURGE_DATA_BOTH_DATA_BUFFERS: + break; + default: + throw new IllegalArgumentException("invalid purge data value " + this.purgeData); + } + } + + /** + * Encoding constructor. + * + * @param purgeData purge data value + * @param client true for the client-to-server command, false for the server-to-client command + * @throws IllegalArgumentException if {@code purgeData} is not a valid RFC 2217 purge data value + */ + public PurgeDataCommand(boolean client, int purgeData) { + this(new int[] { + COM_PORT_OPTION, + client ? PURGE_DATA : PURGE_DATA + SERVER_OFFSET, + purgeData + }); + } + + @Override + public String toString() { + String desc; + switch (this.purgeData) { + case PURGE_DATA_RECEIVE_DATA_BUFFER: + desc = "RECEIVE-DATA-BUFFER"; + break; + case PURGE_DATA_TRANSMIT_DATA_BUFFER: + desc = "TRANSMIT-DATA-BUFFER"; + break; + case PURGE_DATA_BOTH_DATA_BUFFERS: + desc = "BOTH-DATA-BUFFERS"; + break; + default: + desc = "?"; + break; + } + return this.getName() + " " + desc; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.casePurgeData(this); + } + + public boolean isPurgeReceiveDataBuffer() { + return (this.purgeData & PURGE_DATA_RECEIVE_DATA_BUFFER) != 0; + } + + public boolean isPurgeTransmitDataBuffer() { + return (this.purgeData & PURGE_DATA_TRANSMIT_DATA_BUFFER) != 0; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/RFC2217.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/RFC2217.java new file mode 100644 index 00000000..d955de93 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/RFC2217.java @@ -0,0 +1,164 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: RFC2217.java 29 2010-11-22 21:57:58Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + + + +/** + * RFC 2217 constants and utility methods. + * + * @see RFC 2217 + */ +public final class RFC2217 { + + // COM-PORT-OPTION telnet option + public static final int COM_PORT_OPTION = 44; + + // COM-PORT-OPTION commands + public static final int SIGNATURE = 0; + public static final int SET_BAUDRATE = 1; + public static final int SET_DATASIZE = 2; + public static final int SET_PARITY = 3; + public static final int SET_STOPSIZE = 4; + public static final int SET_CONTROL = 5; + public static final int NOTIFY_LINESTATE = 6; + public static final int NOTIFY_MODEMSTATE = 7; + public static final int FLOWCONTROL_SUSPEND = 8; + public static final int FLOWCONTROL_RESUME = 9; + public static final int SET_LINESTATE_MASK = 10; + public static final int SET_MODEMSTATE_MASK = 11; + public static final int PURGE_DATA = 12; + public static final int SERVER_OFFSET = 100; + + // SET_DATASIZE values + public static final int DATASIZE_REQUEST = 0; + public static final int DATASIZE_5 = 5; + public static final int DATASIZE_6 = 6; + public static final int DATASIZE_7 = 7; + public static final int DATASIZE_8 = 8; + + // SET_PARITY values + public static final int PARITY_REQUEST = 0; + public static final int PARITY_NONE = 1; + public static final int PARITY_ODD = 2; + public static final int PARITY_EVEN = 3; + public static final int PARITY_MARK = 4; + public static final int PARITY_SPACE = 5; + + // SET_STOPSIZE values + public static final int STOPSIZE_REQUEST = 0; + public static final int STOPSIZE_1 = 1; + public static final int STOPSIZE_2 = 2; + public static final int STOPSIZE_1_5 = 3; + + // SET_CONTROL values + public static final int CONTROL_OUTBOUND_FLOW_REQUEST = 0; + public static final int CONTROL_OUTBOUND_FLOW_NONE = 1; + public static final int CONTROL_OUTBOUND_FLOW_XON_XOFF = 2; + public static final int CONTROL_OUTBOUND_FLOW_HARDWARE = 3; + public static final int CONTROL_BREAK_REQUEST = 4; + public static final int CONTROL_BREAK_ON = 5; + public static final int CONTROL_BREAK_OFF = 6; + public static final int CONTROL_DTR_REQUEST = 7; + public static final int CONTROL_DTR_ON = 8; + public static final int CONTROL_DTR_OFF = 9; + public static final int CONTROL_RTS_REQUEST = 10; + public static final int CONTROL_RTS_ON = 11; + public static final int CONTROL_RTS_OFF = 12; + public static final int CONTROL_INBOUND_FLOW_REQUEST = 13; + public static final int CONTROL_INBOUND_FLOW_NONE = 14; + public static final int CONTROL_INBOUND_FLOW_XON_XOFF = 15; + public static final int CONTROL_INBOUND_FLOW_HARDWARE = 16; + public static final int CONTROL_OUTBOUND_FLOW_DCD = 17; + public static final int CONTROL_INBOUND_FLOW_DTR = 18; + public static final int CONTROL_OUTBOUND_FLOW_DSR = 19; + + // SET_LINESTATE_MASK bit values + public static final int LINESTATE_TIME_OUT = 0x80; + public static final int LINESTATE_TRANSFER_SHIFT_REGISTER_EMPTY = 0x40; + public static final int LINESTATE_TRANSFER_HOLDING_REGISTER_EMPTY = 0x20; + public static final int LINESTATE_BREAK_DETECT = 0x10; + public static final int LINESTATE_FRAMING_ERROR = 0x08; + public static final int LINESTATE_PARITY_ERROR = 0x04; + public static final int LINESTATE_OVERRUN_ERROR = 0x02; + public static final int LINESTATE_DATA_READY = 0x01; + + // SET_MODEMSTATE_MASK bit values + public static final int MODEMSTATE_CARRIER_DETECT = 0x80; + public static final int MODEMSTATE_RING_INDICATOR = 0x40; + public static final int MODEMSTATE_DSR = 0x20; + public static final int MODEMSTATE_CTS = 0x10; + public static final int MODEMSTATE_DELTA_CARRIER_DETECT = 0x08; + public static final int MODEMSTATE_TRAILING_EDGE_RING_DETECTOR = 0x04; + public static final int MODEMSTATE_DELTA_DSR = 0x02; + public static final int MODEMSTATE_DELTA_CTS = 0x01; + + // PURGE_DATA values + public static final int PURGE_DATA_RECEIVE_DATA_BUFFER = 0x01; + public static final int PURGE_DATA_TRANSMIT_DATA_BUFFER = 0x02; + public static final int PURGE_DATA_BOTH_DATA_BUFFERS = 0x03; + + private RFC2217() { + } + + /** + * Decode an RFC 2217 {@code COM-PORT-OPTION} command. + * + * @throws IllegalArgumentException if the bytes are not a valid encoded RFC 2217 {@link #COM_PORT_OPTION} + */ + public static ComPortCommand decodeComPortCommand(int[] bytes) { + if (bytes.length < 2) + throw new IllegalArgumentException("length < 2"); + if (bytes[0] != COM_PORT_OPTION) + throw new IllegalArgumentException("not a COM-PORT-OPTION (option = " + bytes[0] + ")"); + switch (bytes[1]) { + case SIGNATURE: + case SIGNATURE + SERVER_OFFSET: + return new SignatureCommand(bytes); + case SET_BAUDRATE: + case SET_BAUDRATE + SERVER_OFFSET: + return new BaudRateCommand(bytes); + case SET_DATASIZE: + case SET_DATASIZE + SERVER_OFFSET: + return new DataSizeCommand(bytes); + case SET_PARITY: + case SET_PARITY + SERVER_OFFSET: + return new ParityCommand(bytes); + case SET_STOPSIZE: + case SET_STOPSIZE + SERVER_OFFSET: + return new StopSizeCommand(bytes); + case SET_CONTROL: + case SET_CONTROL + SERVER_OFFSET: + return new ControlCommand(bytes); + case NOTIFY_LINESTATE: + case NOTIFY_LINESTATE + SERVER_OFFSET: + return new NotifyLineStateCommand(bytes); + case NOTIFY_MODEMSTATE: + case NOTIFY_MODEMSTATE + SERVER_OFFSET: + return new NotifyModemStateCommand(bytes); + case FLOWCONTROL_SUSPEND: + case FLOWCONTROL_SUSPEND + SERVER_OFFSET: + return new FlowControlSuspendCommand(bytes); + case FLOWCONTROL_RESUME: + case FLOWCONTROL_RESUME + SERVER_OFFSET: + return new FlowControlResumeCommand(bytes); + case SET_LINESTATE_MASK: + case SET_LINESTATE_MASK + SERVER_OFFSET: + return new LineStateMaskCommand(bytes); + case SET_MODEMSTATE_MASK: + case SET_MODEMSTATE_MASK + SERVER_OFFSET: + return new ModemStateMaskCommand(bytes); + case PURGE_DATA: + case PURGE_DATA + SERVER_OFFSET: + return new PurgeDataCommand(bytes); + default: + throw new IllegalArgumentException("unrecognized COM-PORT-OPTION command " + bytes[1]); + } + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/SignatureCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/SignatureCommand.java new file mode 100644 index 00000000..56f97ba9 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/SignatureCommand.java @@ -0,0 +1,113 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: SignatureCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import java.io.UnsupportedEncodingException; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SIGNATURE; + +/** + * RFC 2217 {@code SIGNATURE} command. + * + * @see RFC 2217 + */ +public class SignatureCommand extends ComPortCommand { + + public static final String ENCODING = "ISO-8859-1"; + + private final String signature; + private boolean decodeFailed; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length that is too short or too long + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SIGNATURE} (client or server) + */ + public SignatureCommand(int[] bytes) { + super("SIGNATURE", SIGNATURE, bytes); + String sig; + boolean failed = false; + try { + sig = new String(this.getPayload(), ENCODING); + } catch (UnsupportedEncodingException e) { + sig = "(string decode failed)"; + failed = true; + } + this.signature = sig; + this.decodeFailed = failed; + } + + /** + * Encoding constructor. + * + * @param signature signature string + * @param client true for the client-to-server command, false for the server-to-client command + */ + public SignatureCommand(boolean client, String signature) { + this(encode(client, signature)); + } + + /** + * Encoding constructor for signature requests. + * + * @param client true for the client command, false for the server command + */ + public SignatureCommand(boolean client) { + this(encode(client, "")); + } + + @Override + public String toString() { + return this.getName() + " " + (this.signature.length() > 0 ? "\"" + this.signature + "\"" : "REQUEST"); + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseSignature(this); + } + + public String getSignature() { + return this.signature; + } + + public boolean decodeFailed() { + return this.decodeFailed; + } + + @Override + int getMinPayloadLength() { + return 0; + } + + @Override + int getMaxPayloadLength() { + return Integer.MAX_VALUE - 2; + } + + private static int[] encode(boolean client, String signature) { + byte[] buf; + try { + buf = signature.getBytes(ENCODING); + } catch (UnsupportedEncodingException e) { + buf = new byte[] { (byte)'?' }; + } + int[] ibuf = new int[2 + buf.length]; + ibuf[0] = COM_PORT_OPTION; + ibuf[1] = client ? SIGNATURE : SIGNATURE + SERVER_OFFSET; + for (int i = 0; i < buf.length; i++) + ibuf[2 + i] = buf[i] & 0xff; + return ibuf; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/StopSizeCommand.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/StopSizeCommand.java new file mode 100644 index 00000000..08b99ead --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/StopSizeCommand.java @@ -0,0 +1,108 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: StopSizeCommand.java 39 2011-03-22 17:21:53Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import static gnu.io.rfc2217.RFC2217.COM_PORT_OPTION; +import static gnu.io.rfc2217.RFC2217.SERVER_OFFSET; +import static gnu.io.rfc2217.RFC2217.SET_STOPSIZE; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_1; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_1_5; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_2; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_REQUEST; + +/** + * RFC 2217 {@code SET-STOPSIZE} command. + * + * @see RFC 2217 + */ +public class StopSizeCommand extends ComPortCommand { + + private int stopSize; + + /** + * Decoding constructor. + * + * @param bytes encoded option starting with the {@code COM-PORT-OPTION} byte + * @throws NullPointerException if {@code bytes} is null + * @throws IllegalArgumentException if {@code bytes} has length != 3 + * @throws IllegalArgumentException if {@code bytes[0]} is not {@link RFC2217#COM_PORT_OPTION} + * @throws IllegalArgumentException if {@code bytes[1]} is not {@link RFC2217#SET_STOPSIZE} (client or server) + * @throws IllegalArgumentException if {@code bytes[2]} is not a valid RFC 2217 stop size value + */ + public StopSizeCommand(int[] bytes) { + super("SET-STOPSIZE", SET_STOPSIZE, bytes); + this.stopSize = bytes[2]; + switch (this.stopSize) { + case STOPSIZE_REQUEST: + case STOPSIZE_1: + case STOPSIZE_2: + case STOPSIZE_1_5: + break; + default: + throw new IllegalArgumentException("invalid stop size value " + this.stopSize); + } + } + + /** + * Encoding constructor. + * + * @param stopSize stop size value + * @param client true for the client-to-server command, false for the server-to-client command + * @throws IllegalArgumentException if {@code stopSize} is not a valid RFC 2217 stop size value + */ + public StopSizeCommand(boolean client, int stopSize) { + this(new int[] { + COM_PORT_OPTION, + client ? SET_STOPSIZE : SET_STOPSIZE + SERVER_OFFSET, + stopSize + }); + } + + @Override + public String toString() { + String desc; + switch (this.stopSize) { + case STOPSIZE_REQUEST: + desc = "REQUEST"; + break; + case STOPSIZE_1: + desc = "1"; + break; + case STOPSIZE_2: + desc = "2"; + break; + case STOPSIZE_1_5: + desc = "1.5"; + break; + default: + desc = "?"; + break; + } + return this.getName() + " " + desc; + } + + @Override + public void visit(ComPortCommandSwitch sw) { + sw.caseStopSize(this); + } + + public int getStopSize() { + return this.stopSize; + } + + @Override + int getMinPayloadLength() { + return 1; + } + + @Override + int getMaxPayloadLength() { + return 1; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/TelnetSerialPort.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/TelnetSerialPort.java new file mode 100644 index 00000000..402b4490 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/TelnetSerialPort.java @@ -0,0 +1,1088 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: TelnetSerialPort.java 48 2012-03-18 20:54:33Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import gnu.io.SerialPort; +import gnu.io.SerialPortEvent; +import gnu.io.SerialPortEventListener; +import gnu.io.UnsupportedCommOperationException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.TooManyListenersException; + +import org.apache.commons.net.telnet.EchoOptionHandler; +import org.apache.commons.net.telnet.InvalidTelnetOptionException; +import org.apache.commons.net.telnet.SuppressGAOptionHandler; +import org.apache.commons.net.telnet.TelnetClient; +import org.apache.commons.net.telnet.TelnetInputListener; +import org.apache.commons.net.telnet.TerminalTypeOptionHandler; + +import static gnu.io.rfc2217.RFC2217.CONTROL_BREAK_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_BREAK_ON; +import static gnu.io.rfc2217.RFC2217.CONTROL_DTR_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_DTR_ON; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_HARDWARE; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_NONE; +import static gnu.io.rfc2217.RFC2217.CONTROL_INBOUND_FLOW_XON_XOFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_HARDWARE; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_NONE; +import static gnu.io.rfc2217.RFC2217.CONTROL_OUTBOUND_FLOW_XON_XOFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_RTS_OFF; +import static gnu.io.rfc2217.RFC2217.CONTROL_RTS_ON; +import static gnu.io.rfc2217.RFC2217.DATASIZE_5; +import static gnu.io.rfc2217.RFC2217.DATASIZE_6; +import static gnu.io.rfc2217.RFC2217.DATASIZE_7; +import static gnu.io.rfc2217.RFC2217.DATASIZE_8; +import static gnu.io.rfc2217.RFC2217.LINESTATE_BREAK_DETECT; +import static gnu.io.rfc2217.RFC2217.LINESTATE_DATA_READY; +import static gnu.io.rfc2217.RFC2217.LINESTATE_FRAMING_ERROR; +import static gnu.io.rfc2217.RFC2217.LINESTATE_OVERRUN_ERROR; +import static gnu.io.rfc2217.RFC2217.LINESTATE_PARITY_ERROR; +import static gnu.io.rfc2217.RFC2217.LINESTATE_TRANSFER_SHIFT_REGISTER_EMPTY; +import static gnu.io.rfc2217.RFC2217.MODEMSTATE_CARRIER_DETECT; +import static gnu.io.rfc2217.RFC2217.MODEMSTATE_CTS; +import static gnu.io.rfc2217.RFC2217.MODEMSTATE_DSR; +import static gnu.io.rfc2217.RFC2217.MODEMSTATE_RING_INDICATOR; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_1; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_1_5; +import static gnu.io.rfc2217.RFC2217.STOPSIZE_2; + +/** + * Implements the client side of the RFC 2217 + * serial-over-Telnet protocol as as {@link SerialPort}. + * + *

+ * This class extends the {@link SerialPort} class and functions in the same way, however, there are + * a couple of differences to be aware of: + *

+ *

+ * + *

+ * The following optional functionality is not implemented and/or inappropriate for a networked connection: + *

+ *

+ * + *

+ * In addition, access servers typically don't support {@link #notifyOnOutputEmpty}. + *

+ * + *

+ * Finally, {@link #sendBreak} is supported but the {@code millis} argument is ignored, as timing cannot be + * assured over a TCP connection. Access servers typically enforce a fixed break time. + *

+ * + * @see SerialPort + * @see RFC 2217 + */ +public class TelnetSerialPort extends SerialPort { + + private static final int DEFAULT_BAUD_RATE = 9600; + + private static final String DEFAULT_TERMINAL_TYPE = "VT100"; + + // Modem state bits we always want the server to report to us regardless of what listener wants. + // This is so we can always stay up-to-date with their values in case isCD(), etc. is invoked. + private static final int MODEMSTATE_ALWAYS_MONITOR + = MODEMSTATE_CARRIER_DETECT | MODEMSTATE_RING_INDICATOR | MODEMSTATE_DSR | MODEMSTATE_CTS; + + // Line state bits we never want the server to report to us regardless of what listener wants; internally, + // we use the LINESTATE_DATA_READY bit only to indicate the listener wants DATA_AVAILABLE notifications. + private static final int LINESTATE_NEVER_MONITOR = LINESTATE_DATA_READY; + + // States + private enum State { + INITIAL(false, false), + ESTABLISHED(true, false), + CLOSED(false, true); + + private final boolean established; + private final boolean closed; + + private State(boolean established, boolean closed) { + this.established = established; + this.closed = closed; + } + + public void checkNotClosed() { + if (this.closed) + throw new IllegalStateException("port is closed"); + } + + public boolean isEstablished() { + return this.established; + } + } + + private final TelnetClient telnetClient; + + private String name; + private String signature; + private State state; + private SerialPortEventListener listener; + + private int baudRate = DEFAULT_BAUD_RATE; + private int dataSize = DATASIZE_8; + private int flowControlInbound = CONTROL_INBOUND_FLOW_NONE; + private int flowControlOutbound = CONTROL_OUTBOUND_FLOW_NONE; + private int parity = RFC2217.PARITY_NONE; + private int stopSize = STOPSIZE_1; + + private boolean cd; + private boolean cts; + private boolean dsr; + private boolean dtr; + private boolean ri; + private boolean rts; + + private int lineStateNotify; // which line state changes we notify listener about + private int lineStateMask; // which line state changes access server notifies us about + private int lineStateLast; // most recent line state rec'd from access server + private int modemStateNotify; // which modem state changes we notify listener about + private int modemStateMask = MODEMSTATE_ALWAYS_MONITOR; // which modem state changes access server notifies us about + private int modemStateLast; // most recent modem state rec'd from access server + + /** + * Constructor. + */ + public TelnetSerialPort() { + this.state = State.INITIAL; + this.name = getClass().getSimpleName(); + this.signature = "jvser v" + Version.JVSER_VERSION; + this.telnetClient = this.createTelnetClient(); + this.telnetClient.registerInputListener(new TelnetInputListener() { + + @Override + public void telnetInputAvailable() { + boolean notify; + synchronized (TelnetSerialPort.this) { + notify = (TelnetSerialPort.this.lineStateNotify & LINESTATE_DATA_READY) != 0; + } + if (notify) + TelnetSerialPort.this.sendEvent(SerialPortEvent.DATA_AVAILABLE); + } + }); + } + + /** + * Get the descriptive name of this client (used for logging purposes). + */ + @Override + public String getName() { + return this.name; + } + + /** + * Set the descriptive name of this client (used for logging purposes). + */ + public void setName(String name) { + this.name = name; + } + + /** + * Get the signature sent to the remote server at connection time. + * By default, the signature is the name of this class. + */ + public String getSignature() { + return this.signature; + } + + /** + * Set the signature sent to the remote server at connection time. + * + * @param signature signature string, or {@code null} (or empty string) to not send a signature + */ + public void setSignature(String signature) { + this.signature = signature; + } + + /** + * Get the {@link TelnetClient} associated with this instance. + */ + public TelnetClient getTelnetClient() { + return this.telnetClient; + } + + /** + * Construct and configure the {@link TelnetClient} to be used for this instance. + */ + protected TelnetClient createTelnetClient() { + TelnetClient tc = new TelnetClient(DEFAULT_TERMINAL_TYPE); + tc.setReaderThread(true); // allows immediate option negotiation + try { + tc.addOptionHandler(new TerminalTypeOptionHandler(DEFAULT_TERMINAL_TYPE, false, false, true, false)); + tc.addOptionHandler(new EchoOptionHandler(false, false, false, false)); + tc.addOptionHandler(new SuppressGAOptionHandler(true, true, true, true)); + tc.addOptionHandler(new TransmitBinaryOptionHandler(true, true, true, true)); + tc.addOptionHandler(new ComPortOptionHandler(this)); + } catch (IOException e) { + throw new RuntimeException("unexpected exception", e); + } catch (InvalidTelnetOptionException e) { + throw new RuntimeException("unexpected exception", e); + } + return tc; + } + + // We wrap the telnet port's InputStream in a NotifyInputStream so we can detect when there + // is new data available to be read. It would be nice if the TelnetClient provided a way to + // notify us directly, but it doesn't, so we have to use this hack. + @Override + public synchronized InputStream getInputStream() throws IOException { + this.state.checkNotClosed(); + return this.telnetClient.getInputStream(); + } + + @Override + public synchronized OutputStream getOutputStream() throws IOException { + this.state.checkNotClosed(); + return this.telnetClient.getOutputStream(); + } + + @Override + public synchronized void close() { + if (this.state == State.CLOSED) + return; + this.state = State.CLOSED; + try { + this.telnetClient.disconnect(); + } catch (IOException e) { + // + } + } + + @Override + public synchronized int getBaudRate() { + this.state.checkNotClosed(); + return this.baudRate; + } + + @Override + public synchronized int getDataBits() { + this.state.checkNotClosed(); + switch (this.dataSize) { + case DATASIZE_5: + return DATABITS_5; + case DATASIZE_6: + return DATABITS_6; + case DATASIZE_7: + return DATABITS_7; + case DATASIZE_8: + return DATABITS_8; + default: + throw new RuntimeException("impossible case"); + } + } + + @Override + public synchronized int getStopBits() { + this.state.checkNotClosed(); + switch (this.stopSize) { + case STOPSIZE_1: + return STOPBITS_1; + case STOPSIZE_2: + return STOPBITS_2; + case STOPSIZE_1_5: + return STOPBITS_1_5; + default: + throw new RuntimeException("impossible case"); + } + } + + @Override + public synchronized int getParity() { + this.state.checkNotClosed(); + switch (this.parity) { + case RFC2217.PARITY_NONE: + return SerialPort.PARITY_NONE; + case RFC2217.PARITY_ODD: + return SerialPort.PARITY_ODD; + case RFC2217.PARITY_EVEN: + return SerialPort.PARITY_EVEN; + case RFC2217.PARITY_MARK: + return SerialPort.PARITY_MARK; + case RFC2217.PARITY_SPACE: + return SerialPort.PARITY_SPACE; + default: + throw new RuntimeException("impossible case"); + } + } + + @Override + public void sendBreak(int millis) { + CommandList commandList = new CommandList(2); + synchronized (this) { + this.state.checkNotClosed(); + if (this.state != State.ESTABLISHED) + return; + commandList.add(new ControlCommand(true, CONTROL_BREAK_ON)); + commandList.add(new ControlCommand(true, CONTROL_BREAK_OFF)); + } + commandList.send(); + } + + @Override + public void setFlowControlMode(int flowControl) throws UnsupportedCommOperationException { + + // Validate bit combination + if ((flowControl & (FLOWCONTROL_RTSCTS_OUT | FLOWCONTROL_XONXOFF_OUT)) == (FLOWCONTROL_RTSCTS_OUT | FLOWCONTROL_XONXOFF_OUT) + || (flowControl & (FLOWCONTROL_RTSCTS_IN | FLOWCONTROL_XONXOFF_IN)) == (FLOWCONTROL_RTSCTS_IN | FLOWCONTROL_XONXOFF_IN)) + throw new UnsupportedCommOperationException("invalid flow control value " + flowControl); + + // Apply changes + CommandList commandList = new CommandList(2); + synchronized (this) { + this.state.checkNotClosed(); + + // Convert to RFC 2217 values + int previousFlowControlOutbound = this.flowControlOutbound; + int previousFlowControlInbound = this.flowControlInbound; + this.flowControlOutbound = (flowControl & FLOWCONTROL_RTSCTS_OUT) != 0 ? CONTROL_OUTBOUND_FLOW_HARDWARE : + (flowControl & FLOWCONTROL_XONXOFF_OUT) != 0 ? CONTROL_OUTBOUND_FLOW_XON_XOFF : CONTROL_OUTBOUND_FLOW_NONE; + this.flowControlInbound = (flowControl & FLOWCONTROL_RTSCTS_IN) != 0 ? CONTROL_INBOUND_FLOW_HARDWARE : + (flowControl & FLOWCONTROL_XONXOFF_IN) != 0 ? CONTROL_INBOUND_FLOW_XON_XOFF : CONTROL_INBOUND_FLOW_NONE; + + // Update server (outbound first per RFC 2217) + if (this.flowControlOutbound != previousFlowControlOutbound && this.state.isEstablished()) + commandList.add(new ControlCommand(true, this.flowControlOutbound)); + if (this.flowControlInbound != previousFlowControlInbound && this.state.isEstablished()) + commandList.add(new ControlCommand(true, this.flowControlInbound)); + } + commandList.send(); + } + + @Override + public synchronized int getFlowControlMode() { + this.state.checkNotClosed(); + int value = FLOWCONTROL_NONE; + switch (this.flowControlOutbound) { + case CONTROL_OUTBOUND_FLOW_HARDWARE: + value |= FLOWCONTROL_RTSCTS_OUT; + break; + case CONTROL_OUTBOUND_FLOW_XON_XOFF: + value |= FLOWCONTROL_XONXOFF_OUT; + break; + default: + break; + } + switch (this.flowControlInbound) { + case CONTROL_INBOUND_FLOW_HARDWARE: + value |= FLOWCONTROL_RTSCTS_IN; + break; + case CONTROL_INBOUND_FLOW_XON_XOFF: + value |= FLOWCONTROL_XONXOFF_IN; + break; + default: + break; + } + return value; + } + + @Override + public void setSerialPortParams(int baudRate, int dataBits, int stopBits, int parity) + throws UnsupportedCommOperationException { + CommandList commandList = new CommandList(4); + synchronized (this) { + this.state.checkNotClosed(); + + // Validate parameters and convert to RFC 2217 values + if (baudRate <= 0) + throw new UnsupportedCommOperationException("invalid baud rate " + baudRate); + switch (dataBits) { + case DATABITS_5: + dataBits = DATASIZE_5; + break; + case DATABITS_6: + dataBits = DATASIZE_6; + break; + case DATABITS_7: + dataBits = DATASIZE_7; + break; + case DATABITS_8: + dataBits = DATASIZE_8; + break; + default: + throw new UnsupportedCommOperationException("invalid data bits " + dataBits); + } + switch (stopBits) { + case STOPBITS_1: + stopBits = STOPSIZE_1; + break; + case STOPBITS_2: + stopBits = STOPSIZE_2; + break; + case STOPBITS_1_5: + stopBits = STOPSIZE_1_5; + break; + default: + throw new UnsupportedCommOperationException("invalid stop bits " + stopBits); + } + switch (parity) { + case SerialPort.PARITY_NONE: + parity = RFC2217.PARITY_NONE; + break; + case SerialPort.PARITY_ODD: + parity = RFC2217.PARITY_ODD; + break; + case SerialPort.PARITY_EVEN: + parity = RFC2217.PARITY_EVEN; + break; + case SerialPort.PARITY_MARK: + parity = RFC2217.PARITY_MARK; + break; + case SerialPort.PARITY_SPACE: + parity = RFC2217.PARITY_SPACE; + break; + default: + throw new UnsupportedCommOperationException("invalid parity " + parity); + } + + // Update my state + boolean changed = false; + if (this.baudRate != baudRate) { + this.baudRate = baudRate; + changed = true; + } + if (this.dataSize != dataBits) { + this.dataSize = dataBits; + changed = true; + } + if (this.stopSize != stopBits) { + this.stopSize = stopBits; + changed = true; + } + if (this.parity != parity) { + this.parity = parity; + changed = true; + } + + // Update access server if there was a change + if (changed && this.state.isEstablished()) + this.addSerialPortGeometry(commandList); + } + commandList.send(); + } + + @Override + public void setDTR(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.dtr != value) { + this.dtr = value; + if (this.state.isEstablished()) + commandList.add(new ControlCommand(true, this.dtr ? CONTROL_DTR_ON : CONTROL_DTR_OFF)); + } + } + commandList.send(); + } + + @Override + public synchronized boolean isDTR() { + this.state.checkNotClosed(); + return this.dtr; + } + + @Override + public void setRTS(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.rts != value) { + this.rts = value; + if (this.state.isEstablished()) + commandList.add(new ControlCommand(true, this.rts ? CONTROL_RTS_ON : CONTROL_RTS_OFF)); + } + } + commandList.send(); + } + + @Override + public synchronized boolean isRTS() { + this.state.checkNotClosed(); + return this.rts; + } + + @Override + public synchronized boolean isCTS() { + this.state.checkNotClosed(); + return this.cts; + } + + @Override + public synchronized boolean isDSR() { + this.state.checkNotClosed(); + return this.dsr; + } + + @Override + public synchronized boolean isRI() { + this.state.checkNotClosed(); + return this.ri; + } + + @Override + public synchronized boolean isCD() { + this.state.checkNotClosed(); + return this.cd; + } + + // This is invoked by the ComPortOptionHandler once the server has agreed to accept COM-PORT-OPTION subnegotiation commands + + void startSubnegotiation() { + CommandList commandList = new CommandList(12); + synchronized (this) { + + // Update state + this.state.checkNotClosed(); + this.state = State.ESTABLISHED; + + // Request signature from peer + commandList.add(new SignatureCommand(true)); + + // Send signature if desired + if (this.signature != null && this.signature.length() > 0) + commandList.add(new SignatureCommand(true, this.signature)); + + // Send all configuration information + this.addSerialPortGeometry(commandList); + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + commandList.add(new ModemStateMaskCommand(true, this.modemStateMask)); + commandList.add(new ControlCommand(true, this.flowControlInbound)); + commandList.add(new ControlCommand(true, this.flowControlOutbound)); + commandList.add(new ControlCommand(true, this.dtr ? CONTROL_DTR_ON : CONTROL_DTR_OFF)); + commandList.add(new ControlCommand(true, this.rts ? CONTROL_RTS_ON : CONTROL_RTS_OFF)); + } + commandList.send(); + } + + // Method to send serial port "geometry" in the order recommended by RFC 2217 (section 2) + + private void addSerialPortGeometry(CommandList commandList) { + commandList.add(new BaudRateCommand(true, this.baudRate)); + commandList.add(new DataSizeCommand(true, this.dataSize)); + commandList.add(new ParityCommand(true, this.parity)); + commandList.add(new StopSizeCommand(true, this.stopSize)); + } + + // This is invoked by the ComPortOptionHandler when we receive a command from the server + + void handleCommand(ComPortCommand command) { + + // Incoming commands should be server versions + if (!command.isServerCommand()) { + return; + } + + // Handle command + command.visit(new AbstractComPortCommandSwitch() { + + @Override + public void caseBaudRate(BaudRateCommand command) { + synchronized (TelnetSerialPort.this) { + TelnetSerialPort.this.baudRate = command.getBaudRate(); + } + } + + @Override + public void caseDataSize(DataSizeCommand command) { + synchronized (TelnetSerialPort.this) { + TelnetSerialPort.this.dataSize = command.getDataSize(); + } + } + + @Override + public void caseParity(ParityCommand command) { + synchronized (TelnetSerialPort.this) { + TelnetSerialPort.this.parity = command.getParity(); + } + } + + @Override + public void caseStopSize(StopSizeCommand command) { + synchronized (TelnetSerialPort.this) { + TelnetSerialPort.this.stopSize = command.getStopSize(); + } + } + + @Override + public void caseControl(ControlCommand command) { + synchronized (TelnetSerialPort.this) { + switch (command.getControl()) { + case CONTROL_OUTBOUND_FLOW_NONE: + case CONTROL_OUTBOUND_FLOW_XON_XOFF: + case CONTROL_OUTBOUND_FLOW_HARDWARE: + TelnetSerialPort.this.flowControlOutbound = command.getControl(); + break; + case CONTROL_INBOUND_FLOW_NONE: + case CONTROL_INBOUND_FLOW_XON_XOFF: + case CONTROL_INBOUND_FLOW_HARDWARE: + TelnetSerialPort.this.flowControlInbound = command.getControl(); + break; + case CONTROL_DTR_ON: + TelnetSerialPort.this.dtr = true; + break; + case CONTROL_DTR_OFF: + TelnetSerialPort.this.dtr = false; + break; + case CONTROL_RTS_ON: + TelnetSerialPort.this.rts = true; + break; + case CONTROL_RTS_OFF: + TelnetSerialPort.this.rts = false; + break; + default: + break; + } + } + } + + @Override + public void caseNotifyLineState(NotifyLineStateCommand command) { + int lineState = command.getLineState(); + int notify; + synchronized (TelnetSerialPort.this) { + notify = TelnetSerialPort.this.lineStateNotify; + TelnetSerialPort.this.lineStateLast = lineState; + } + notify &= lineState; // notify only if bit is equal to 1 + if ((notify & LINESTATE_TRANSFER_SHIFT_REGISTER_EMPTY) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.OUTPUT_BUFFER_EMPTY); + if ((notify & LINESTATE_BREAK_DETECT) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.BI); + if ((notify & LINESTATE_FRAMING_ERROR) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.FE); + if ((notify & LINESTATE_PARITY_ERROR) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.PE); + if ((notify & LINESTATE_OVERRUN_ERROR) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.OE); + } + + @Override + public void caseNotifyModemState(NotifyModemStateCommand command) { + int modemState = command.getModemState(); + int notify; + synchronized (TelnetSerialPort.this) { + notify = TelnetSerialPort.this.modemStateNotify; + TelnetSerialPort.this.modemStateLast = modemState; + } + notify &= modemState ^ modemStateLast; // notify only if bit has changed + if ((notify & MODEMSTATE_CARRIER_DETECT) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.CD, (modemState & MODEMSTATE_CARRIER_DETECT) != 0); + if ((notify & MODEMSTATE_RING_INDICATOR) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.RI, (modemState & MODEMSTATE_RING_INDICATOR) != 0); + if ((notify & MODEMSTATE_DSR) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.DSR, (modemState & MODEMSTATE_DSR) != 0); + if ((notify & MODEMSTATE_CTS) != 0) + TelnetSerialPort.this.sendEvent(SerialPortEvent.CTS, (modemState & MODEMSTATE_CTS) != 0); + } + + @Override + protected void caseDefault(ComPortCommand command) { + // + } + }); + } + + // Listener management + + @Override + public synchronized void addEventListener(SerialPortEventListener listener) throws TooManyListenersException { + this.state.checkNotClosed(); + if (this.listener != null) + throw new TooManyListenersException("only one listener allowed"); + this.listener = listener; + } + + @Override + public synchronized void removeEventListener() { + this.listener = null; + } + + // Notification configuration + + @Override + public synchronized void notifyOnDataAvailable(boolean value) { + this.state.checkNotClosed(); + updateLineStateMask(LINESTATE_DATA_READY, value); + } + + @Override + public void notifyOnOutputEmpty(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.updateLineStateMask(LINESTATE_TRANSFER_SHIFT_REGISTER_EMPTY, value) && this.state.isEstablished()) + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnCTS(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (updateModemStateMask(MODEMSTATE_CTS, value) && this.state.isEstablished()) + commandList.add(new ModemStateMaskCommand(true, this.modemStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnDSR(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (updateModemStateMask(MODEMSTATE_DSR, value) && this.state.isEstablished()) + commandList.add(new ModemStateMaskCommand(true, this.modemStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnRingIndicator(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (updateModemStateMask(MODEMSTATE_RING_INDICATOR, value) && this.state.isEstablished()) + commandList.add(new ModemStateMaskCommand(true, this.modemStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnCarrierDetect(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (updateModemStateMask(MODEMSTATE_CARRIER_DETECT, value) && this.state.isEstablished()) + commandList.add(new ModemStateMaskCommand(true, this.modemStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnOverrunError(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.updateLineStateMask(LINESTATE_OVERRUN_ERROR, value) && this.state.isEstablished()) + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnParityError(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.updateLineStateMask(LINESTATE_PARITY_ERROR, value) && this.state.isEstablished()) + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnFramingError(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.updateLineStateMask(LINESTATE_FRAMING_ERROR, value) && this.state.isEstablished()) + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + } + commandList.send(); + } + + @Override + public void notifyOnBreakInterrupt(boolean value) { + CommandList commandList = new CommandList(1); + synchronized (this) { + this.state.checkNotClosed(); + if (this.updateLineStateMask(LINESTATE_BREAK_DETECT, value) && this.state.isEstablished()) + commandList.add(new LineStateMaskCommand(true, this.lineStateMask)); + } + commandList.send(); + } + + // Methods for sending event notifications + + private void sendEvent(int type) { + this.sendEvent(type, true); + } + + private void sendEvent(int type, boolean newValue) { + SerialPortEventListener currentListener; + synchronized (this) { + currentListener = this.listener; + } + if (currentListener == null) + return; + SerialPortEvent event = new SerialPortEvent(this, type, !newValue, newValue); + try { + currentListener.serialEvent(event); + } catch (Exception e) { + System.err.println(this.name + ": exception from listener " + listener + ": " + e.getMessage()); + } + } + + // Internal utility methods + + // Send a subnegotiation to the peer + private void sendSubnegotiation(ComPortCommand command) { + assert !Thread.holdsLock(TelnetSerialPort.this); // otherwise we can deadlock + try { + this.telnetClient.sendSubnegotiation(command.getBytes()); + } catch (IOException e) { + System.err.println(this.name + ": exception sending subcommand: " + e.getMessage()); + } + } + + // Update line state notifications; return true if we need to send new mask to access server + private synchronized boolean updateLineStateMask(int bit, boolean value) { + int previous = this.lineStateMask; + if (value) { + this.lineStateNotify |= bit; + this.lineStateMask |= bit; + } else { + this.lineStateNotify &= ~bit; + this.lineStateMask &= ~bit; + } + this.lineStateMask &= ~LINESTATE_NEVER_MONITOR; + return this.lineStateMask != previous; + } + + // Update modem state notifications; return true if we need to send new mask to access server + private synchronized boolean updateModemStateMask(int bit, boolean value) { + int previous = this.modemStateMask; + if (value) { + this.modemStateNotify |= bit; + this.modemStateMask |= bit; + } else { + this.modemStateNotify &= ~bit; + this.modemStateMask &= ~bit; + } + this.modemStateMask |= MODEMSTATE_ALWAYS_MONITOR; + return this.modemStateMask != previous; + } + + // Unimplemented methods + + @Override + public synchronized void enableReceiveThreshold(int threshold) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public synchronized void disableReceiveThreshold() { + this.state.checkNotClosed(); + } + + @Override + public synchronized boolean isReceiveThresholdEnabled() { + this.state.checkNotClosed(); + return false; + } + + @Override + public synchronized int getReceiveThreshold() { + this.state.checkNotClosed(); + return 0; + } + + @Override + public synchronized void enableReceiveTimeout(int timeout) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public synchronized void disableReceiveTimeout() { + this.state.checkNotClosed(); + } + + @Override + public synchronized boolean isReceiveTimeoutEnabled() { + this.state.checkNotClosed(); + return false; + } + + @Override + public synchronized int getReceiveTimeout() { + this.state.checkNotClosed(); + return 0; + } + + @Override + public synchronized void enableReceiveFraming(int framingByte) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public synchronized void disableReceiveFraming() { + this.state.checkNotClosed(); + } + + @Override + public synchronized boolean isReceiveFramingEnabled() { + this.state.checkNotClosed(); + return false; + } + + @Override + public synchronized int getReceiveFramingByte() { + this.state.checkNotClosed(); + return 0; + } + + @Override + public synchronized void setInputBufferSize(int size) { + this.state.checkNotClosed(); + } + + @Override + public synchronized int getInputBufferSize() { + this.state.checkNotClosed(); + return 0; + } + + @Override + public synchronized void setOutputBufferSize(int size) { + this.state.checkNotClosed(); + } + + @Override + public synchronized int getOutputBufferSize() { + this.state.checkNotClosed(); + return 0; + } + + // Utility class + + @SuppressWarnings("serial") + private class CommandList extends ArrayList { + + public CommandList(int size) { + super(size); + } + + public void send() { + for (ComPortCommand command : this) + TelnetSerialPort.this.sendSubnegotiation(command); + this.clear(); + } + } + + @Override + public int getBaudBase() throws UnsupportedCommOperationException, IOException { + this.state.checkNotClosed(); + return baudRate; + } + + @Override + public boolean getCallOutHangup() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public int getDivisor() throws UnsupportedCommOperationException, IOException { + this.state.checkNotClosed(); + return 1; + } + + @Override + public byte getEndOfInputChar() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + return 0x04; + } + + @Override + public boolean getLowLatency() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public byte getParityErrorChar() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public String getUARTType() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public boolean setBaudBase(int arg0) throws UnsupportedCommOperationException, IOException { + this.state.checkNotClosed(); + return true; + } + + @Override + public boolean setCallOutHangup(boolean arg0) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public boolean setDivisor(int arg0) throws UnsupportedCommOperationException, IOException { + this.state.checkNotClosed(); + return true; + } + + @Override + public boolean setEndOfInputChar(byte arg0) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + return true; + } + + @Override + public boolean setLowLatency() throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public boolean setParityErrorChar(byte arg0) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } + + @Override + public boolean setUARTType(String arg0, boolean arg1) throws UnsupportedCommOperationException { + this.state.checkNotClosed(); + throw new UnsupportedCommOperationException(); + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/TransmitBinaryOptionHandler.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/TransmitBinaryOptionHandler.java new file mode 100644 index 00000000..a02f513c --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/TransmitBinaryOptionHandler.java @@ -0,0 +1,40 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: TransmitBinaryOptionHandler.java 46 2011-10-16 23:06:39Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import org.apache.commons.net.telnet.TelnetOptionHandler; + +/** + * Handler for the telnet {@code TRANSMIT-BINARY} option defined by RFC 856. + * + * @see RFC 856 + */ +public class TransmitBinaryOptionHandler extends TelnetOptionHandler { + + public static final int TRANSMIT_BINARY_OPTION = 0; + + public TransmitBinaryOptionHandler(boolean initlocal, boolean initremote, boolean acceptlocal, boolean acceptremote) { + super(TRANSMIT_BINARY_OPTION, initlocal, initremote, acceptlocal, acceptremote); + } + + @Override + public int[] answerSubnegotiation(int[] data, int length) { + return null; + } + + @Override + public int[] startSubnegotiationLocal() { + return null; + } + + @Override + public int[] startSubnegotiationRemote() { + return null; + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/Util.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/Util.java new file mode 100644 index 00000000..8f95d333 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/Util.java @@ -0,0 +1,70 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: Util.java 6 2010-11-20 23:37:06Z archie.cobbs $ + */ + +package gnu.io.rfc2217; + +import java.util.ArrayList; + +/** + * Utility methods. + */ +final class Util { + + static final String[] LINE_STATE_BITS = new String[] { + "TIME_OUT", + "TRANSFER_SHIFT_REGISTER_EMPTY", + "TRANSFER_HOLDING_REGISTER_EMPTY", + "BREAK_DETECT", + "FRAMING_ERROR", + "PARITY_ERROR", + "OVERRUN_ERROR", + "DATA_READY", + }; + + static final String[] MODEM_STATE_BITS = new String[] { + "CARRIER_DETECT", + "RING_INDICATOR", + "DSR", + "CTS", + "DELTA_CARRIER_DETECT", + "TRAILING_EDGE_RING_DETECTOR", + "DELTA_DSR", + "DELTA_CTS", + }; + + private Util() { + } + + static String decodeBits(int value, String[] names) { + ArrayList list = new ArrayList(8); + for (int i = 0; i < 8; i++) { + if ((value & (1 << (7 - i))) != 0) + list.add(names[i]); + } + if (list.isEmpty()) + return "(none)"; + names = list.toArray(new String[list.size()]); + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < names.length; i++) { + if (i > 0) + buf.append(' '); + buf.append(names[i]); + } + return buf.toString(); + } + + static String rawBytes(int[] data, int off, int len) { + StringBuilder buf = new StringBuilder(); + for (int i = 0; i < len; i++) { + if (i > 0) + buf.append(' '); + buf.append(String.format("0x%02x", data[off + i])); + } + return buf.toString(); + } +} + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/Version.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/Version.java new file mode 100644 index 00000000..3e6c7cd0 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/Version.java @@ -0,0 +1,48 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id$ + */ + +package gnu.io.rfc2217; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Contains library version information. + */ +public final class Version { + + /** + * The version of this library. + */ + public static final String JVSER_VERSION; + + private static final String PROPERTIES_RESOURCE = "/jvser.properties"; + private static final String VERSION_PROPERTY_NAME = "jvser.version"; + + static { + Properties properties = new Properties(); + InputStream input = Version.class.getResourceAsStream(PROPERTIES_RESOURCE); + if (input == null) + throw new RuntimeException("can't find resource " + PROPERTIES_RESOURCE); + try { + properties.load(input); + } catch (IOException e) { + throw new RuntimeException("unexpected exception", e); + } finally { + try { + input.close(); + } catch (IOException e) { + // ignore + } + } + JVSER_VERSION = properties.getProperty(VERSION_PROPERTY_NAME, "?"); + } + + private Version() { + } +} diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/overview.html b/nrjavaserial/src/main/java/gnu/io/rfc2217/overview.html new file mode 100644 index 00000000..82c42082 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/overview.html @@ -0,0 +1,29 @@ + + + + +

+This is the API documentation for version @VERSION@ of the jvser +library, which implements the client side of the RFC 2217 +serial-over-Telnet protocol. +

+ +

+RFC 2217 defines a TCP protcol for virtualizing +traditional serial ports over a telnet connection. This allows clients to connect to remote serial +lines using access servers that support this protocol. +

+ +

+This library provides the TelnetSerialPort class +for RFC 2217 serial port connections. +This class implements the +javax.comm.SerialPort +API, and so is backward-compatible with existing Java software that works with serial ports. +

+ +

+Visit the jvser homepage at http://jvser.googlecode.com/. +

+ + diff --git a/nrjavaserial/src/main/java/gnu/io/rfc2217/package-info.java b/nrjavaserial/src/main/java/gnu/io/rfc2217/package-info.java new file mode 100644 index 00000000..62bcd6e3 --- /dev/null +++ b/nrjavaserial/src/main/java/gnu/io/rfc2217/package-info.java @@ -0,0 +1,19 @@ + +/* + * Copyright (C) 2010 Archie L. Cobbs. All rights reserved. + * + * $Id: package-info.java 31 2010-11-22 22:43:26Z archie.cobbs $ + */ + +/** + * Top level Java package for the jvser RFC 2217 client library. + * + *

+ * The central class in this package is {@link gnu.io.rfc2217.TelnetSerialPort}. + *

+ * + * @see gnu.io.rfc2217.TelnetSerialPort + * @see RFC 2217 + * @see The jvser project home page + */ +package gnu.io.rfc2217; diff --git a/nrjavaserial/src/main/resources/jvser.properties b/nrjavaserial/src/main/resources/jvser.properties new file mode 100644 index 00000000..73508fca --- /dev/null +++ b/nrjavaserial/src/main/resources/jvser.properties @@ -0,0 +1,3 @@ +# $Id: jvser.properties 13 2010-11-22 15:02:45Z archie.cobbs $ + +jvser.version=1.0.48