LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Can CVI do synchronous multi threads?

Thank you.

In fact my real code is pretty much the same as the sample codes I posted. In my real code, I have a similar "Ok" callback to do a while(1) loop DAQ unless some of the DAQ data exceed some limits. Therefore, you can say while DAQ is working, the main thread is locked in the while(1) loop just like the sample "OkCallback". In my secondary thread, i.e. the async timer callback, DAQ data is simply displayed in numeric boxes and plotted in the graph which was created in main thread. Since async timer is totally an independent thread on the main thread, I cannot understand why firing timing will be an issue for display. So far, I am pretty sure there is nothing wrong with my traditional DAQ MIO boards, because I put the while (1); command in the beginning of the DAQ callback code and the timer callback is just trying to randomly display some data in the graph and numeric boxes, the data can not be displayed or plotted at all except one numeric box. As you suggested, ProcessSystemEvents () should not be appear in my main thread since it will lower my DAQ rate. I really don't know how to make it work correctly. Can I talk to you directly over phone? Thanks.

 

 

  

0 Kudos
Message 11 of 26
(2,319 Views)
I see the problem now.  The while loop in the main thread is blocking the processing of CVI events so the screen does not get redrawn correctly.  You need to move the DAQ acquire from the main thread into the async timer loop.  You want the entire get data, display data operation to run in its own thread.  To turn the DAQ acquire process on/off you would simply suspend the timer.  Does that make sense?
 
CVI works best as an event driven model.  You want your software to respond to events, not to sit in a loop waiting for something to happen.  In your case, the DAQ acquire/output cycle will be triggered by the timer event.  Your main thread stays free to handle any operator initiated events (from control callbacks)
 
 
0 Kudos
Message 12 of 26
(2,320 Views)
One other issue in your current design.  You get the DAQ data in the main thread, but then access that data from the timer thread to display it.  You have to be real carefull when accessing data from another thread that could be changing, or when setting and clearing flags between threads to indicate that data is ready or has been used.  I dont know what kind of data buffer you are using (again you can post the code here to get more input) but it is generally best to move data between threads using a thread safe queue.
0 Kudos
Message 13 of 26
(2,317 Views)

I cannot move my DAQ into the timer callback, because my DAQ rate is over 100 Hz.

Why in my sample code, the graph can be updated correctly even though the "while" loop kept "blocking" events?

 

0 Kudos
Message 14 of 26
(2,317 Views)

What kind of DAQ card are you using?  Having the DAQ card read at a rate greater than 100Hz, moving the data to a buffer in the PC, then moving the data from that buffer for display are all different things.  The DAQ card reads data based on it's own internal hardware timing (which you setup with the acquisition rate), the data is moved to the PC, usually through a DMA channel by NIDAQ, and you then read the data from a buffer queue maintained by NIDAQ.  You can read this queue and display the data at a much slower rate than the clock frequency of the incoming data, you are just reading bigger chunks of data at one time.  In other words, read it in segments instead of one point at a time.  The speed you can acquire is dictated by the size of the onboard buffer of the DAQ card and how fast the data can be offloaded to the PC (fortunately something that is handled for you by NIDAQ), and the size of the NIDAQ buffer. 

All you have to do is move the total block of data read from the NIDAQ buffer at a rate that is faster than the data is comming in from the card.  The execution of the read data buffer command and display of the data must simply take less time than your timer event callback.  This should be pretty easy even if you drop your timer rate to 50mS. 

Take a look at the CVI example for a continuous analog acquire:
Under Help -> Find Examples
Look for Hardware Input and Output/DAQmx/Analog Measurements/Voltage for ContAcq-Intclk-Anlgstart.prj
There is a lot of additional information in these forums on how to do continuous data acquisitions that are almost seamless where you ping-pong between a pair of buffers so that the read process is essentially continuous.

Message Edited by mvr on 09-26-2006 12:52 PM

0 Kudos
Message 15 of 26
(2,307 Views)

Here is another piece of the puzzle, you want to use your daq card in continous, non-blocking mode.  See this link.
http://forums.ni.com/ni/board/message?board.id=180&message.id=20013&requireLogin=False

What I called the ping-pong buffer arrangement was called double buffered in conventional NIDAQ, but handled for you by NIDAQmx.  You should be able to set up for continuous acquisition without any problem.

0 Kudos
Message 16 of 26
(2,298 Views)

Based on your thoughts, let me make a tentative conclusion on our discussion:

1. while (1) loop in my main thread blocks CVI from updating user interface so that both graph and numeric can not be displayed or updated correctly.

2. ProcessSystemEvents() in my DAQ routine lowered the DAQ rate due to the responses to other events such as update user interface.

3. Instead of putting DAQ routine in main thread, I should put DAQ routine in the second thread (async timer callback) to let main thread freely responded other events simultaneously.

4. DAQ rate can be set as high as possible, while retriving DAQ data segments from buffer can be as slow as we want.

 

I have following questions about the async timer callback:

1. When will the timer callback return? It denpends on time interval or code completion?

2. What if I need to process DAQ data one by one in real-time? I am using PCI-MIO-16XE-10 DAQ board.

 

Thanks again for your help.

 

0 Kudos
Message 17 of 26
(2,306 Views)
Hello howdy,

