Java jssc read from multiple COM ports, simultaneously, write all data out on one port

310 Views Asked by At

I have several COM ports bringing data in every 1-2 seconds. Each line, from every device, starts with a $ and ends with a CR and LF. Every line is a different length, none more than 82 bytes long.

I am trying to combine four 4800 baud inputs and one 34800 baud input into one output line at 192k baud.

You can see my code here: https://github.com/ian5142/nema0183_aggregator

https://github.com/ian5142/nema0183_aggregator/find/master

  • NEMA_aggregrator is the main
  • RS232Control contains all of the JSSC stuff.
  • NEMADateUpdate is called by RS232Control when GPS data is sent in. Changes the Date in one of the lines. Works fine.

Relevant code from main:

RS232Control gps = new RS232Control("COM32", 4800, true);
        while (true) {
            String line = gps.testRead2();
            sentences.add(line);
            gps.changePort("COM41", 115200, false);
            gps.testWrite(line);
            
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                Logger.getLogger(Nema0183_aggregator.class.getName()).log(Level.SEVERE, null, ex);
            }
            for (int i = 0 ; (i < 8) && (!line.startsWith("$GPGLL")) ; i++ ) {
                gps.changePort("COM32", 4800, true);
                line = gps.testRead2();
                sentences.add(line);
                gps.changePort("COM41", 115200, false);
                gps.testWrite(line);

                try {
                    Thread.sleep(500);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Nema0183_aggregator.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            gps.changePort("COM39", 4800, false);
            line = gps.testRead2();
            sentences.add(line);
            gps.changePort("COM41", 115200, false);
            gps.testWrite(line);
            
            gps.changePort("COM32", 4800, true);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ex) {
                Logger.getLogger(Nema0183_aggregator.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

Then RS232Control is here:

import java.util.ArrayList;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import jssc.*; // Java Simple Serial Connector, the library that contains the serial methods
import static nema0183_aggregator.RS232Control.serialPort;

/**
 *
 * @author Ian Van Schaick
 */
public class RS232Control {

    static SerialPort serialPort;
    String portName;
    static long portOpen;
    StringBuilder message;
    Boolean receivingMessage;
    SerialPortReader reader;
    String readLine;
    Boolean acknowledge;
    int baud;
    boolean gpsData;
    NEMADateUpdate gpsUpdate;
    String lineSep;

    /**
     * 
     * @param portNum
     * @param portbaud
     * @param gps 
     */
    public RS232Control(String portNum, int portbaud, boolean gps) {
        gpsData = gps;
        if (gpsData == true) {
            gpsUpdate = new NEMADateUpdate ();
        }
        portName = portNum;
        baud = portbaud;
        serialPort = new SerialPort(portName);
        message = new StringBuilder();
        receivingMessage = false;
        reader = new SerialPortReader();
        readLine = "";
        acknowledge = false;
        lineSep = System.getProperty("line.separator");
        openP();
    }

 protected void changePort (String portNum, int portbaud, boolean gps) {
        close();
        gpsData = gps;
        if (gpsData == true) {
            gpsUpdate = new NEMADateUpdate ();
        }
        portName = portNum;
        baud = portbaud;
        serialPort = new SerialPort(portName);
        message = new StringBuilder();
        receivingMessage = false;
        reader = new SerialPortReader();
        readLine = "";
        acknowledge = false;
        lineSep = System.getProperty("line.separator");
        openP();
    }
    
    /**
     * Opens a COM port at the specified settings (baudrate 8N1)
     * Can throw an error opening the port
     */
    private void openP() {
        try {
            serialPort.openPort();
            serialPort.setParams(baud,
                    SerialPort.DATABITS_8,
                    SerialPort.STOPBITS_1,
                    SerialPort.PARITY_NONE);

            serialPort.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
            int mask = SerialPort.MASK_RXCHAR;
            serialPort.setEventsMask(mask);
            serialPort.addEventListener(reader);
            serialPort.setRTS(false);
            serialPort.setDTR(false);
            acknowledge = true;
        } catch (SerialPortException ex) {
            Logger.getLogger(RS232Control.class.getName()).log(Level.SEVERE, null, ex);
            System.out.println("There is an error opening port т: " + ex);
        }
    }
    
    /**
     * Closes the serial port, can throw a SerialPortException error.
     *
     * @return
     */
    private boolean close() {
        boolean success = false;
        try {
            serialPort.closePort();
            success = true;
        } catch (SerialPortException ex) {
            Logger.getLogger(RS232Control.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }
/**
     * Opens the serial port. Tries to read a string from the serial port.
     * Closes the serial port.
     *
     * @return Returns the byte array read from the serial port.
     */
    protected String testRead2() {
        String line = "";
        ArrayList <String> readList = new ArrayList <String> ();
        boolean lineFin = false;
        for (int i = 0; i < 100 && (!lineFin); i++) {
            try {
                line =  line + serialPort.readString(1);
            } catch (SerialPortException ex) {
                Logger.getLogger(RS232Control.class.getName()).log(Level.SEVERE, null, ex);
            }
            if (line.endsWith(lineSep)) {
                lineFin = true;
            }
        if (gpsData == true) {
            line = gpsUpdate.dateUpdate(line);
        }
    }
        return line;
    }
    
    /**
     * Writes the String message to the serial port
     *
     * @param message The string to write to the serial port
     * @return Returns true if the write was successful
     */
    protected boolean testWrite(String message) {
        boolean success = false;
        
        try {
            if ( (!message.isBlank() ) && message.startsWith("$") ) {
                success = serialPort.writeString(message);
            }
        } catch (SerialPortException ex) {
            Logger.getLogger(RS232Control.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }
}

/**
 * In this class must implement the method serialEvent, through it we learn
 * about events that happened to our port. But we will not report on all events
 * but only those that we put in the mask. In this case the arrival of the data
 * and change the status lines CTS and DSR
 */
class SerialPortReader implements SerialPortEventListener {

    /**
     * Reads the data bit by bit from the serial port Can throw a
     * SerialPortException error
     *
     * @param event
     */
    @Override
    public void serialEvent(SerialPortEvent event) {
        if (event.isRXCHAR() && event.getEventValue() == 10) {
            try {
                byte buffer[] = serialPort.readBytes(10);
            } catch (SerialPortException ex) {
                System.out.println("Error in receiving string from COM-port: " + ex);
            }
        }
    }
    
    /**
     * Prints out the message read from the serial port
     *
     * @param message
     */
    protected void processMessage(String message) {
//        System.out.println(message);
    }
}

I tried to use a for / while loop to iterate over the COM ports but this means that I miss incoming data from the other ports.

Any ideas on how to do this?

2

There are 2 best solutions below

0
On

Sounds like barcode scanning. But that's not important.

Keep separate instances of RS232Control for each port. Poll them repeatedly, and put any data into a queue.

In another thread take from queue, merge and send forward.

Sometimes multi-threaded solutions are easier. It's worth your while.

0
On

I would suggest using a global list variable to indentify each COM port, probably with a class, to create two members: a boolean for when data is read from the COM port and another boolean for when that COM port’s data has already been checked by you when you iterate over them in a while loop. If they are all on separate threads, then they can all have data received concurrently and the while loop will continue to iterate over all of the COM ports until they all have their alreadyChecked (data already received and sent to main port) booleans to true. This can be done by doing a for loop within the while loop to count how many class instances still have there alreadyChecked boolean to false I hope this helps.