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

org.bidib.jbidibc.jserialcomm.JSerialCommSerialConnector Maven / Gradle / Ivy

The newest version!
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();
            }
        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy