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

org.bidib.jbidibc.jserialcomm.debug.DebugReader Maven / Gradle / Ivy

The newest version!
package org.bidib.jbidibc.jserialcomm.debug;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean;

import org.bidib.jbidibc.debug.AbstractDebugReader;
import org.bidib.jbidibc.debug.DebugMessageProcessor;
import org.bidib.jbidibc.debug.LineEndingEnum;
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 com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortEvent;
import com.fazecast.jSerialComm.SerialPortMessageListenerWithExceptions;

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 AtomicBoolean closeInProgress = new AtomicBoolean();

    // 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();

        for (SerialPort serialPort : SerialPort.getCommPorts()) {
            portIdentifiers.add(serialPort.getSystemPortName());
        }

        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(String portName, int baudRate, Context context)
        throws PortNotFoundException, PortNotOpenedException {

        // reset the close in progress flag
        closeInProgress.set(false);

        startReceiveQueueWorker();

        Boolean useHardwareFlowControl =
            context != null ? context.get("serial.useHardwareFlowControl", Boolean.class, Boolean.FALSE)
                : Boolean.FALSE;

        LOGGER.info("Open port with portName: {}, useHardwareFlowControl: {}", portName, useHardwareFlowControl);

        port = SerialPort.getCommPort(portName);

        if (useHardwareFlowControl) {
            port.setFlowControl(SerialPort.FLOW_CONTROL_RTS_ENABLED | SerialPort.FLOW_CONTROL_CTS_ENABLED);
        }
        else {
            port.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        }

        LOGGER.info("Open port with baudRate: {}", baudRate);

        port.setComPortParameters(baudRate, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
        boolean opened = port.openPort();

        if (!opened) {
            LOGGER.warn("Port was not opened: {}", portName);
            throw new PortNotOpenedException("Port was not opened: " + portName, "unknown");
        }

        getConnectionListener().opened(portName);

        // enable the message receiver before the event listener is added
        getMessageReceiver().enable();

        port.addDataListener(new MessageListener());

        if (useHardwareFlowControl) {
            // Activate DTR
            try {
                LOGGER.info("Activate DTR.");

                port.setDTR(); // pin 1 in DIN8; on main connector, this is DTR
            }
            catch (Exception e) {
                LOGGER.warn("Set DTR true failed.", e);
            }
        }

        // fireCtsChanged(true, true);

        return port;
    }

    private final class MessageListener implements SerialPortMessageListenerWithExceptions {
        @Override
        public int getListeningEvents() {
            return SerialPort.LISTENING_EVENT_DATA_RECEIVED | SerialPort.LISTENING_EVENT_PORT_DISCONNECTED;
        }

        @Override
        public byte[] getMessageDelimiter() {
            return new byte[0];
        }

        @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;
            }
        }

        @Override
        public void catchException(Exception ex) {
            LOGGER.warn("The serial port delivered an exception.", ex);

        }
    }

    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 void close() {
        if (port != null) {
            LOGGER.debug("Close the port.");
            long start = System.currentTimeMillis();

            final SerialPort comPortToClose = this.port;
            this.port = null;

            LOGGER.info("Close the port, comPort: {}", comPortToClose);

            comPortToClose.removeDataListener();
            comPortToClose.closePort();

            long end = System.currentTimeMillis();
            LOGGER.info("Closed the port. duration: {}", end - start);

            // no longer process received messages
            getMessageReceiver().disable();

            stopReceiveQueueWorker();

            try {
                if (getConnectionListener() != null) {
                    getConnectionListener().closed(getRequestedPortName());
                }
            }
            catch (Exception ex) {
                LOGGER.warn("Notify connection listener failed.", ex);
            }

            setRequestedPortName(null);
        }
    }

    @Override
    public boolean isOpened() {
        return port != null && port.isOpen();
    }

    @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);

            String commPort;
            SerialPort serialPort = SerialPort.getCommPort(portName);
            if (serialPort != null) {
                commPort = serialPort.getSystemPortName();
            }
            else {
                LOGGER.warn("Requested port is not available: {}", portName);
                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 (PortNotFoundException | PortNotOpenedException ex) {
                    LOGGER.warn("Open communication failed.", ex);
                    try {
                        close();
                    }
                    catch (Exception e4) {
                        // ignore
                    }
                    throw new PortNotOpenedException(portName, PortNotOpenedException.PORT_IN_USE);
                }
            }
            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 byte[] data, int len) {

        LOGGER.debug("Start receiving messages.");

        synchronized (this) {
            LOGGER.debug("Starting message receiver.");
            try {
                if (len > 0) {
                    output.write(data, 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(final 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: {}, string: {}", bytes.length, ByteUtils.bytesToHex(buffer),
    // new String(bytes));
    // }
    //
    // boolean added = receiveQueue.offer(buffer);
    // if (!added) {
    // LOGGER.error("The message was not added to the receive queue: {}", ByteUtils.bytesToHex(buffer));
    // }
    //
    // output.reset();
    // }
    //
    // @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);
    // }
    //
    // LOGGER.info("Start the receiveQueueWorker finished. Current receiveQueueWorker: {}", receiveQueueWorker);
    // }
    //
    // @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, requestedPortName: {}", requestedPortName);
    //
    // 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, requestedPortName: {}", requestedPortName);
    // receiveQueueWorkerThreadId.set(0);
    // }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy