org.bidib.jbidibc.jserialcomm.JSerialCommSerialConnector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbidibc-jserialcomm Show documentation
Show all versions of jbidibc-jserialcomm Show documentation
jBiDiB jbidibc jserialcomm POM
package org.bidib.jbidibc.jserialcomm;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.bidib.jbidibc.messages.MessageReceiver;
import org.bidib.jbidibc.messages.base.AbstractBaseBidib;
import org.bidib.jbidibc.messages.base.RawMessageListener;
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.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.jbidibc.serial.LineStatusListener;
import org.bidib.jbidibc.serial.SerialMessageEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.fazecast.jSerialComm.SerialPortMessageListener;
public class JSerialCommSerialConnector extends AbstractBaseBidib {
private static final Logger LOGGER = LoggerFactory.getLogger(JSerialCommSerialConnector.class);
private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("RAW");
private SerialPort comPort;
private MessageReceiver messageReceiver;
private LineStatusListener lineStatusListener;
private ScheduledExecutorService lineStatusWorker;
/**
* @return the messageReceiver
*/
@Override
public MessageReceiver getMessageReceiver() {
return messageReceiver;
}
/**
* @param messageReceiver
* the messageReceiver to set
*/
@Override
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;
}
protected boolean isImplAvaiable() {
return (comPort != null);
}
@Override
public boolean isOpened() {
return comPort != null && comPort.isOpen();
}
@Override
protected void internalOpen(String portName, Context context) throws PortNotFoundException, PortNotOpenedException {
super.internalOpen(portName, context);
LOGGER.info("Create the lineStatusWorker thread pool: {}", lineStatusWorker);
this.lineStatusWorker =
Executors
.newScheduledThreadPool(1,
new ThreadFactoryBuilder().setNameFormat("jserialLineStatusWorkers-thread-%d").build());
Boolean useHardwareFlowControl = context.get("serial.useHardwareFlowControl", Boolean.class, Boolean.TRUE);
LOGGER.info("Open port with portName: {}, useHardwareFlowControl: {}", portName, useHardwareFlowControl);
final MessageReceiver serialMessageReceiver = getMessageReceiver();
comPort = SerialPort.getCommPort(portName);
if (useHardwareFlowControl) {
comPort.setFlowControl(SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED);
}
else {
comPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
}
Integer baudRate = context.get("serial.baudrate", Integer.class, Integer.valueOf(115200));
LOGGER.info("Open port with baudRate: {}", baudRate);
comPort.setComPortParameters(baudRate, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
boolean opened = comPort.openPort();
if (!opened) {
LOGGER.warn("Port was not opened: {}", portName);
throw new PortNotOpenedException("Port was not opened: " + portName, "unknown");
}
startReceiverAndQueues(serialMessageReceiver, context);
comPort.addDataListener(new MessageListener());
if (useHardwareFlowControl) {
// Activate DTR
try {
LOGGER.info("Activate DTR.");
comPort.setDTR(); // pin 1 in DIN8; on main connector, this is DTR
}
catch (Exception e) {
LOGGER.warn("Set DTR true failed.", e);
}
}
fireCtsChanged(true, true);
setConnected(true);
}
private final class MessageListener implements SerialPortMessageListener {
@Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_RECEIVED | SerialPort.LISTENING_EVENT_PORT_DISCONNECTED;
}
@Override
public byte[] getMessageDelimiter() {
return new byte[] { ByteUtils.getLowByte(0xFE) };
}
@Override
public boolean delimiterIndicatesEndOfMessage() {
return true;
}
@Override
public void serialEvent(SerialPortEvent event) {
LOGGER.trace("serialEvent, eventType: {}", event.getEventType());
switch (event.getEventType()) {
case SerialPort.LISTENING_EVENT_DATA_RECEIVED:
byte[] data = event.getReceivedData();
receive(data, data.length);
break;
case SerialPort.LISTENING_EVENT_PORT_DISCONNECTED:
LOGGER.warn("Port was disconnected.");
portDisconnected();
break;
default:
break;
}
}
}
private void portDisconnected() {
Thread worker = new Thread(() -> {
LOGGER.info("Start close port because port disconnected was signalled.");
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();
}
@Override
public boolean close() {
if (comPort != null) {
LOGGER.info("Start closing the port: {}", comPort);
long start = System.currentTimeMillis();
final SerialPort comPortToClose = this.comPort;
this.comPort = null;
LOGGER.info("Close the port, comPort: {}", comPortToClose);
comPortToClose.removeDataListener();
comPortToClose.closePort();
final MessageReceiver serialMessageReceiver = getMessageReceiver();
stopReceiverAndQueues(serialMessageReceiver);
firstPacketSent = false;
long end = System.currentTimeMillis();
LOGGER.info("Closed the port. duration: {}", end - start);
fireCtsChanged(false, true);
setConnected(false);
// comPort = null;
LOGGER.info("Shutdown the lineStatusWorker: {}", lineStatusWorker);
if (lineStatusWorker != null) {
try {
this.lineStatusWorker.shutdownNow();
this.lineStatusWorker.awaitTermination(500, TimeUnit.MILLISECONDS);
}
catch (Exception ex) {
LOGGER.warn("Terminate lineStatusWorker failed.", ex);
}
// free the lineStatusWorker
this.lineStatusWorker = null;
}
return true;
}
else {
LOGGER.info("No port to close available.");
}
return false;
}
public List getPortIdentifiers() {
List portIdentifiers = new ArrayList<>();
for (SerialPort serialPort : SerialPort.getCommPorts()) {
portIdentifiers.add(serialPort.getSystemPortName());
}
return portIdentifiers;
}
protected void fireCtsChanged(boolean ready, boolean manualEvent) {
LOGGER.info("CTS has changed, ready: {}", ready);
lineStatusWorker.schedule(() -> {
// signal changed line status to the bidib implementation
if (lineStatusListener != null) {
lineStatusListener.notifyLineStatusChanged(ready, manualEvent);
}
}, 0, TimeUnit.MILLISECONDS);
}
private ByteArrayOutputStream sendBuffer = new ByteArrayOutputStream(100);
@Override
protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {
if (comPort != null && data != null) {
try {
if (!firstPacketSent) {
LOGGER.info("Send initial sequence.");
try {
byte[] initialSequence = new byte[] { ByteUtils.MAGIC };
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(initialSequence));
}
if (rawMessageListener != null) {
rawMessageListener.notifySend(initialSequence);
}
comPort.writeBytes(initialSequence, initialSequence.length);
Thread.sleep(10);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(initialSequence));
}
if (rawMessageListener != null) {
rawMessageListener.notifySend(initialSequence);
}
comPort.writeBytes(initialSequence, initialSequence.length);
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());
}
int sent = comPort.writeBytes(sendBuffer.toByteArray(), sendBuffer.size());
if (sent == 0) {
MSG_RAW_LOGGER.warn(">> sent bytes: {}", sent);
byte[] bytes = data.toByteArray();
LOGGER
.error("The message has not been sent to comPort: {}, message: {}", comPort,
ByteUtils.bytesToHex(bytes));
throw new RuntimeException("Write message to output failed: " + ByteUtils.bytesToHex(bytes));
}
else {
MSG_RAW_LOGGER.info(">> sent bytes: {}", sent);
}
}
catch (IOException ex) {
byte[] bytes = data.toByteArray();
LOGGER
.warn("Send message to output stream failed: [{}] - {}", bytes.length, ByteUtils.bytesToHex(bytes));
throw new RuntimeException("Send message to output stream failed: " + ByteUtils.bytesToHex(bytes), ex);
}
finally {
sendBuffer.reset();
}
}
}
}