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

org.bidib.jbidibc.spsw.debug.NetDebugReader Maven / Gradle / Ivy

There is a newer version: 2.0.28
Show newest version
package org.bidib.jbidibc.spsw.debug;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

import org.bidib.jbidibc.core.ConnectionListener;
import org.bidib.jbidibc.core.exception.NoAnswerException;
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.debug.DebugInterface;
import org.bidib.jbidibc.debug.DebugMessageProcessor;
import org.bidib.jbidibc.debug.LineEndingEnum;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import de.ibapl.spsw.api.DataBits;
import de.ibapl.spsw.api.FlowControl;
import de.ibapl.spsw.api.Parity;
import de.ibapl.spsw.api.Speed;
import de.ibapl.spsw.api.StopBits;
import de.ibapl.spsw.ser2net.Ser2NetProvider;

public class NetDebugReader implements DebugInterface {

    private static final Logger LOGGER = LoggerFactory.getLogger(NetDebugReader.class);

    private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("DEBUG_RAW");

    private Ser2NetProvider ser2NetProvider;

    private DebugMessageProcessor messageReceiver;

    private String requestedPortName;

    private ConnectionListener connectionListener;

    private Semaphore portSemaphore = new Semaphore(1);

    private Semaphore sendSemaphore = new Semaphore(1);

    private final ScheduledExecutorService receiveWorker = Executors.newScheduledThreadPool(1);

    private AtomicBoolean closeInProgress = new AtomicBoolean();

    private BlockingQueue freeBufferQueue = new LinkedBlockingQueue<>();

    private BlockingQueue receiveQueue = new LinkedBlockingQueue<>();

    private Thread receiveQueueWorker;

    private AtomicBoolean receiverRunning = new AtomicBoolean();

    private AtomicLong receiveQueueWorkerThreadId = new AtomicLong();

    public NetDebugReader(DebugMessageProcessor messageReceiver) {
        this.messageReceiver = messageReceiver;
    }

    @Override
    public void initialize() {
        // warmup
        for (int i = 0; i < 100; i++) {
            ByteArrayOutputStream item = new ByteArrayOutputStream(64);

            freeBufferQueue.add(item);
        }
    }

    @Override
    public List getPortIdentifiers() {
        return Collections.emptyList();
    }

    @Override
    public DebugMessageProcessor getMessageReceiver() {
        return messageReceiver;
    }

    /**
     * @return the connectionListener
     */
    public ConnectionListener getConnectionListener() {
        return connectionListener;
    }

    /**
     * @param connectionListener
     *            the connectionListener to set
     */
    public void setConnectionListener(ConnectionListener connectionListener) {
        this.connectionListener = connectionListener;
    }