I might be able to shed some light in the differences that you have seen between updating the numeric control and the graph. I didn't run this example code, but I would expect the same results that mvr found: if the thread that loads/creates the panel is blocked, the numeric controls would update and the plots themselves would update, but the legend and the graph axes would not update.

The reason for this difference lies in the two different drawing modes used in the CVI UI: there is immediate drawing and there is invalidation drawing. Roughly speaking, immediate drawing is used for value changes, and invalidation drawing is used for attribute changes (colors, formats, control sizes, etc). As far as the graph control is concerned, plots are considered values, but everything else is considered attributes.

The reason for this distinction has to do with the trade-offs involved. Immediate drawing is more responsive, but is more "expensive" from a performance standpoint, especially when you need to draw multiple times in succession. Invalidation drawing works by batching up all the drawing requests and then scheduling an event that draws everything once, but only when the UI is allowed to process events. Because attributes are not considered as time-critical as values, they use this drawing mode.

As mvr mentioned in another post, in order to behave correctly, the UI library assumes that you are able to process events regularly, in the thread that owns the UI. In this case, I understand that you don't want to call ProcessSystemEvents very often in order to not interfere with your DAQ timing. One thing you can try is to use the ProcessDrawEvents function, which performs a reduced set of operations relative to ProcessSystemEvents; it should handle invalidation drawing. Other than that, you would have to redesign your app, so that your drawing takes place preferably in your UI thread, without interfering with your DAQ thread. You can use, for example, synchronous DAQ events, or PostDeferredCall, or a thread-safe queue. These are all mechanisms for switching actions from one thread to the other, freeing the first thread to continue doing time-critical non-UI tasks.

Luis
Message 18 of 26
(2,304 Views)

Thanks Luis and mvr. You make me deeply understand the problem. From my research, I realized that probably I should use threadpool instead of async timer to do the DAQ and real-time display simultaneously, since unlike a timer, threadpool does not have a time interval (like interruption) issue related to the DAQ. Hence, I can just simply changed my original DAQ callback into a thread function, while displaying DAQ data from thread-safe queue in the graph. Can I create a new thread in a "callback" function instead of "main"?

Here is the structure I want to change into:

1. StartDAQCallback function:

In this callback fuction, I want to create a new thread for DAQ. Meanwhile, I want to build a safe thread queue for DAQ data. After they are created, I will read the queue every 50 ms and display the data in the graph. ProcessSystemEvents() will also be included in this callback for responding when user clicks "stop" or "pause" button in UI.

 

2. ThreadFunction:

Basically this a real-time control function. It acquires DAQ data from sensor and send commands to actuators in real-time. It will quit the thread either when some DAQ data exceed the range or user click "stop" button in UI. 

 

What do you guys think about the mechanism I proposed above? Will it work well for my application? My objective is fairly straight forward: 1. Displaying DAQ data in the graph timely while not lowering DAQ rate. 2. Respond user input while displaying and acquiring. Thank you for your input.

 

0 Kudos
Message 19 of 26
(2,292 Views)

From your post it sounds like you have a pretty good handle on how to implement your application.  I think what you have described will work for you.

Moving the DAQ acquisition process into a loop in a separate thread pool is fine.  Doing it in the way you feel most comfortable with is generally the best way to go.  The suggestion to use the async timer was made because it is less complicated to implement than setting up a thread pool etc, and it could probably be made to work.  But setting up your own thread process is a better way to go if you want to use a loop based design. 

There are CVI examples that you can probably modify pretty easily to get to where you want go by setting up a thread and executing your data acquisition loop in it.  Once the DAQ process is executing in its own thread and is not blocking or being blocked by the main thread, the majority of your timing issues should be solved.  There is a CVI function PostDeferredCallToThread() that can be used for communication of commands/events/actions between threads.  This can be used by your main thread to suspend/resume and terminate the DAQ data routine in the secondary thread.  More information is available in CVI help under Posting Events and Multithreading and by searching the CVI examples on this web site.  In the CVI examples for running a process in another thread, they cover things like how to signal the thread to shut itself shutdown.

Updating the display with the latest DAQ Data can be handled by setting the displays controls directly in the same method as you were using in the timer thread example, or by passing the data through a thread safe queue and letting another thread (async timer is fine), handle the updates.  I think you have already solved the display problem, the issue has been getting the DAQ acquire routine to run correctly. 

As far as ProcessSystemEvents(), you should not need to call it at all.  The DAQ thread will not need it since it only handles getting data from NIDAQ.  The display panel itself is in the main thread.  As long as all the callbacks in your main thread do not enter long or endless loops, the main user display will redraw  just fine.  As for the process that writes data to the screen, both writing it directly from the DAQ thread, or using a timer or other thread to update the screen, again the screen should update without the need for ProcessSystemEvent() calls.

I see that you want to process the daq data in virtual realtime so that you can control an external process.  You can probably do this from inside your DAQ reader thread.  As long as the process you are trying to control does not require the response time to a data change event to be faster than about 10mS you should be fine.  The 10mS limitation comes from some of the issues with a standard OS like windows, it is not a CVI issue.  As I mentioned before, if better than 10mS response time is required consider using a realtime OS.

I know it can be frustrating trying to resolve these kind of issues in a forum, but I think you are well on the way to getting this working.  Feel free to ask any kind question here.  Good Luck.

Message Edited by mvr on 09-27-2006 11:27 AM

0 Kudos
Message 20 of 26
(2,258 Views)