org.bidib.jbidibc.rxtx.RxtxSerialConnector Maven / Gradle / Ivy
package org.bidib.jbidibc.rxtx;
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 org.bidib.jbidibc.core.BidibLibrary;
import org.bidib.jbidibc.core.MessageReceiver;
import org.bidib.jbidibc.core.base.AbstractBaseBidib;
import org.bidib.jbidibc.core.base.RawMessageListener;
import org.bidib.jbidibc.core.exception.InvalidConfigurationException;
import org.bidib.jbidibc.core.exception.InvalidLibraryException;
import org.bidib.jbidibc.core.exception.PortNotFoundException;
import org.bidib.jbidibc.core.exception.PortNotOpenedException;
import org.bidib.jbidibc.core.helpers.Context;
import org.bidib.jbidibc.core.utils.ByteUtils;
import org.bidib.jbidibc.serial.LineStatusListener;
import org.bidib.jbidibc.serial.SerialMessageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import gnu.io.CommPortIdentifier;
import gnu.io.CommPortOwnershipListener;
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 RxtxSerialConnector extends AbstractBaseBidib {
private static final Logger LOGGER = LoggerFactory.getLogger(RxtxSerialConnector.class);
private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("RAW");
private SerialPort port;
private CommPortOwnershipListener commPortOwnershipListener;
private CommPortIdentifier commPort;
private MessageReceiver messageReceiver;
protected Object receiveLock = new Object();
private byte[] inputBuffer = new byte[2048];
private boolean useHardwareFlowControl;
private LineStatusListener lineStatusListener;
/**
* @return the messageReceiver
*/
public MessageReceiver getMessageReceiver() {
return messageReceiver;
}
/**
* @param messageReceiver
* the messageReceiver to set
*/
public void setMessageReceiver(MessageReceiver messageReceiver) {
this.messageReceiver = messageReceiver;
}
/**
* @return the lineStatusListener
*/
public LineStatusListener getLineStatusListener() {
return lineStatusListener;
}
/**
* @param lineStatusListener
* the lineStatusListener to set
*/
public void setLineStatusListener(LineStatusListener lineStatusListener) {
this.lineStatusListener = lineStatusListener;
}
@Override
protected void internalOpen(String portName, final Context context)
throws PortNotFoundException, PortNotOpenedException {
super.internalOpen(portName, context);
LOGGER.info("Internal open the port.");
if (commPort == null) {
try {
commPort = CommPortIdentifier.getPortIdentifier(portName);
}
catch (NoSuchPortException ex) {
LOGGER.warn("Requested port is not available: {}", portName, ex);
throw new PortNotFoundException(portName);
}
}
else {
LOGGER.info("Use existing commPort: {}", commPort);
}
if (commPortOwnershipListener == null) {
commPortOwnershipListener = new CommPortOwnershipListener() {
@Override
public void ownershipChange(int type) {
LOGGER.info("Ownership changed, type: {}", type);
}
};
}
commPort.addPortOwnershipListener(commPortOwnershipListener);
// open the port
SerialPort scmPort = null;
try {
scmPort = (SerialPort) commPort.open(RxtxSerialBidib.class.getName(), 2000);
Boolean useHardwareFlowControl = context.get("serial.useHardwareFlowControl", Boolean.class, Boolean.TRUE);
LOGGER.info("Open port with portName: {}, useHardwareFlowControl: {}", portName, useHardwareFlowControl);
if (useHardwareFlowControl) {
LOGGER
.info(
"Set flow control mode to SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT!");
scmPort.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN | SerialPort.FLOWCONTROL_RTSCTS_OUT);
this.useHardwareFlowControl = true;
}
else {
LOGGER.info("Set flow control mode to SerialPort.FLOWCONTROL_NONE!");
scmPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
this.useHardwareFlowControl = false;
}
Integer baudRate = context.get("serial.baudrate", Integer.class, Integer.valueOf(115200));
LOGGER.info("Open port with baudRate: {}", baudRate);
scmPort.setSerialPortParams(baudRate, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
}
catch (UnsupportedCommOperationException ex) {
LOGGER.warn("Configure RXTX com port failed.", ex);
throw new InvalidConfigurationException("Configure RXTX com port failed.");
}
catch (PortInUseException ex) {
LOGGER.warn("The port is in use already.", ex);
throw new PortNotOpenedException("The port is in use already.", ex.getMessage());
}
final SerialPort serialPort = scmPort;
// start the receiver and queues
startReceiverAndQueues(messageReceiver, context);
final SerialPortEventListener eventListener = 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:
// MSG_OUTPUTSTREAM_LOGGER.info("<<<<");
try {
synchronized (receiveLock) {
InputStream input = serialPort.getInputStream();
int len = -1;
try {
len = input.read(inputBuffer, 0, inputBuffer.length);
int remaining = input.available();
if (remaining > 0) {
LOGGER
.warn("More data in inputStream might be available, remaining: {}",
remaining);
}
}
catch (IOException ex) {
LOGGER.warn("Read data from input stream to buffer failed.", ex);
}
if (len > -1) {
receive(inputBuffer, len);
}
}
}
catch (Exception ex) {
LOGGER.error("Message receiver returned from receive with an exception!", ex);
}
break;
case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
LOGGER.trace("The output buffer is empty.");
// // only the sendQueueWorker must release the semaphore
// if (sendQueueWorkerThreadId.get() == Thread.currentThread().getId()) {
// MSG_OUTPUTSTREAM_LOGGER.info(">>>> OBE");
// // sendSemaphore.release();
// }
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() });
// TODO this should only stop sending messages but not close the port
fireCtsChanged(event.getNewValue());
if (event.getNewValue() == false) {
LOGGER.warn("Close the port.");
Thread worker = new Thread(new Runnable() {
public void run() {
LOGGER.info("Start close port because 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;
case SerialPortEvent.OE:
LOGGER.warn("OE (overrun error) is signalled.");
break;
case SerialPortEvent.DSR:
LOGGER.warn("DSR is signalled.");
break;
default:
LOGGER
.warn("SerialPortEvent was triggered, type: {}, old value: {}, new value: {}",
new Object[] { event.getEventType(), event.getOldValue(), event.getNewValue() });
break;
}
}
};
try {
serialPort.addEventListener(eventListener);
}
catch (TooManyListenersException ex) {
LOGGER.warn("Add eventlistener to RXTX com port failed.", ex);
throw new InvalidConfigurationException("Add eventlistener to RXTX com port failed.");
}
// react on port removed ...
serialPort.notifyOnCTS(true);
serialPort.notifyOnCarrierDetect(true);
serialPort.notifyOnBreakInterrupt(true);
serialPort.notifyOnDSR(true);
serialPort.notifyOnOverrunError(true);
serialPort.notifyOnOutputEmpty(true);
if (this.useHardwareFlowControl) {
// Activate DTR
try {
LOGGER.info("Activate DTR.");
serialPort.setDTR(true); // pin 1 in DIN8; on main connector, this is DTR
}
catch (Exception e) {
LOGGER.warn("Set DTR true failed.", e);
}
}
LOGGER.info("Let the serial port notify data available.");
serialPort.notifyOnDataAvailable(true);
if (this.useHardwareFlowControl) {
// Activate RTS
try {
LOGGER.info("Activate RTS.");
serialPort.setRTS(true);
}
catch (Exception e) {
LOGGER.warn("Set RTS true failed.", e);
}
}
try {
if (this.useHardwareFlowControl) {
fireCtsChanged(serialPort.isCTS());
}
else {
fireCtsChanged(true);
}
}
catch (Exception e) {
LOGGER.warn("Get CTS value failed.", e);
}
// keep the port
port = serialPort;
}
protected void fireCtsChanged(boolean ready) {
LOGGER.info("CTS has changed, ready: {}", ready);
// signal changed line status to the bidib implementation
if (lineStatusListener != null) {
lineStatusListener.notifyLineStatusChanged(ready);
}
}
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 boolean isOpened() {
boolean isOpened = false;
try {
LOGGER.debug("Check if port is opened: {}", port);
isOpened = (port != null && port.getOutputStream() != null);
}
catch (IOException ex) {
LOGGER.warn("OutputStream is not available.", ex);
}
return isOpened;
}
protected boolean isImplAvaiable() {
return (port != null);
}
@Override
public boolean close() {
if (port != null) {
LOGGER.info("Start closing the port: {}", port);
long start = System.currentTimeMillis();
// Deactivate DTR
if (this.useHardwareFlowControl) {
try {
LOGGER.info("Deactivate DTR.");
port.setDTR(false); // pin 1 in DIN8; on main connector, this is DTR
}
catch (Exception e) {
LOGGER.warn("Set DTR to false failed.", e);
}
// Deactivate RTS
try {
LOGGER.info("Deactivate RTS.");
port.setRTS(false);
}
catch (Exception e) {
LOGGER.warn("Set RTS to false failed.", e);
}
}
// this makes the close operation faster ...
try {
LOGGER.info("Remove event listener.");
port.removeEventListener();
LOGGER.info("Removed event listener.");
// port.enableReceiveTimeout(200);
}
catch (Exception e) {
LOGGER.warn("Remove event listener failed.", e);
}
// stop the receiver and queues
final MessageReceiver serialMessageReceiver = getMessageReceiver();
stopReceiverAndQueues(serialMessageReceiver);
try {
if (this.useHardwareFlowControl) {
fireCtsChanged(port.isCTS());
}
else {
fireCtsChanged(false);
}
}
catch (Exception e) {
LOGGER.warn("Get CTS value failed.", e);
}
LOGGER.info("Close the port.");
try {
port.close();
}
catch (Exception e) {
LOGGER.warn("Close port failed.", e);
}
fireCtsChanged(false);
long end = System.currentTimeMillis();
LOGGER.info("Closed the port. duration: {}", end - start);
if (commPortOwnershipListener != null) {
LOGGER.info("Remove the PortOwnershipListener from commPort: {}", commPort);
try {
commPort.removePortOwnershipListener(commPortOwnershipListener);
}
catch (Exception ex) {
LOGGER.warn("Remove port ownership listener failed.", ex);
}
commPortOwnershipListener = null;
}
port = null;
commPort = null;
// cleanupAfterClose(serialMessageReceiver);
return true;
}
else {
LOGGER.info("No port to close available.");
}
return false;
}
private final ByteArrayOutputStream sendBuffer = new ByteArrayOutputStream(2048);
@Override
protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {
if (port != null && data != null) {
try {
OutputStream os = port.getOutputStream();
if (!firstPacketSent) {
LOGGER.info("Send initial sequence.");
try {
byte[] initialSequence = new byte[] { (byte) BidibLibrary.BIDIB_PKT_MAGIC };
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(initialSequence));
}
if (rawMessageListener != null) {
rawMessageListener.notifySend(initialSequence);
}
os.write(initialSequence);
Thread.sleep(10);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(initialSequence));
}
if (rawMessageListener != null) {
rawMessageListener.notifySend(initialSequence);
}
os.write(initialSequence);
firstPacketSent = true;
LOGGER.info("Send initial sequence passed.");
}
catch (Exception ex) {
LOGGER.warn("Send initial sequence failed.", ex);
}
}
sendBuffer.reset();
SerialMessageEncoder.encodeMessage(data, sendBuffer);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", sendBuffer.toByteArray().length,
ByteUtils.bytesToHex(sendBuffer.toByteArray()));
}
if (rawMessageListener != null) {
rawMessageListener.notifySend(sendBuffer.toByteArray());
}
os.write(sendBuffer.toByteArray());
os.flush();
}
catch (Exception ex) {
byte[] bytes = data.toByteArray();
LOGGER
.warn("Send message to output stream failed: [{}] - {}", data.size(), ByteUtils.bytesToHex(bytes));
throw new RuntimeException("Send message to output stream failed: " + ByteUtils.bytesToHex(bytes), ex);
}
finally {
sendBuffer.reset();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy