Search code examples
javaswingserial-portrunnable

Java Gui communication with serial port


I have Class, that receive and send information from serial port:

public class Terminal implements Runnable
{
    static LinkedList<String> receiver = new LinkedList<String>();
    public Terminal()
    {
        //...
    }
    public String getReceivedMessage()
    {
        String data = receivedMessfges.removeFirst();
        return data;
    }
    // Other function that perform connection to COM port
    // ...
}

Also I have Swing based gui class:

public class Gui extends JFrame
{
// Functions that display information, received from COM port
}

What is the right method of delivering information from Terminal to Gui using third class:

public class Monitor
{
    static Gui gui;
    static terminal terminal;
    public static void main(String args[])
    {
        monitor = new Monitor();
    }
    public Monitor()
    {
        gui = new Gui();
        terminal = new Terminal();
    }
    // Functions, that get information COM port
    // using getReceivedMessage() function
    // and display it on Gui
}

Thanks)


Solution

  • I would use the Monitor class to communicate between Gui and Terminal.

    If receivedMessfges.removeFirst(); doesn't return until it has received a complete message that should be displayed in the gui, you could just do this:

    Thread messageChecker = new Thread(new Runnable() {
        public void run() {
            while (!Thread.isInterrupted()) {
                String message = terminal.getReceivedMessage();
                // Prepare message for gui display
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        gui.methodToDisplayTheReceivedMessage(message);
                    }
                }
            }
        }
    }).start();
    

    somewhere inside the Monitor class.

    The code does the following:

    • create a new Runnable (anonymous class)
      • With a run() method in which we
        • check if the thread has been interrupted
        • if it has, stop
        • if it hasn't, wait until we have a new message from the and send it to the gui.
    • create a new Thread set to execute the run() method of our new Runnable
    • start this Thread.
    • Assign a reference to the Thread to the variable messageChecker.

    To stop the Thread, simply call messageChecker.interrupt();.

    If, on the other hand, terminal.getReceivedMessage(); return only partial messages, i.e. what has been received up until we call it, I think the best approach would be to use the Observer pattern.

    • make the terminal class implement the interface Observable
    • make the Monitor class implement the interface Observer

    Then, you need to write code to notify the observers of a complete message as soon as the terminal class has one. I would suggest to have a Thread internal to the Terminal class checking periodically for new data. As soon as it has a complete message, it should call notifyObservers(message) (with message obviously being a variable of type String containing the complete message).

    In the Monitor class, you have to have an update(Observable o, Object arg) method to satisfy the Observer interface:

    // In the Monitor class
    void update(Observable terminal, Object message) {
        SwingUtilities.invokeLater(new Runnable() {
            gui.methodToDisplayTheReceivedMessage((String)message); // This cast is safe, since we only ever call notifyObservers() with a string argument.
        }
    }
    

    Last but not least, you need to call terminal.addObserver(this); from the Monitor class (from the constructor, probably), otherwise notifyObservers() will notify all your 0 observers and nothing happens.

    Googling for the "java observer pattern" yields lots of useful resources and examples, in case you want to know more about it.