    private Ser2NetProvider internalOpen(String portName, int baudRate, Context context) throws IOException {

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

        startReceiveQueueWorker();

        // open the port

        LOGGER.info("The interface port is a valid inet address. Create the Ser2NetProvider instance.");
        String[] splited = portName.split(":");
        String host = splited[0];
        int dataPort = Integer.parseInt(splited[1]);

        LOGGER.info("Create ser2NetProvider with host: {}, dataPort: {}", host, dataPort);

        Ser2NetProvider ser2NetProvider = null;
        try {
            ser2NetProvider = new Ser2NetProvider(host, dataPort);

            DataBits dataBits = DataBits.DB_8;
            StopBits stopBits = StopBits.SB_1;
            Parity parity = Parity.NONE;
            Set flowControls = FlowControl.getFC_NONE();
            ser2NetProvider.open(Speed.fromNative(baudRate), dataBits, stopBits, parity, flowControls);
        }
        catch (IOException ex) {
            LOGGER.warn("Open connection to remote serial port failed.", ex);

            throw ex;
        }

        getConnectionListener().opened(portName);

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

        final Ser2NetProvider ser2NetProviderReciever = ser2NetProvider;
        receiveWorker.submit(new Runnable() {

            @Override
            public void run() {
                LOGGER.info("The receiverWorker is running.");
                receiverWorkerEnabled.set(true);

                byte[] receiveData = new byte[1024];
                try (BufferedInputStream in = new BufferedInputStream(ser2NetProviderReciever.getInputStream())) {
                    int receivedCount = 0;

                    // wait for client sending data
                    while ((receivedCount = in.read(receiveData)) > 0 && receiverWorkerEnabled.get()) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER
                                .debug("Received data from tcp socket, len: {}, data: {}.", receivedCount,
                                    ByteUtils.bytesToHex(receiveData, receivedCount));
                        }

                        receive(receiveData, receivedCount);
                    }
                }
                catch (IOException ex) {
                    if (receiverWorkerEnabled.get()) {
                        LOGGER.warn("--- Interrupt NetDebugReader-run", ex);
                    }
                    else {
                        LOGGER.info("The NetDebugReader worker is terminating.");
                    }
                }

            }
        });

        return ser2NetProvider;
    }

    private AtomicBoolean receiverWorkerEnabled = new AtomicBoolean();

    @Override
    public void close() {

        // close the port if available
        if (ser2NetProvider != null) {
            LOGGER.info("Close the port, ser2NetProvider: {}", ser2NetProvider);

            long start = System.currentTimeMillis();

            // TODO Auto-generated method stub

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

            stopReceiveQueueWorker();

            try {
                LOGGER.info("Close the COM port: {}", ser2NetProvider);

                ser2NetProvider.close();
            }
            catch (Exception e) {
                LOGGER.warn("Close port failed.", e);
            }

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

            ser2NetProvider = null;

            if (connectionListener != null) {
                connectionListener.closed(requestedPortName);
            }

            requestedPortName = null;
        }
    }

    @Override
    public boolean isOpened() {
        boolean isOpened = false;
        try {
            portSemaphore.acquire();

            LOGGER.debug("Check if port is opened: {}", ser2NetProvider);
            isOpened = (ser2NetProvider != null && ser2NetProvider.getOutputStream() != null);
        }
        catch (InterruptedException ex) {
            LOGGER.warn("Wait for portSemaphore was interrupted.", ex);
        }
        catch (IOException ex) {
            LOGGER.warn("OutputStream is not available.", ex);
        }
        finally {
            portSemaphore.release();
        }
        return isOpened;
    }

    @Override
    public void open(String portName, int baudRate, ConnectionListener connectionListener, Context context)
        throws PortNotFoundException, PortNotOpenedException {

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

        setConnectionListener(connectionListener);

        if (ser2NetProvider == null) {
            if (portName == null || portName.trim().isEmpty()) {
                throw new PortNotFoundException("");
            }

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

            requestedPortName = portName;

            try {
                portSemaphore.acquire();

                try {
                    close();

                    // 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 (ser2NetProvider != null) {
            try {
                sendSemaphore.acquire();

                if (MSG_RAW_LOGGER.isInfoEnabled()) {
                    MSG_RAW_LOGGER.info(">> '{}'", message);
                }

                OutputStream output = ser2NetProvider.getOutputStream();

                output.write(message.getBytes());
                output.write(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 (ser2NetProvider != null) {
            try {
                sendSemaphore.acquire();

                if (MSG_RAW_LOGGER.isInfoEnabled()) {
                    MSG_RAW_LOGGER.info(">> '{}'", ByteUtils.bytesToHex(content));
                }

                OutputStream output = ser2NetProvider.getOutputStream();

                output.write(content);
            }
            catch (Exception e) {
                throw new RuntimeException("Send message to output stream failed.", e);
            }
            finally {
                sendSemaphore.release();
            }
        }
    }

    /**
     * @param data
     *            the received data
     * @param len
     *            the len of data
     */
    private void receive(final byte[] data, int len) {

        try {
            ByteArrayOutputStream buffer = freeBufferQueue.take();
            buffer.write(data, 0, len);
            boolean added = receiveQueue.offer(buffer);
            if (!added) {
                LOGGER
                    .error("The message was not added to the receive queue: {}",
                        ByteUtils.bytesToHex(buffer.toByteArray()));
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Add buffer to receive queue failed.", ex);
        }

    }

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

    private void stopReceiveQueueWorker() {
        LOGGER.info("Stop the receive queue worker.");
        receiverRunning.set(false);
        receiverWorkerEnabled.set(false);

        try {
            receiveQueueWorker.interrupt();

            receiveQueueWorker.join(1000);

            LOGGER.info("receiveQueueWorker has finished.");
        }
        catch (Exception ex) {
            LOGGER.warn("Interrupt receiveQueueWorker failed.", ex);
        }

        try {
            LOGGER.info("Shutdown receiveWorker.");
            receiveWorker.shutdown();
            receiveWorker.awaitTermination(2000, TimeUnit.MILLISECONDS);
        }
        catch (Exception ex) {
            LOGGER.warn("Wait for shutdown of receiveWorker failed.", ex);
        }

        receiveQueueWorker = null;
    }

    private void processReceiveQueue() {
        LOGGER.info("The receiveQueueWorker is ready for processing.");

        while (receiverRunning.get()) {
            ByteArrayOutputStream bytes = null;
            try {
                // get the message to process
                bytes = receiveQueue.take();

                if (bytes != null) {
                    // process
                    try {
                        getMessageReceiver().processMessages(bytes);
                    }
                    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);
            }
            finally {
                if (bytes != null) {
                    bytes.reset();
                    freeBufferQueue.add(bytes);
                }
            }
            bytes = null;
        }

        LOGGER.info("The receiveQueueWorker has finished processing.");
        receiveQueueWorkerThreadId.set(0);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy