Multifunction DAQ

cancel
Showing results for 
Search instead for 
Did you mean: 

.stop takes a long time using multiple tasks

Hi Henry,

 

It sounds like you want to cancel an asynchronous read before it is complete, but you want it to return any data that it read. As far as I know, the asynchronous read functions in the DAQmx .NET API do not support this. Here are some other options and techniques that may help:

 

You can perfom non-blocking reads. There are two ways to do this:
  1. Use the DaqStream.AvailableSamplesPerChannel property to determine how many samples are in DAQmx's buffer, and read exactly that many.
  2. Read -1 samples. For continuous tasks, this reads all of the samples currently available in the buffer without waiting for it to become full. For finite tasks, this causes DAQmx to wait until the buffer is full, but you can set DaqStream.ReadAllAvailableSamples = true to make reading -1 samples read all samples currently available in the buffer instead.

DAQmx can notify you when a finite task is done. To do this, pass FiniteSamples to ConfigureSampleClock(), and register a delegate for the inputTask.Done event. In the done event callback, perform a synchronous read. In your stop button callback, perform a non-blocking read, and then stop the task. This way, you get all of your data if the task completes, and you get whatever is available if you press the stop button. In C and LabVIEW, you can also get a callback after every N samples has been transferred into the buffer, but I do not think this is supported in .NET.

 

You could queue a user work item or schedule a timer, and have it perform non-blocking reads or short-timeout blocking reads at some time interval that is acceptable for your application.


Brad
---
Brad Keryan
NI R&D
Message 11 of 15
(2,014 Views)
Brad, I am very greatful for the large amount of time you have devoted to this issue.  I have tried all of the suggestions you have forwarded and none of them work:

1.       If an attempt is made to check .Stream.AvailableSamplesPerChannel during an acquire, the function will lock up the host application for a very long time.
2.       If a .Control(TaskAction.Abort) is issued prior to this call, an exception is generated.
3.       If an attempt is made to .Stream.ReadRaw(x) during an acquire, the function will lock up the host application for a very long time.
4.       If a .Control(TaskAction.Abort) is issued , then .Stream.ReadRaw(-1) is issued, the function does not work regardless of the setting of .Stream.ReadAllAvailableSamples = true/false

Perhaps the .NET version does not include full functionality, for instance I cannot discover a .Read function.  Or, perhaps, this very specialized application is just beyond my comprehension.
Time allowing, perhaps you could modify the example I sent you to demonstrate your suggestions as I have had no luck whatsoever in finding a viable solution.

Thanks,
Henry.
0 Kudos
Message 12 of 15
(1,997 Views)
I have a need to revisit this issue.  We are loosing critical data due to the fact that we cannot discover a viable method to stop a multifunction acquisition without loosing data or waiting a very long time (with our host application locking up) for the data to become available to us.  Perhaps we could have an applications engineer verify that .NET does not include functionality which allows us to perform this task?  We are unaware of a Read function for .NET as suggested in previous postings.  I can verify that ReadRaw does not work.  If someone could kindly help us resolve this issue, it would be greatly appreciated.
0 Kudos
Message 13 of 15
(1,969 Views)

Hi Henry,

 

Sorry I didn't reply sooner, I was out of the office last week. Yesterday, I wrote an AI-only proof-of-concept demonstrating how to use multiple non-blocking reads to implement an asynchronous read with cancellation, and I was just about to post it here. Here are the highlights, with the object lifetime management and UI code removed. (My proof of concept updates a progress bar and a status label to show what it's doing.) I'm also attaching the full code and project file.

 

Clicking the Start button configures and starts the task, and queues a user work item to call the asyncRead() method.

 

        private void start_Click(object sender, EventArgs e)
        {
            task.AIChannels.CreateVoltageChannel("Dev1/ai0:2", "", (AITerminalConfiguration)(-1), -5, 5, AIVoltageUnits.Volts);
            task.Timing.ConfigureSampleClock("", 25, SampleClockActiveEdge.Rising, SampleQuantityMode.ContinuousSamples);
            task.Control(TaskAction.Verify);
            sampleCount = 0;
            desiredSampleCount = 1000;
            task.Start();
            ThreadPool.QueueUserWorkItem(asyncRead);
        }

 

Pressing the Stop button sets an event to tell the asyncRead() method to cancel what it's doing, then waits for another event to find out when asyncRead() is done. Because asyncRead() blocks on the cancel event instead of blocking on the DAQmx task, it's always ready to cancel with only a moment's notice.

 

        private void stop_Click(object sender, EventArgs e)
        {
            readCancelEvent.Set();
            readCompleteEvent.WaitOne();
            task.Stop();
            task.Dispose();
            task = null;
        }

 

The asyncRead() method waits for the cancel event to be signaled, with a 50 ms timeout, so it will wake up every 50 ms to read DAQmx data. However, if you signal the cancel event, it should wake up immediately. And since the DAQmx reads it performs are non-blocking, they do not require waiting for samples to be acquired or for triggers to be received. You could probably use a .NET timer callback to do the exact same thing.

 

        private void asyncRead(object state)
        {
            while (!readCancelEvent.WaitOne(50) && sampleCount < desiredSampleCount)
            {
                nonBlockingRead();
            }
            readCompleteEvent.Set();
        }

 

Calling ReadMultiSample(-1) on a continous task performs a non-blocking read. My nonBlockingRead() method then copies the samples into an array, one channel at a time. If you don't need to display the data as it is acquired, you could simplify this by leaving the data in DAQmx's buffer until the very end, and only calling ReadMultiSample() when the stop button is clicked.

 

        private void nonBlockingRead()
        {
            double[,] tempSamples = reader.ReadMultiSample(-1);
            int samplesToCopy = Math.Min(tempSamples.GetLength(1), desiredSampleCount - sampleCount);
            for (int chanNum = 0; chanNum < tempSamples.GetLength(0); ++chanNum)
            {
                Array.Copy(tempSamples, chanNum * tempSamples.GetLength(1), samples, chanNum * samples.GetLength(1) + sampleCount, samplesToCopy);
            }
            sampleCount += samplesToCopy;
        }

 

If you want to be able to cancel your acquisition quickly, it is important to not perform any operations that will block for a long time. As I already pointed out, despite the asynchronous interface of BeginReadMultiSample()/EndReadMultiSample(), they really perform a blocking read (such as ReadMultiSample(1000)) behind the scenes.

 

Brad

 

---
Brad Keryan
NI R&D
0 Kudos
Message 14 of 15
(1,966 Views)

Brad,

thank you once again for your patience, insight and hands-on assistance.  I will try your latest offering.  It certainly looks like a viable solution.

Henry.

0 Kudos
Message 15 of 15
(1,899 Views)