12-04-2009 03:35 PM
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:
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.
12-07-2009 03:03 PM
12-16-2009 11:48 AM
12-16-2009 12:54 PM
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
12-29-2009 06:19 PM
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.