org.bidib.jbidibc.rxtx.debug.DebugReader Maven / Gradle / Ivy
package org.bidib.jbidibc.rxtx.debug;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.TooManyListenersException;
import java.util.concurrent.Semaphore;
import org.bidib.jbidibc.debug.AbstractDebugReader;
import org.bidib.jbidibc.debug.DebugMessageProcessor;
import org.bidib.jbidibc.debug.LineEndingEnum;
import org.bidib.jbidibc.debug.exception.InvalidLibraryException;
import org.bidib.jbidibc.messages.ConnectionListener;
import org.bidib.jbidibc.messages.exception.PortNotFoundException;
import org.bidib.jbidibc.messages.exception.PortNotOpenedException;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import gnu.io.UnsupportedCommOperationException;
public class DebugReader extends AbstractDebugReader {
private static final Logger LOGGER = LoggerFactory.getLogger(DebugReader.class);
private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("DEBUG_RAW");
static final int DEFAULT_TIMEOUT = /* 1500 */300;
private SerialPort port;
// private DebugMessageProcessor messageReceiver;
// private String requestedPortName;
private ConnectionListener connectionListener;
private Semaphore portSemaphore = new Semaphore(1);
private Semaphore sendSemaphore = new Semaphore(1);
// private BlockingQueue receiveQueue = new LinkedBlockingQueue<>();
//
// private Thread receiveQueueWorker;
//
// private AtomicBoolean receiverRunning = new AtomicBoolean();
//
// private AtomicLong receiveQueueWorkerThreadId = new AtomicLong();
public DebugReader(final DebugMessageProcessor messageReceiver) {
super(messageReceiver);
}
@Override
public void initialize() {
}
@Override
public List getPortIdentifiers() {
List portIdentifiers = new ArrayList();
try {
// get the comm port identifiers
Enumeration> e = CommPortIdentifier.getPortIdentifiers();
while (e.hasMoreElements()) {
CommPortIdentifier id = (CommPortIdentifier) e.nextElement();
LOGGER
.debug("Process current CommPortIdentifier, name: {}, portType: {}", id.getName(),
id.getPortType());
if (id.getPortType() == CommPortIdentifier.PORT_SERIAL) {
portIdentifiers.add(id.getName());
}
else {
LOGGER
.debug("Skip port because no serial port, name: {}, portType: {}", id.getName(),
id.getPortType());
}
}
}
catch (UnsatisfiedLinkError ule) {
LOGGER.warn("Get comm port identifiers failed.", ule);
throw new InvalidLibraryException(ule.getMessage(), ule.getCause());
}
catch (Error error) {
LOGGER.warn("Get comm port identifiers failed.", error);
throw new RuntimeException(error.getMessage(), error.getCause());
}
return portIdentifiers;
}
// @Override
// public DebugMessageProcessor getMessageReceiver() {
// return messageReceiver;
// }
//
// /**
// * @return the connectionListener
// */
// @Override
// public ConnectionListener getConnectionListener() {
// return connectionListener;
// }
//
// /**
// * @param connectionListener
// * the connectionListener to set
// */
// @Override
// public void setConnectionListener(ConnectionListener connectionListener) {
// this.connectionListener = connectionListener;
// }
private SerialPort internalOpen(CommPortIdentifier commPort, int baudRate, Context context)
throws PortInUseException, UnsupportedCommOperationException, TooManyListenersException {
startReceiveQueueWorker();
// open the port
SerialPort serialPort = commPort.open(DebugReader.class.getName(), 2000);
getConnectionListener().opened(commPort.getName());
LOGGER.info("Set flow control mode to SerialPort.FLOWCONTROL_NONE!");
serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
// serialPort.enableReceiveTimeout(DEFAULT_TIMEOUT);
serialPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
clearInputStream(serialPort);
// enable the message receiver before the event listener is added
getMessageReceiver().enable();
serialPort.addEventListener(new SerialPortEventListener() {
@Override
public void serialEvent(SerialPortEvent event) {
// this callback is called every time data is available
LOGGER.trace("serialEvent received: {}", event);
switch (event.getEventType()) {
case SerialPortEvent.DATA_AVAILABLE:
try {
receive(port);
}
catch (Exception ex) {
LOGGER.warn("Process received bytes failed.", ex);
}
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
LOGGER.info("The output buffer is empty.");
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> Notify output empty");
}
break;
case SerialPortEvent.CD:
LOGGER.warn("CD is signalled.");
break;
case SerialPortEvent.CTS:
LOGGER
.warn("The CTS value has changed, old value: {}, new value: {}",
new Object[] { event.getOldValue(), event.getNewValue() });
if (event.getNewValue() == false) {
LOGGER.warn("CTS has changed to false.");
}
break;
case SerialPortEvent.HARDWARE_ERROR:
LOGGER.warn("HARDWARE_ERROR is signalled.");
if (event.getNewValue() == true) {
LOGGER.warn("Close the port.");
Thread worker = new Thread(() -> {
LOGGER.info("Start close port because hardware error was detected.");
try {
// the listeners are notified in close()
close();
}
catch (Exception ex) {
LOGGER.warn("Close after error failed.", ex);
}
LOGGER.warn("The port was closed.");
});
worker.start();
}
break;
default:
LOGGER
.warn("SerialPortEvent was triggered, type: {}, old value: {}, new value: {}",
new Object[] { event.getEventType(), event.getOldValue(), event.getNewValue() });
break;
}
}
});
serialPort.notifyOnDataAvailable(true);
// react on port removed ...
serialPort.notifyOnCTS(true);
serialPort.notifyOnCarrierDetect(true);
serialPort.notifyOnBreakInterrupt(true);
serialPort.notifyOnDSR(true);
serialPort.notifyOnOverrunError(true);
// Activate RTS
try {
LOGGER.info("Activate RTS.");
serialPort.setRTS(true);
}
catch (Exception e) {
LOGGER.warn("Set RTS true failed.", e);
}
return serialPort;
}
private void clearInputStream(final SerialPort serialPort) {
// get and clear stream
try {
InputStream serialStream = serialPort.getInputStream();
// purge contents, if any
int count = serialStream.available();
LOGGER.debug("input stream shows {} bytes available", count);
while (count > 0) {
serialStream.skip(count);
count = serialStream.available();
}
LOGGER.debug("input stream shows {} bytes available after purge.", count);
}
catch (Exception e) {
LOGGER.warn("Clear input stream failed.", e);
}
}
@Override
public void close() {
if (port != null) {
LOGGER.debug("Close the port.");
long start = System.currentTimeMillis();
// Deactivate RTS
try {
LOGGER.info("Deactivate RTS.");
port.setRTS(false);
Thread.sleep(100);
}
catch (Exception e) {
LOGGER.warn("Set RTS to false failed.", e);
}
// this makes the close operation faster ...
try {
port.removeEventListener();
// port.enableReceiveTimeout(200);
}
catch (Exception e) {
LOGGER.warn("Remove event listener and set receive timeout failed.", e);
}
try {
port.close();
}
catch (Exception e) {
LOGGER.warn("Close port failed.", e);
}
long end = System.currentTimeMillis();
LOGGER.debug("Closed the port. duration: {}", end - start);
port = null;
// no longer process received messages
getMessageReceiver().disable();
stopReceiveQueueWorker();
if (connectionListener != null) {
connectionListener.closed(getRequestedPortName());
}
setRequestedPortName(null);
}
}
@Override
public boolean isOpened() {
boolean isOpened = false;
try {
portSemaphore.acquire();
LOGGER.debug("Check if port is opened: {}", port);
isOpened = (port != null && port.getOutputStream() != null);
}
catch (InterruptedException ex) {
LOGGER.warn("Wait for portSemaphore was interrupted.", ex);
}
catch (IOException ex) {
LOGGER.warn("OutputStream is not available.", ex);
}
finally {
portSemaphore.release();
}
return isOpened;
}
@Override
public void open(String portName, int baudRate, ConnectionListener connectionListener, Context context)
throws PortNotFoundException, PortNotOpenedException {
setConnectionListener(connectionListener);
if (port == null) {
if (portName == null || portName.trim().isEmpty()) {
throw new PortNotFoundException("");
}
LOGGER.info("Open port with name: {}", portName);
CommPortIdentifier commPort = null;
try {
commPort = CommPortIdentifier.getPortIdentifier(portName);
}
catch (NoSuchPortException ex) {
LOGGER.warn("Requested port is not available: {}", portName, ex);
throw new PortNotFoundException(portName);
}
LOGGER.info("Set the requestedPortName: {}, baudRate: {}", portName, baudRate);
setRequestedPortName(portName);
try {
portSemaphore.acquire();
try {
// try to open the port
close();
port = internalOpen(commPort, baudRate, context);
LOGGER.info("The port was opened internally.");
}
catch (PortInUseException ex) {
LOGGER.warn("Open communication failed because port is in use.", ex);
try {
close();
}
catch (Exception e4) {
// ignore
}
throw new PortNotOpenedException(portName, PortNotOpenedException.PORT_IN_USE);
}
catch (TooManyListenersException | UnsupportedCommOperationException ex) {
LOGGER.warn("Open communication failed because port has thrown an exception.", ex);
try {
close();
}
catch (Exception e4) {
// ignore
}
throw new PortNotOpenedException(portName, PortNotOpenedException.UNKNOWN);
}
}
catch (InterruptedException ex) {
LOGGER.warn("Wait for portSemaphore was interrupted.", ex);
throw new PortNotOpenedException(portName, PortNotOpenedException.UNKNOWN);
}
finally {
portSemaphore.release();
}
}
else {
LOGGER.warn("Port is already opened.");
}
}
/**
* Send the bytes of the message to the outputstream and add <CR>+<LF>.
*
* @param bytes
* the bytes to send
*/
@Override
public void send(final String message, LineEndingEnum lineEnding) {
if (port != null) {
try {
sendSemaphore.acquire();
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> '{}'", message);
}
OutputStream output = port.getOutputStream();
output.write(message.getBytes());
output.write(lineEnding.getValues());
// output.flush();
}
catch (Exception e) {
throw new RuntimeException("Send message to output stream failed.", e);
}
finally {
sendSemaphore.release();
}
}
}
@Override
public void send(byte[] content) {
if (port != null) {
try {
sendSemaphore.acquire();
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> [{}] - {}", content.length, ByteUtils.bytesToHex(content));
}
OutputStream output = port.getOutputStream();
output.write(content);
// output.flush();
}
catch (Exception e) {
throw new RuntimeException("Send message to output stream failed.", e);
}
finally {
sendSemaphore.release();
}
}
}
private byte[] inputBuffer = new byte[2048];
private ByteArrayOutputStream output = new ByteArrayOutputStream();
private void receive(final SerialPort port) {
LOGGER.debug("Start receiving messages.");
synchronized (this) {
LOGGER.debug("Starting message receiver.");
try {
InputStream input = null;
if (port != null) {
input = port.getInputStream();
}
if (input != null) {
// read the values from in the port
int len = input.read(inputBuffer);
if (len > 0) {
output.write(inputBuffer, 0, len);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info("<<<< len: {}, data: {}", output.size(),
ByteUtils.bytesToHex(output.toByteArray()));
}
addDataToReceiveQueue(output);
if (output != null && output.size() > 0) {
LOGGER.warn("Data in output: {}", output.toString());
}
}
}
else {
LOGGER.error("No input available.");
}
}
catch (Exception e) {
LOGGER.warn("Exception detected in message receiver!", e);
throw new RuntimeException(e);
}
}
}
// @Override
// private void addDataToReceiveQueue(ByteArrayOutputStream output) {
//
// byte[] bytes = output.toByteArray();
//
// byte[] buffer = new byte[bytes.length];
// System.arraycopy(bytes, 0, buffer, 0, bytes.length);
//
// if (MSG_RAW_LOGGER.isInfoEnabled()) {
// MSG_RAW_LOGGER.info("<<<< len: {}, data: {}", bytes.length, ByteUtils.bytesToHex(buffer));
// }
//
// boolean added = receiveQueue.offer(buffer);
// if (!added) {
// LOGGER.error("The message was not added to the receive queue: {}", ByteUtils.bytesToHex(buffer));
// }
//
// output.reset();
// }
//
// private void startReceiveQueueWorker() {
// receiverRunning.set(true);
//
// LOGGER.info("Start the receiveQueueWorker. Current receiveQueueWorker: {}", receiveQueueWorker);
// receiveQueueWorker = new Thread(new Runnable() {
//
// @Override
// public void run() {
// try {
// processReceiveQueue();
// }
// catch (Exception ex) {
// LOGGER.warn("The processing of the receive queue was terminated with an exception!", ex);
//
// // running.set(false);
// }
//
// LOGGER.info("Process receive queue has finished.");
// }
// }, "receiveQueueWorker");
//
// try {
// receiveQueueWorkerThreadId.set(receiveQueueWorker.getId());
// receiveQueueWorker.start();
// }
// catch (Exception ex) {
// LOGGER.error("Start the receiveQueueWorker failed.", ex);
// }
// }
//
// @Override
// private void stopReceiveQueueWorker() {
// LOGGER.info("Stop the receive queue worker.");
// receiverRunning.set(false);
//
// try {
// receiveQueueWorker.interrupt();
//
// receiveQueueWorker.join(1000);
//
// LOGGER.info("receiveQueueWorker has finished.");
// }
// catch (Exception ex) {
// LOGGER.warn("Interrupt receiveQueueWorker failed.", ex);
// }
// receiveQueueWorker = null;
// }
//
// private void processReceiveQueue() {
// byte[] bytes = null;
// LOGGER.info("The receiveQueueWorker is ready for processing.");
//
// // final DebugMessageProcessor serialMessageReceiver = getMessageReceiver();
// while (receiverRunning.get()) {
// try {
// // get the message to process
// bytes = receiveQueue.take();
//
// if (bytes != null) {
// // process
// try {
//
// ByteArrayOutputStream output = new ByteArrayOutputStream();
// output.write(bytes);
//
// getMessageReceiver().processMessages(output);
// }
// catch (Exception ex) {
// LOGGER.warn("Process received bytes failed.", ex);
// }
//
// }
// }
// catch (InterruptedException ex) {
// LOGGER.warn("Get message from receiveQueue failed because thread was interrupted.");
// }
// catch (Exception ex) {
// LOGGER.warn("Get message from receiveQueue failed.", ex);
// bytes = null;
// }
//
// }
//
// LOGGER.info("The receiveQueueWorker has finished processing.");
// receiveQueueWorkerThreadId.set(0);
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy