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

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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy