All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gnu.io.rfc2217.TelnetSerialPort Maven / Gradle / Ivy


/*
 * 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:
 * 
    *
  • * To "open" a serial port, create an instance of this class, configure it as required, * and then get the {@link TelnetClient} via {@link #getTelnetClient} and invoke * {@link TelnetClient#connect(java.net.InetAddress, int) TelnetClient.connect()} (or one of its variants). * This will create the telnet connection to the access server. *
  • * *
  • * Once connected, if the underlying telnet connection is broken, an {@link IOException} will be * thrown when attempting to access the serial port input or output streams. In addition, a * {@link SerialPortEvent#DATA_AVAILABLE DATA_AVAILABLE} event will be immediately generated. *
  • *
* * * * The following optional functionality is not implemented and/or inappropriate for a networked connection: *
    *
  • Receive threshold
  • *
  • Receive timeout
  • *
  • Receive framing byte
  • *
  • Input buffer size
  • *
  • Output buffer size
  • *
* * * * 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(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy