-2

My application is supposed to read and write to a Serial Port. The data is read in the EventListener of the PortReader class. I wish to assign this data to a global String variable (private String sPortReaderString) for further use. The global string variable should return its value by using a method called getPortReader() which simply returns the string sPortReaderString. In the application's JFrame I open the serial port connection, send a command for which I automatically receive a reply from serial device, and I display that reply output in a label. The problem is that the label is always blank since the sPortReaderString returned from getPortReader() has nothing assigned to it. I am very sure that the sPortReaderString gets assigned a value in the EvenListener. The problem seems to be that the method getPortReader() in the JFrame is called before any value had enough time to get assigned to sPortReaderString. Please take a look at my otuput and the code below:

The following is the output I get:

sPortReaderString:
PortReader

Short example of portsMethod in JFrame:

public class MyJFrame extends javax.swing.JFrame {

    public MySerialPort msp = new MySerialPort();   

    public MainJFrame() {
        portsMethod();
    }

    private void portsMethod() {

        msp.getPortNames();//Gets the name of the port (COM1 in my case)
        msp.openPort();//Opens COM1 port
        msp.getFirmwareVersion();//Prompts for device firmware version by sending a string command
        msp.getPortReader();//Reads the reply from device

    }

}

The following is the example of my Serial Port class:

public class MySerialPort {

    private SerialPort serialPort;
    private int iBaudRate = SerialPort.BAUDRATE_57600;
    private int iDataBits = SerialPort.DATABITS_8;
    private int iStopBits = SerialPort.STOPBITS_1;
    private int iParity = SerialPort.PARITY_NONE;
    private String sPortName;
    private String sPortReaderString = "";
    private StringBuilder sbPortReaderString = new StringBuilder();

    public void getFirmwareVersion() {
        sendPortCommand("<FirmVer>\r\n");
    }

    public void clearPortReader() {
        sbPortReaderString.setLength(0);
    }

    public String getPortReader() {
        System.out.print("sPortReaderString: " + sPortReaderString);
        return sPortReaderString;
    }

    public void getPortNames() {
        String[] sPorts = SerialPortList.getPortNames();
        sPortName = sPorts[0];
    }

    public void openPort() {

        serialPort = new SerialPort(sPortName);

        try {

            if (serialPort.openPort()) {

                if (serialPort.setParams(iBaudRate, iDataBits, iStopBits, iParity)) {

                    serialPort.addEventListener(new PortReader(), SerialPort.MASK_RXCHAR
                            | SerialPort.MASK_RXFLAG
                            | SerialPort.MASK_CTS
                            | SerialPort.MASK_DSR
                            | SerialPort.MASK_RLSD);

                } else {
                    serialPort.closePort();
                }

            } else {}
        } catch (SerialPortException | HeadlessException ex) {}
    }

    private void sendPortCommand(String sSendPortCommand) {

        if (sSendPortCommand.length() > 0) {
            try {
                serialPort.writeBytes(sSendPortCommand.getBytes());
            } catch (Exception ex) {}
        }
    }

    private class PortReader implements SerialPortEventListener {

        private String sBuffer = "";

