0

Im currently writing a program for Uni that uses RFID trackers to track the movement of RFID tags as they pass through them. This is mimicking the movement of a wallet as it passes through rooms.

I have added a button to my GUI which initializes the trackers and updates my database as the tags pass through them. They stay online for 60 seconds and all work fine. However, once this button is pressed, my GUI freezes until the process has stopped. This prevents a table on my GUI updating.

I realize that i need to use threads, ive found SwingWorker which looks good however after a while of trying to understand it i cant seem to figure it out.

I am wondering if someone with more experience than me could tell me if SwingWorker will work for what i need and give me some advice on how to use it.

Here are some snippets of my code:

Tracking JButton:

JButton trackingButton = new JButton("Start Tracking");
trackingButton.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        try {
            RFIDHandler.openRFID(); //runs the RFID readers
        } catch (PhidgetException | SQLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
    }
});

The start of my openRFID method:

public static void openRFID() throws PhidgetException, SQLException {   

    RFID phid1 = new RFID();
    RFID phid2 = new RFID();
    WalletDAO dao = new WalletDAO();

    // Open and start detecting rfid cards
    phid1.open(5000);  // wait 5 seconds for device to respond
    phid2.open(5000);  // wait 5 seconds for device to respond


    phid1.setAntennaEnabled(true);
    phid2.setAntennaEnabled(true);

    // Make the RFID Phidget able to detect loss or gain of an rfid card
    phid1.addTagListener(new RFIDTagListener() {
        public void onTag(RFIDTagEvent e) {
            try {
                Wallet w = dao.getWallet(e.getTag());                   
                String location = w.getLocation();
                System.out.println(location);

                if(Objects.equals(location, "Room A")) {
                    System.out.println(w.getName() + "'s Wallet read");
                    w.setLocation("Room B");
                    try {
                        dao.updateLocation(w);
                        WalletLocatorInterface.refreshTable(); // refreshes table on GUI
                        System.out.println("Currently in room B");
                    } catch (SQLException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }
                // ... else statement + another TagListener 

    System.out.println("Gathering data for 60 seconds\n\n");
    try {
        Thread.sleep(60000);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    phid1.close();
    phid2.close();
    System.out.println("\nClosed RFIDs");
}

Let me know if you need me to clear anything up, thanks.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
henry434
  • 97
  • 1
  • 9
  • Does `openRFID` update the UI? If not, you don't "need" a `SwingWorker`. If you'd like to get feedback from the process, to update the UI with, then you benefit from a `SwingWorker` – MadProgrammer Feb 12 '19 at 22:34
  • @MadProgrammer yeah, as the tag is scanned 'location' field in my database is updated and my gui is refreshed – henry434 Feb 12 '19 at 22:38

2 Answers2

2

Your problem is the Thread.sleep. You could accomplish what you are doing here with ScheduledExecutorService. Simply modify your code to this:

...
    System.out.println("Gathering data for 60 seconds\n\n");
    try {
        Thread.sleep(60000);
    } catch (InterruptedException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }

    Executors.newScheduledThreadPool(1).schedule(() -> {
        phid1.close();
        phid2.close();
        System.out.println("\nClosed RFIDs");
    }, 60, TimeUnit.SECONDS);
}

(By the way, I used a lambda expression, which can be explained here.)

Regarding SwingWorker, I don't have much experience. However, with SwingWorker, you can do stuff in the background and not make your GUI stutter when updating. However, if you don't really update the GUI, like @MadProgrammer said, then you wouldn't really benefit from using a SwingWorker.

Leftist Tachyon
  • 304
  • 1
  • 15
  • hmm that doesnt seem to work.. im getting this error: Cannot make a static reference to the non-static method schedule(Runnable, long, TimeUnit) from the type ScheduledExecutorService – henry434 Feb 12 '19 at 22:46
  • when i make it static i get more errors on schedule: schedule cannot be resolved or is not a field – henry434 Feb 12 '19 at 22:47
  • 1
    @henry434 that's because a SchedulerExecutorService is used as an instance, not a static class. To return a ScheduledExecutorService use the factory methods provided by Executors. – LppEdd Feb 12 '19 at 22:48
  • My bad. Please see edits. – Leftist Tachyon Feb 12 '19 at 22:49
  • @LeftistTachyon wow! yes that solved it thank you very much my man! – henry434 Feb 12 '19 at 22:52
0

When you try and design something like this, you need to re-think your approach in a much more detached way.

The first thing I might do, is define a delegate that can be passed information, this makes it easier to deal with, as you don't need to mess about with SwingWorkers PropertyChangeListener and it allows you to decouple the code...

public interface RFIDWorkerDelegate {
    public void locationsUpdated(List<String> locations);
}

Next, you need to take the operations which are possibly going to block the UI and place them into the SwingWorkers doInBackfground.

You need to then decide the best time to update the UI with the changes you want. You then call publish to "publish" the results. These will be passed to the process method which is called within the context of the Event Dispatching Thread, for example...

public class RFIDWorker extends SwingWorker<Void, String> {

    private RFIDWorkerDelegate delegate;

    public RFIDWorker(RFIDWorkerDelegate delegate) {
        this.delegate = delegate;
    }

    @Override
    protected void process(List<String> chunks) {
        delegate.locationsUpdated(chunks);
    }

    @Override
    protected Void doInBackground() throws Exception {
        RFID phid1 = new RFID();
        RFID phid2 = new RFID();
        WalletDAO dao = new WalletDAO();

        // Open and start detecting rfid cards
        phid1.open(5000);  // wait 5 seconds for device to respond
        phid2.open(5000);  // wait 5 seconds for device to respond

        phid1.setAntennaEnabled(true);
        phid2.setAntennaEnabled(true);

        // Make the RFID Phidget able to detect loss or gain of an rfid card
        phid1.addTagListener(new RFIDTagListener() {
            public void onTag(RFIDTagEvent e) {
                Wallet w = dao.getWallet(e.getTag());
                String location = w.getLocation();
                System.out.println(location);

                publish(location);

                if (Objects.equals(location, "Room A")) {
                    System.out.println(w.getName() + "'s Wallet read");
                    w.setLocation("Room B");
                    try {
                        dao.updateLocation(w);
                        WalletLocatorInterface.refreshTable(); // refreshes table on GUI
                        System.out.println("Currently in room B");
                    } catch (SQLException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }
            }
        });

        System.out.println("Gathering data for 60 seconds\n\n");
        try {
            Thread.sleep(60000);
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        phid1.close();
        phid2.close();
        System.out.println("\nClosed RFIDs");
        return null;
    }
}

Then you might call it doing something like...

new RFIDWorker(new RFIDWorkerDelegate() {
    @Override
    public void locationsUpdated(List<String> locations) {
        // Update the UI as required
    }
});
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366