org.bidib.jbidibc.scm.debug.DebugReader Maven / Gradle / Ivy
The newest version!
package org.bidib.jbidibc.scm.debug;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.lang3.StringUtils;
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.NoAnswerException;
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.scm.ScmPortIdentifierUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.serialpundit.core.SerialComException;
import com.serialpundit.serial.ISerialComDataListener;
import com.serialpundit.serial.ISerialComEventListener;
import com.serialpundit.serial.SerialComLineEvent;
import com.serialpundit.serial.SerialComManager;
import com.serialpundit.serial.SerialComManager.BAUDRATE;
import com.serialpundit.serial.SerialComManager.DATABITS;
import com.serialpundit.serial.SerialComManager.FLOWCONTROL;
import com.serialpundit.serial.SerialComManager.PARITY;
import com.serialpundit.serial.SerialComManager.STOPBITS;
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 SerialComManager scm;
private long handle = -1;
private ISerialComDataListener dataListener;
// private DebugMessageProcessor messageReceiver;
//
// private String requestedPortName;
//
// private ConnectionListener connectionListener;
private Semaphore portSemaphore = new Semaphore(1);
private Semaphore sendSemaphore = new Semaphore(1);
private ISerialComEventListener eventListener;
// private boolean addEventListener = true;
private AtomicBoolean closeInProgress = new AtomicBoolean();
// private BlockingQueue receiveQueue = new LinkedBlockingQueue<>();
//
// private Thread receiveQueueWorker;
//
// private AtomicBoolean receiverRunning = new AtomicBoolean();
//
// private AtomicLong receiveQueueWorkerThreadId = new AtomicLong();
/**
* @param messageReceiver
* the message receiver
*/
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
List portIds = ScmPortIdentifierUtils.getPortIdentifiers();
portIdentifiers.addAll(portIds);
}
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 long internalOpen(String portName, int baudRate, Context context) throws SerialComException {
// reset the close in progress flag
closeInProgress.set(false);
startReceiveQueueWorker();
// open the port
handle = scm.openComPort(portName, true, true, true);
LOGGER.info("Opened serial port, handle: {}", handle);
scm.configureComPortControl(handle, FLOWCONTROL.NONE, 'x', 'x', false, true);
scm
.configureComPortData(handle, DATABITS.DB_8, STOPBITS.SB_1, PARITY.P_NONE,
BAUDRATE.valueOf("B" + Integer.toString(baudRate)), 0);
getConnectionListener().opened(portName);
// enable the message receiver before the event listener is added
getMessageReceiver().enable();
// add event listener
eventListener = new ISerialComEventListener() {
@Override
public void onNewSerialEvent(SerialComLineEvent lineEvent) {
LOGGER.error("eventCTS : {}, eventDSR : {}", lineEvent.getCTS(), lineEvent.getDSR());
}
};
scm.registerLineEventListener(handle, eventListener);
// add the data listener
dataListener = new ISerialComDataListener() {
@Override
public void onNewSerialDataAvailable(final byte[] data) {
try {
addDataToReceiveQueue(data);
}
catch (Exception ex) {
LOGGER.warn("Process received bytes failed.", ex);
}
}
@Override
public void onDataListenerError(int errorNum) {
LOGGER.error("Data listener notified an error: {}", errorNum);
if (isOpened() && !closeInProgress.get()) {
LOGGER.info("Close the port.");
closeInProgress.set(true);
if (dataListener != null) {
try {
LOGGER.info("Unregister data listener: {}", dataListener);
scm.unregisterDataListener(handle, dataListener);
}
catch (Exception ex) {
LOGGER.warn("Unregister data listener after error detection failed.", ex);
}
dataListener = null;
}
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
LOGGER.info("Error detected. Close the port.");
try {
close();
}
catch (Exception ex) {
LOGGER.warn("Close scm port failed.", ex);
}
}
});
t1.start();
try {
Thread.sleep(20);
}
catch (InterruptedException ex) {
LOGGER.warn("Wait for close thread to startup was interrupted.", ex);
}
}
else {
LOGGER.info("Port is closed.");
try {
Thread.sleep(20);
}
catch (InterruptedException ex) {
LOGGER.warn("Wait for close thread was interrupted.", ex);
}
}
}
};
LOGGER.info("Registering data listener fro handle: {}.", handle);
// register data listener for this port
scm.registerDataListener(handle, dataListener);
LOGGER.info("Registered data listener.");
try {
LOGGER.info("Activate RTS.");
scm.setRTS(handle, true);
Thread.sleep(100);
}
catch (Exception e) {
LOGGER.warn("Set RTS true failed.", e);
}
return handle;
}
@Override
public void close() {
// close the port if available
if (scm != null) {
LOGGER.info("Close the port, handle: {}", handle);
long start = System.currentTimeMillis();
// unregister data listener
LOGGER.info("Unregister data listener: {}", dataListener);
if (handle > -1) {
try {
LOGGER.info("Deactivate RTS.");
scm.setRTS(handle, false);
Thread.sleep(100);
}
catch (Exception e) {
LOGGER.warn("Set RTS to false failed.", e);
}
}
if (dataListener != null && handle > -1) {
try {
scm.unregisterDataListener(handle, dataListener);
}
catch (SerialComException ex) {
LOGGER.warn("Unregister dataListener failed.", ex);
}
try {
Thread.sleep(200);
}
catch (InterruptedException ex) {
LOGGER.warn("Sleep after unregister data listener failed.", ex);
}
}
dataListener = null;
// // unregister line event listener
if (eventListener != null && handle > -1) {
LOGGER.info("Unregister line event listener.");
try {
scm.unregisterLineEventListener(handle, eventListener);
}
catch (SerialComException ex) {
LOGGER.warn("Unregister lineEventListener failed.", ex);
}
try {
Thread.sleep(200);
}
catch (InterruptedException ex) {
LOGGER.warn("Sleep after unregister line event listener failed.", ex);
}
}
eventListener = null;
// no longer process received messages
getMessageReceiver().disable();
stopReceiveQueueWorker();
if (handle > -1) {
try {
LOGGER.info("Close the COM port: {}", handle);
// port.close();
scm.closeComPort(handle);
}
catch (Exception e) {
LOGGER.warn("Close port failed.", e);
}
}
else {
LOGGER.info("Don't close port because handle is not valid.");
}
long end = System.currentTimeMillis();
LOGGER.info("Closed the port. duration: {}", end - start);
scm = null;
handle = -1;
if (getConnectionListener() != null) {
getConnectionListener().closed(getRequestedPortName());
}
setRequestedPortName(null);
}
}
@Override
public boolean isOpened() {
boolean isOpened = (handle > -1);
return isOpened;
}
@Override
public void open(String portName, int baudRate, ConnectionListener connectionListener, Context context)
throws PortNotFoundException, PortNotOpenedException {
setConnectionListener(connectionListener);
if (scm == null) {
if (portName == null || portName.trim().isEmpty()) {
throw new PortNotFoundException("");
}
LOGGER.info("Open port with name: {}, baudRate: {}", portName, baudRate);
setRequestedPortName(portName);
try {
portSemaphore.acquire();
try {
close();
String tempDir = System.getProperty("java.io.tmpdir");
String appSuffix = System.getProperty("jbidibc.appsuffix");
StringBuilder scmExpandFile = new StringBuilder("jbidibc");
if (StringUtils.isNotBlank(appSuffix)) {
scmExpandFile.append("-").append(appSuffix);
}
LOGGER.info("Prepared scmExpandFile: {}", scmExpandFile);
File temp = new File(tempDir, scmExpandFile.toString());
scm = new SerialComManager("scm", temp.getAbsolutePath(), true, false);
// open the commPort
internalOpen(portName, baudRate, context);
LOGGER.info("The port was opened internally.");
}
catch (NoAnswerException naex) {
LOGGER.warn("Open communication failed.", naex);
try {
close();
}
catch (Exception e4) { // NOSONAR
// ignore
}
throw naex;
}
catch (Exception e2) {
LOGGER.info("Open port failed. Close port and throw exception.", e2);
// close port to cleanup and stop the send queue worker
try {
close();
}
catch (Exception e3) { // NOSONAR
LOGGER.warn("Close port failed.", e3);
}
throw new PortNotOpenedException(portName, PortNotOpenedException.UNKNOWN);
}
catch (UnsatisfiedLinkError err) {
LOGGER.info("Open port failed. Close port and throw exception.", err);
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 (handle > -1) {
try {
sendSemaphore.acquire();
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> '{}'", message);
}
scm.writeBytes(handle, message.getBytes());
scm.writeBytes(handle, lineEnding.getValues());
}
catch (Exception e) {
throw new RuntimeException("Send message to output stream failed.", e);
}
finally {
sendSemaphore.release();
}
}
}
@Override
public void send(byte[] content) {
if (handle > -1) {
try {
sendSemaphore.acquire();
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER.info(">> '{}'", ByteUtils.bytesToHex(content));
}
scm.writeBytes(handle, content);
}
catch (Exception e) {
throw new RuntimeException("Send message to output stream failed.", e);
}
finally {
sendSemaphore.release();
}
}
}
// private void receive(final byte[] bytes) {
//
// 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));
// }
// }
// @Override
// 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);
// }
}