        @Override
        public void serialEvent(SerialPortEvent spe) {

            if (spe.isRXCHAR() || spe.isRXFLAG()) {

                if (spe.getEventValue() > 0) {

                    try {

                        //Read chars from buffer
                        byte[] bBuffer = serialPort.readBytes(spe.getEventValue());
                        sBuffer = new String(bBuffer);

                        SwingUtilities.invokeAndWait(
                                new Runnable() {

                                    @Override
                                    public void run() {
                                        sbPortReaderString.append(sBuffer);
                                    }
                                });

                        sPortReaderString = new String(sbPortReaderString);
               //if I print sPortReaderString in here it is not blank and has the correct value

                        System.out.print("PortReader");

                    } catch (SerialPortException | InterruptedException | InvocationTargetException ex) {
                    }
                }
            }
        }
    }
}
jadrijan
  • 1,438
  • 4
  • 31
  • 48
  • 3
    You should at least print or log your exceptions. You are catching and dropping exceptions in a couple of places that may be masking errors. – Gray Feb 29 '12 at 20:14
  • For the purposes of making demo code shorter, I took out the exceptions. I am not getting any error exceptions. – jadrijan Feb 29 '12 at 20:20
  • 1
    Have you tried making your `sPortReaderString` be `volatile`? Is it being accessed by multiple threads? – Gray Feb 29 '12 at 20:24
  • 1
    You say `msp.getPortReader();//Reads the reply from device` That is not the case. If only prints the what was read. Are you sure that it has been read by the time you make this call? – Jacob Tomaw Feb 29 '12 at 20:25
  • @Gray - yes, i have tried making my sPortReaderString volatile. in the method of the jframe i don't think i have any threads. – jadrijan Feb 29 '12 at 20:26
  • @JacobTomaw - that is exactly what I tried to explain. how do I make sure that it has been read by the time I make the call msp.getPortReader? thanks – jadrijan Feb 29 '12 at 20:28

1 Answers1

2

It seems pretty logical to me:

You have a first event handling method execution in the EDT which gets the firmware version and then gets the port reader. Getting the firmware version causes an event to be received, in a different thread (and thus in parallel to the execution of portsMethod() in the EDT).

The event handling code invokes SwingUtilities.invokeAndWait(). This call thus waits for the first event handling methd to complete, and then appends the received string to sbPortReaderString. This append is thus done after the portsMethod has completed.

The serial port offers an event-based mechanism. I would simply use it to propagate the event to one or several listeners in the EDT:

// accessed only from the EDT
private List<MyPortListener> portListeners = new ArrayList<MyPortListener>();

public void addMyPortListener(MyPortListener listener) {
    portListeners.add(listener);
}

public void removeMyPortListener(MyPortListener listener) {
    portListeners.remove(listener);
}

... 

    @Override
    public void serialEvent(SerialPortEvent spe) {
        ...
        final String receivedString = ...;
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                for (MyPortListener listener : portListeners) {
                    listener.stringReveivedFromSerialPort(receivedString);
                }
            }
        });
    }

Side note: your code is hard to understand mainly because your variables and methods are badly named.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • thank you very much, can you please direct me to a short example of how i would do this? i have never done anything similar. – jadrijan Feb 29 '12 at 20:32
  • One more question Sir. Where does the MyPortListener come from? Thank you – jadrijan Feb 29 '12 at 21:21
  • 1
    You define it. It's an interface having only one method: `void stringReveivedFromSerialPort(String)`. – JB Nizet Feb 29 '12 at 21:22
  • thank you for your patience...could you please be a bit more specific since I am a beginner. by interface, do you mean a class (like my JFrame) or...? thank you again very much – jadrijan Feb 29 '12 at 21:27
  • 1
    Interfaces are a core concept of Java, and are used everywhere in Swing. See http://docs.oracle.com/javase/tutorial/java/concepts/interface.html – JB Nizet Feb 29 '12 at 22:04
  • dear friend, i have spent the whole day yesterday reading about the interfaces, but i am still confused with the example you have given me. in the example i don't understand where what goes (in what class) and which class implements the interface MyPortListener. I am guessing that code under and including SwingUtilities.invokeLater goes below my sPortReaderString = new String(sbPortReaderString); and I pass the sPortReaderString to stringReveivedFromSerialPort(). Where do addMyPortListener and removeMyPortListener go? I can't find a single example on internet about this. – jadrijan Mar 01 '12 at 15:45
  • The portListeners field and the add/removeMyPortListener methods belong to the MySerialPort class. In your serialEvent method, instead of your `SwingUtilities.invokeAndWait()` call, you should have my `SwingUtilities.invokeLater()` call. You add a MyPortSerialListener to the MySerialPort instance exactly as you would add an ActionListener to a JButton instance. If you use Swing, you should know how to do that. Listeners are a core part of Swing. See http://docs.oracle.com/javase/tutorial/uiswing/events/intro.html. – JB Nizet Mar 01 '12 at 15:54