-1

This would be my first question on StackOverflow, but certainly not the first time it's been helpful. However I didn't get any further trying the different possible solutions I found until now.

I'm currently fulfilling an internship and creating a utility to visualise the information read through a USB-to-CAN interface - 'controller area network'. Simply put: it reads out messages from the CAN connector on an embedded system. Each message consists of an identifier field (containing a source address, destination address, identifier and so on) + 8 data bytes max. These fields are converted into strings and I want to add them to a JList every time a new message is received. The JList 'log_listview' is initialized with a new DefaultListModel 'listModel' and it's added to the 'Can_Panel', the ContentPane. The reading happens in a separate thread called 'vci_thread', which is initialized and started right after creating the panel.

Reading starts only after about 5 seconds, the time it takes to initialize the USB-to-CAN interface. At first sight, the JList performs well. Each received message is correctly added to the listModel and I didn't even notice there were errors because the program simply continues with no missing elements, auto scrolling and autmatic removal of the first element when listModel.size() > 99...

Now and then I get the following error (The "Size: #" is the debug output of the actual number of elements in the JList):

Size: 60
Size: 61
Size: 62
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at javax.swing.BufferStrategyPaintManager.flushAccumulatedRegion(Unknown Source)
    at javax.swing.BufferStrategyPaintManager.endPaint(Unknown Source)
    at javax.swing.RepaintManager.endPaint(Unknown Source)
    at javax.swing.JComponent._paintImmediately(Unknown Source)
    at javax.swing.JComponent.paintImmediately(Unknown Source)
    at javax.swing.RepaintManager$3.run(Unknown Source)
    at javax.swing.RepaintManager$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.access$1000(Unknown Source)
    at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Size: 63
Size: 64
Size: 65
Size: 66

Also, when a lot of messages are coming in quickly, i regularily get the following error. I believe it has something to do with the removal of the first index as the size is currently 100:

Size: 100
Size: 100
Size: 100
Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: 99 >= 99
    at java.util.Vector.elementAt(Unknown Source)
    at javax.swing.DefaultListModel.getElementAt(Unknown Source)
    at javax.swing.plaf.basic.BasicListUI.paintCell(Unknown Source)
    at javax.swing.plaf.basic.BasicListUI.paintImpl(Unknown Source)
    at javax.swing.plaf.basic.BasicListUI.paint(Unknown Source)
    at javax.swing.plaf.ComponentUI.update(Unknown Source)
    at javax.swing.JComponent.paintComponent(Unknown Source)
    at javax.swing.JComponent.paint(Unknown Source)
    at javax.swing.JComponent.paintToOffscreen(Unknown Source)
    at javax.swing.BufferStrategyPaintManager.paint(Unknown Source)
    at javax.swing.RepaintManager.paint(Unknown Source)
    at javax.swing.JComponent._paintImmediately(Unknown Source)
    at javax.swing.JComponent.paintImmediately(Unknown Source)
    at javax.swing.RepaintManager$3.run(Unknown Source)
    at javax.swing.RepaintManager$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.prePaintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.access$1000(Unknown Source)
    at javax.swing.RepaintManager$ProcessingRunnable.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Size: 100
Size: 100

Here's the code that's executed after a new CAN message is read and the bytes have been converted into strings:

    // Add a new element containing the strings:
    listModel.addElement(counter + ": " + src_addr_hex + " " + dest_addr_hex + " - " + predef_hex + " - " + str_data);

    // Auto scroll down after the element has been added
    if (listModel.size()-1 >= 0) {
        try {
            log_listview.ensureIndexIsVisible(listModel.size()-1);
        } catch (Exception ex) {
        //
        }
    }

    // When listModel contains 100 elements, remove the first index
    if (listModel.size()>99) {
        listModel.remove(0);
    }

This the reading, together with this code is executed inside a while() loop and incorporates a Thread.sleep(1) every time to limit cpu usage. I've ensured with a try{} catch() that when there's no new message to be read, the string conversion and the addition to the JList is simply skipped. When messages come in faster than one cycle of this loop, they are simply cached and handled one by one until eventually my program catches up. It's a rather extensive question, but I'm hoping one could help me out.

Update: I've migrated the code into a new runnable 'update_JList' and calling it with the SwingUtilities.invokeLater method right after reading a line. I'm not getting any errors and it's great to see how smooth the GUI updates. However, now I'm facing another problem of duplicates or missing elements in the JList:

Screenshot

On the left is the debug output showing every incoming message one by one. On the right is the JList showing the message with source address 45 14 times.

mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • a) make sure the list update happens on the EDT b) assuming the ensure is done in a listDataListener, make sure it happens _after_ the list is ready with any internal update, that is wrap your code into a invokeLater – kleopatra Apr 17 '13 at 15:06
  • something wrong in the code you are not showing ... note, we can't mind read your code, at least not at a distance ;-) Show an SSCCE that demonstrates the problem – kleopatra Apr 18 '13 at 11:22
  • Currently I can't think of an SSCCE to replicate the problem and I'm afraid you'd need a USB-to-CAN interface and the same embedded system to test with :) The problem of the duplicates seems to be solved by setting a boolean after reading a new CAN message and resetting it right after adding the element. Only once in a while there's one element missing in comparison with the debug output, though there's no code between these two lines: System.out.println("Strings"); & SwingUtilities.invokeLater(update_JList); – Pieter Vochten Apr 18 '13 at 12:13
  • no, you can mock _any_ input ... – kleopatra Apr 18 '13 at 12:13

1 Answers1

1

The reading happens in a separate thread called 'vci_thread', which is initialized and started right after creating the panel.

Updates to the GUI must be done on the EDT, not a separate Thread. Read the section from the Swing tutorial on Concurrency for more information.

You can either use a SwingWorker or if you want to continue using your existing Thread, then you will need to use SwingUtilities.invokeLater() whenever you want to update the model.

camickr
  • 321,443
  • 19
  • 166
  • 288