2

I have a program to get the values from a Bar code scanner(using jssc library) and it returns value as expected using event listener but I need to access that value from another class.

I tried to instantiate BarcodeScanner class to main method (ProcessTicket class) and called scannerHandler method and also called the getter method of model class to retrieve value but the getter method runs before the scan is completed. Could you please help me to achieve this?

    public class BarcodeScanner {
            public static Object SerialPortReader;
            static SerialPort serialPort;
         public void scannerHandler() {
                serialPort = new SerialPort("COM4");      
    try{
       serialPort.openPort();//Open serial port
      //Set params. Also set params by this string: 
       serialPort.setParams(9600, 8, 1, 0);
       serialPort.setParams(9600, 8, 1, 0);
       serialPort.writeString(new String(new byte[]{0x02})); //triggers barcode scanner
       serialPort.addEventListener(new SerialPortReader());//Add SerialPortEventListenerS
    } catch (SerialPortException ex) {
                    System.out.println(ex);
                }
            }

        public static class SerialPortReader implements SerialPortEventListener {
                String str;
                String value;

                public void serialEvent(SerialPortEvent event) {
                    if (event.isRXCHAR() && event.getEventValue() > 0) {//If data is available
                        //Check bytes count in the input buffer
                        try {
                            byte[] bytesCont = serialPort.readBytes(14);
                            str = new String(bytesCont);
                            ModelClass modelClass = new ModelClass();
                            modelClass.setBarcodeValue(str);
                        } catch (SerialPortException e) {
                            e.printStackTrace();
                        }
                    }
        }

My ProcessTicket.java Class

public class ProcessTicket {
    public static void main(String[] args) throws SQLException, SerialPortException {
 BarcodeScanner bSC = new BarcodeScanner();
        bSC.scannerHandler();
       BarcodeScanner.SerialPortReader portReader = new BarcodeScanner.SerialPortReader();
       ModelClass modelClass = new ModelClass();
       String value = modelClass.getBarcodeValue();
       System.out.println(value);
    }
}
sanki
  • 59
  • 2
  • 9
  • Make a method in your listener which returns what you want. Store the listener object when you create it in a variable, and call that method. – Rogue Aug 11 '19 at 17:43
  • You have a "catch" with no "try". You should at least make your code compile before you start thinking about runtime errors. – Andrew Koster Aug 11 '19 at 18:26
  • It's also weird that once you read from the port, you put the result in this "ModelClass" object and then promptly discard it because it's a local variable and the method ends. What do you expect that to do? – Andrew Koster Aug 11 '19 at 18:37
  • Thank you for you input. I forgot to paste try block, and to add modelclass on the ProcessTicket. @Rogue How do I create a method in my listener? Could you please give me little example. I am still getting familiar with event and actionlisteners. – sanki Aug 11 '19 at 19:13
  • Just like any other method in any other class. `public int getMyInt() { return 42; }`. You make any method you want, it's your class. For a reference on general OOP programming, [Oracle has some in-depth beginner's tutorials](https://docs.oracle.com/javase/tutorial/java/index.html). Specifically to this problem, the "Classes and Objects" section. – Rogue Aug 11 '19 at 19:16
  • @Rogue I did that, it just returns null. I created a method getValue() that the returns value str . I tested my method using System.out and returns correct value. I debug the code to check the how its running and found that main method runs all the code from top to bottom and does not stop when I invoke when I instantiate and call the method. It runs getValue method and returns null before I am able to scan and get the value from the scanner. – sanki Aug 11 '19 at 20:51

1 Answers1

1

The main problem here is that you're treating an inherently asynchronous operation (reading from an external sensor in the real world) as if it's synchronous.

I simulated that external sensor stuff to make a standalone app that tests your business logic:

HowToRetrieveTheValueFromTheEventListenerFromAnotherClassInJava.java

package com.stackoverflow;

/**
 * https://stackoverflow.com/questions/57452205/how-to-retrieve-the-value-from-the-event-listener-from-another-class-in-java
 */
public class HowToRetrieveTheValueFromTheEventListenerFromAnotherClassInJava {

    public static void main(String[] args) {

        BarcodeScanner barcodeScanner = new BarcodeScanner((String barcode) -> {

            System.out.println("Barcode scanned: " + barcode);
        });

        barcodeScanner.startScan();

        MockUser.startScanningStuffLol();
    }
}

That call to MockUser.startScanningStuffLol() is only necessary because I'm testing this just in code, without using a real barcode scanner. Please don't focus on it. I'll post its implementation if you ask, but otherwise I'm assuming that your OS/Java/hardware are working the way they were designed to work, and you can just test this with those tools instead of my MockUser software simulation.

Here are the rest of the classes that you need to implement this:

BarcodeScannedCallback.java

package com.stackoverflow;

public interface BarcodeScannedCallback {

    void callback(String barcode);
}

Since we're dealing with an asynchronous operation, we can't just start it and then check for a return value, like we would with a synchronous operation. Instead, we need to pass in a function that will be called once the operation is complete, and just wait for it to finish. BarcodeScannedCallback is the signature of that function (in other words, a description of how that function needs to be structured). It takes one string parameter, and returns nothing.

The implementation of BarcodeScannedCallback is this function that I've already mentioned above, which I'm passing into the BarcodeScanner constructor:

(String barcode) -> {
    System.out.println("Barcode scanned: " + barcode);
}

As you can see, this function takes one string parameter, and returns nothing. So, it's an implementation of the BarcodeScannedCallback interface.

Now for the last class: the one that bridges our main method and the serial port, using the above interface.

BarcodeScanner.java

package com.stackoverflow;

public class BarcodeScanner implements SerialPortEventListener {

    private SerialPort serialPort;

    private final BarcodeScannedCallback callback;

    public void startScan() {

        try {

            serialPort = new SerialPort("COM4");
            serialPort.openPort();

            serialPort.addEventListener(this);

            // Also you can set params by this string: serialPort.setParams(9600, 8, 1, 0);
            serialPort.setParams(9600, 8, 1, 0);

            // Triggers barcode scanner.
            serialPort.writeString(new String(new byte[]{0x02}));

        } catch (SerialPortException ex) {

            System.out.println(ex);
        }
    }

    @Override
    public void serialEvent(SerialPortEvent event) {

        boolean isDataAvailable = event.isRXCHAR() && event.getEventValue() > 0;

        if (isDataAvailable) {

            try {

                byte[] bytesCont = serialPort.readBytes(14);
                String barcode = new String(bytesCont);

                callback.callback(barcode);

            } catch (SerialPortException ex) {

                System.out.println(ex);
            }
        }
    }

    public BarcodeScanner(BarcodeScannedCallback callback) {

        this.callback = callback;
    }
}

So here's the full lifecycle of these events:

  • You create a BarcodeScanner.
  • You tell the BarcodeScanner, via the implementation of BarcodeScannedCallback that you pass into its constructor, what code to run once it receives a barcode over the serial port.
  • You call startScan on the BarcodeScanner, which opens the serial port and starts waiting for the user to scan a barcode.
  • The user scans a barcode. This data is transmitted over the serial port. The operating system's implementation of SerialPort calls BarcodeScanner.serialEvent.
  • Your implementation of serialEvent does its validations, pulls the data from the serial port and converts it from bytes to a string, and calls the BarcodeScannedCallback function that was passed in at the beginning.

When I run this (with my MockUser class setting up a background thread that "scans" a barcode every 3 seconds), I get this output:

Barcode scanned: 420L0L
Barcode scanned: 007
Barcode scanned: 12345

In your case, you should be able to scan 3 barcodes with your real-world barcode scanner, and get the same results.

Note that you may need to do something to keep the main method's thread from ending prematurely, depending on the context that you're running this in.

If you're running it in an Android app or a web server, those frameworks keep their main thread running indefinitely, until you kill the app/server.

But if you're running it as a custom command-line app (which it seems like you're doing, based on the existence of a main method), you will need to do something to keep it alive until you decide to kill it. The simplest way is to put an infinite loop like while (true); on the last line of your main method.

Andrew Koster
  • 1,550
  • 1
  • 21
  • 31
  • Thank you so much for the detailed explanation. It is working as expected. I am creating an application using Javafx. The barcode is supposed to trigger when button is pressed and pull data from the database based on the barcode. – sanki Aug 12 '19 at 13:36