LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Most Efficient method of refreshing panels

Hi,
I'm writing a program in LabWindows/CVI that continuously reads data from the comport, refreshing the GUI with the new information. My first attempt to do this was like so:

while (!done) {
RefreshFromComPort (); // my function
ProcessSystemEvents (); // Standard CVI function
}

This worked OK except that every time the user activated a button or pulled down a menu, ProccessSystemEvents wouldn't return until it was released, so the panels would not refresh during that time.

I've found two ways to get something to happen during this time: run it in a callback or run it in a separate thread. However, my attempts at using either of these methods resulted in performance that was less than ideal.

When using the callback method, I had the RefreshFromComPort function called by a Timer with an interval of 0. This allowed the panels to refresh at a reasonable rate, and continue to do so when they would normally have been blocked. However, interaction with controls in this version was quite sluggish, especially noticeable with text boxes. Anyone know how to improve this? (Note to CVI devel team: I don't know how 0 delay timers are currently implemented, but the functionality that I feel would be most useful is to push a EVENT_TIMER_TICK onto the event queue when the program is first started and then every time the event is processed (it's callback called) after that. That way these events would be created as fast as they can be handled, but still allow other events to get into the event queue ASAP).

The third method I attempted was running the RefreshFromComPort function (in a loop) in its own thread, and calling RunUserInterface in main. In my original code, the RefreshFromComPort was using about 2/3 of the CPU time and the ProcessSystemEvents the other 1/3. I expected that by running the comport code in a separate thread I should be able to get better performance since the RunUserInterface code could be run while the comport thread is waiting for data. However, to my surprise, when I rewrote the program to use separate threads, the I actually got much worse performance. I don't know why this is so, but have a few ideas. I noticed that in this version, I could see each control refreshing individually (I update 1 panel with up to 100 numeric controls for each call to RefreshFromComPort), whereas in the other 2 versions, LabWindows didn't have a chance to handle any redraw events until I had changed all the controls I wanted to change on a panel. Could this be what is causing RunUserInterface to use more time that it used to? Is there a way for me to (partially) freeze the panel, to let me do the updates I want, and then unfreeze it when I'm ready for it to redraw the panel in light of the changes? (while preferably still allowing the user to interact with the panel 😉 Any other ideas on why this is slower?

I figured that since my program is a textbook example of what LabWindows/CVI was designed for, there is probably a fairly simple way to do what I want, and I am simply missing it. I will greatly appreciate any suggestions for fixing one of the three methods I presented above or a different way of approaching the problem that will work better.

Thank you.
D. Jackson Peacock

PS. I'm using LabWindows/CVI 5.5 in Windows 2000
0 Kudos
Message 1 of 4
(3,445 Views)
Actually, the best way to separate the User Interface and serial communications processes is through multithreading, in which you can actually combine a callback model for your COM port with this as well. I will first address the two tactics you tried and then provide you with a solid workaround and explanation of an example that I have made describing the preferred method.

Your method of using a Timer callback doesn't really isolate your serial communications from the UIR and I don't recommend doing this because you are relying on software determination of the Timer's event processing as to when communications take place and since the Timer is actually part of the UI this causes the connection between the two.

As for your multithreading model I don't really understand what you were trying to accomplish with this, but to answer your variety of questions look at the following:

1) The reason that your application takes up so much cpu time is because its thread priority is set to high, use the SetSleepPolicy function to "sleep more" to change this.

2) If you want to freeze your UIR, you could simply call Delay or the SDK function Sleep and your UI thread will cease to process messages and wait until those functions return. The messages will still be queued for it but you can't see the buttons moving because the panel can't redraw. You could also try just swallowing events.

3) I think that just the general format of your COM thread caused the poor performance, because if done correctly it will work better.

I have attached an example that actually shows quite a bit about multithreading, separating your UI and serial communications processes, as well as how threads get their own message queue that will receive messages for thread-specific processes that generate them and when you use message processing functions like ProcessSystemEvents in that thread it only processes messages for that thread and calls the corresponding callback in that same threads process. This example uses the InstallComCallback function to set up its COM port event processing.

To use it, just run the project, then click the Start Callback Test button. This will send a long string out of COM1 on your machine (you will need a null-modem cable connecting COM1 and COM2) from the UI thread, and will immediately put the UI thread to sleep while this asynchronous communication takes place. Meanwhile, the second thread will process messages generated by COM2 (which is receiving the string from COM1) and call a callback function to handle them, all within a separate thread from the UI. Since they are separate threads, the UI thread being asleep does not affect the serial thread at all.

Jason F.
Applications Engineer
National Instruments
www.ni.com/ask
Message 2 of 4
(3,445 Views)
Thank you for your response. I think that the method you gave to uncouple the serial code from UI code will be useful. I also agree with you that the reason the threaded version is slower is because of the way that I wrote my function, but I'm not sure why. Let me better explain what I'm doing.

I'm not really reading the comport continuously. What I'm really doing each time I need data is sending a command over the serial port and then reading the response. I have read everything I need from the comport before I return from the RefreshFromComPort function, so I don't have to worry about the comport buffers overflowing. I also don't have to ensure that reading happens at any set timeframe, as the only use I have for the data I'm reading is to display
it on the screen. So my main concern right now in not uncoupling the *serial*port* code from the UI but allowing the application to be *refreshing* continuously without causing interaction with the panel to become sluggish. (Although latter I plan on moving the serial port code to a separate thread to allow pipelining of commands, and the method you presented in the sample program will work great for this.) Refreshing the panel involves doing several things
* send command & read back response
* parse response and compute values
* SetCtrlVal for a bunch of numeric controls
* SetCtrlAttribute (ATTR_TEXT_BGCOLOR) for all the numeric controls to indicate if they are within acceptable limits

I have a feeling that the reason the threaded version is slower is not the serial port part of the refresh routine but the drawing part. However, I don't understand why.
thanks,
jackson
0 Kudos
Message 3 of 4
(3,445 Views)
Daniel,

Did you ever solve this problem? I am trying to do the same thing you were I think.

Regards,

Derrick
0 Kudos
Message 4 of 4
(3,445 Views)