From 04:00 PM CDT – 08:00 PM CDT (09:00 PM UTC – 01:00 AM UTC) Tuesday, April 16, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

Measurement Studio for .NET Languages

cancel
Showing results for 
Search instead for 
Did you mean: 

vb.net delegates in Measurement Studio?

I have written a few multithreaded applications in VB.NET.
I am new to Measurement Studio.

Are delegates not used to spawn a data acquisition thread?

I have looked at a few of the measurement studio examples that
ship with the product. I have not seen delegates used.

?
Philip Newman
General Dynamics
Electric Boat
0 Kudos
Message 1 of 11
(6,366 Views)

The Measurement Studio hardware class libraries use delegates to notify you when an acquisition is complete. The hardware class libraries (NI4882, VisaNS, and DAQmx) all use the same pattern for asynchronous I/O (i.e. I/O in a thread other than the thread that initiated it). For each Read or Write method that initiates an acquistion or generation, there exists a corresponding pair of BeginRead/EndRead or BeginWrite/EndWrite methods that take a delegate, create a thread to do the acquisition, and call your delegate when the acquisition or generation is complete.

Assuming you are using the DAQmx class library, check out the following example and search for BeginReadMultiSample to see how this is done:

C:\Program Files\National Instruments\MeasurementStudioVS2003\DotNET\Examples\DAQmx\Analog In\Measure Voltage\ContAcqVoltageSamples_IntClk\Vb

This pattern is described in concept topics for the hardware class libraries. The DAQmx concept topic is Asynchronously Reading and Writing with the NI-DAQmx .NET Class Library. There are similar topics for the NI4882 and VisaNS class libraries, if you are using those.

0 Kudos
Message 2 of 11
(6,359 Views)
"...BeginWrite/EndWrite methods that take a delegate, create a thread to do the acquisition, and call your delegate when the acquisition or generation is complete."

i am looking at the ContAcqVoltageSamples_IntClk example.

so the delegates are there...just hidden in the DAQmx?

the supplied examples have the "callback".
seems to resemble a delegate address..

If the "AnalogInCallback" is executing, how is the stop button on the user interface acknowledged?

The callback is a spawned thread?
Philip Newman
General Dynamics
Electric Boat
0 Kudos
Message 3 of 11
(6,363 Views)
Because of the maximum limit of 5000 characters per post, I need to break up my response to your questions. This post is part 1 of 2.


@serpos wrote:
"...BeginWrite/EndWrite methods that take a delegate, create a thread to do the acquisition, and call your delegate when the acquisition or generation is complete."

i am looking at the ContAcqVoltageSamples_IntClk example.

so the delegates are there...just hidden in the DAQmx?

the supplied examples have the "callback".
seems to resemble a delegate address..


I wouldn't describe the delegates as being hidden in the DAQmx driver. To initiate an asynchronous acquisition, you create a delegate of type AsyncCallback that references a method that you want the driver to call when the read is complete. You then pass this delegate to the BeginRead method to start the acquisition. In the  ContAcqVoltageSamples_IntClk example, the code that creates the delegate is the following:

analogCallback =

New AsyncCallback(AddressOf AnalogInCallback)

If you look up AsyncCallback in the NI Measurement Studio Help, you see it is defined as follows:

[Visual Basic]
<Serializable>
Public Delegate Sub AsyncCallback( _
   ByVal ar As IAsyncResult _
)

The AsyncCallback that you create is the delegate.

Message Edited by drohacek on 12-06-2005 10:28 AM

Message 4 of 11
(6,320 Views)

Because of the maximum limit of 5000 characters per post, I need to break up my response to your questions. This post is part 2 of 2.


@serpos wrote:

If the "AnalogInCallback" is executing, how is the stop button on the user interface acknowledged?

The callback is a spawned thread?


When you call a BeginRead method, the DAQmx driver uses a worker thread (i.e. not the thread that calls the BeginRead method) to perform the acquisition. If the thread that calls BeginRead returns to the application main message processing loop, then it is able to acknowledge the stop button on the UI, provided it isn't performing some other operation, while the acquisition is ongoing.

However, things get a little bit more complicated with respect to what happens while AnalogInCallback is executing. By default, the DAQmx driver executes AnalogInCallback in the worker thread. But this is not what is happening in the ContAcqVoltageSamples_IntClk example program. In this example program, the application instructs the DAQmx driver to execute the AnalogInCallback in the user interface thread, with the following line of code:

analogInReader.SynchronizingObject =

Me

Because the driver executes AnalogInCallback in the user interface thread, the user interface thread is not processing messages and so cannot acknowledge the stop button until it finishes executing AnalogInCallback. The reason that we need to execute AnalogInCallback in the user interface thread is that AnalogInCallback updates the user interface and it is a requirement of Windows Forms controls that they be updated only from the thread that creates them. Updating the controls from another thread results in undefined behavior in the .NET Framework 1.1 and in an exception in the .NET Framework 2.0.

If you need to make sure that the stop button remains responsive and you need to update your user inteface with data obtained in the AnalogInCallback, you will have to write some additional code. You have a few options:

  1. Instruct the DAQmx driver to execute AnalogInCallback in the UI thread and call Application.DoEvents to process messages yourself, at appropriate intervals, within your AnalogInCallback.
  2. Instruct the DAQmx driver to execute AnalogInCallback in the UI thread and request more samples at a time from the driver. This results in a greater ratio of time spent doing the acquisition to time spent within the AnalogInCallback.
  3. Instruct the DAQmx driver to execute AnalogInCallback in the worker thread, create a data structure (array or queue) that AnalogInCallback writes data to, and use a timer in the UI thread to periodically pull data from the data structure to update the UI. Make sure you access the data structure in a thread safe manner (obtain a lock to it before you access it from either thread).

Which of these options is best for you depends on the nature of your application. Some of the factors you might need to consider are exactly how responsive the stop button must be, how much you need to do within AnalogInCallback (e.g. does your AnalogInCallback perform expensive analysis operations on the data?), and whether your acquisition is continuous (in this case, you must make sure that you call BeginRead frequently enough to prevent the buffer on the DAQ device from overflowing).

I realize that there is a lot of information here. Please post any followup questions you might have.

Message Edited by drohacek on 12-06-2005 10:27 AM

Message Edited by drohacek on 12-06-2005 10:28 AM

Message 5 of 11
(6,327 Views)
Part 1 of 2
drohacek wrote:

"Instruct the DAQmx driver to execute AnalogInCallback in the worker thread, create a data structure (array or queue) that AnalogInCallback writes data to, and use a timer in the UI thread to periodically pull data from the data structure to update the UI. Make sure you access the data structure in a thread safe manner (obtain a lock to it before you access it from either thread)."
This appears to be the high-performance option. I have searched through the Studio 8 examples and I have not seen this coded.
This is my idea of a simple multithreaded App. (text box on a form displaying a counter with start/stop buttons)
The counter is a separate thread from the UI(form) thread.
I am trying to think of how I would adapt the following threading code to the BeginMultiChannelRead, AsyncCallback, etc...
Imports System.Threading
Public Class Form1
    Inherits System.Windows.Forms.Form
    Dim thrd, thrd2 As Thread
    Delegate Sub ChangeTextControlDelegate(ByVal aTextBox As TextBox, ByVal newText As String)
    Delegate Sub SCdelegate(ByVal iterations As Integer)  

 Private Class CounterArgs
        Public Iterations As Integer
        Public startcounter As SCdelegate
        Public Sub StartCounting()
            startcounter(Iterations) 
        End Sub
 End Class

    Private Sub button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim iterations As Integer = CInt(TextBox2.Text)
        Dim ca As New CounterArgs
        ca.Iterations = iterations
        ca.startcounter = AddressOf Count
        thrd = New Thread(AddressOf ca.StartCounting)
        thrd.IsBackground = True
        thrd.Start 
  End Sub

Philip Newman
General Dynamics
Electric Boat
0 Kudos
Message 6 of 11
(6,264 Views)
Part 2 of 2
    Private Sub Count(ByVal iterations As Integer)
        Dim cnt As Integer
        For cnt = 0 To iterations
            If Me.TextBox1.InvokeRequired Then
                Me.TextBox1.Invoke _
                (New ChangeTextControlDelegate(AddressOf SetDisplayText), New Object() {TextBox1, "1--> " & cnt.ToString})
            Else
                Me.SetDisplayText(TextBox1, "1--> " & cnt.ToString)
            End If
            If Me.TextBox1.InvokeRequired Then
                thrd.Sleep(100)
            End If
        Next
    End Sub

    Public Sub SetDisplayText(ByVal aTextBox As TextBox, ByVal newText As String)
        aTextBox.Text = newText
    End Sub

End Class

Can you post a simple example of your option 3?
It would be great to see how to implement ContAcqVoltageSamples_IntClk using a "worker thread" rather than having the acquisition
crammed into the UI.

Your previous posts were enlightening.
Thanks.
Philip Newman
General Dynamics
Electric Boat
0 Kudos
Message 7 of 11
(6,262 Views)


@serpos wrote:

Can you post a simple example of your option 3?
It would be great to see how to implement ContAcqVoltageSamples_IntClk using a "worker thread" rather than having the acquisition
crammed into the UI.

Your previous posts were enlightening.
Thanks.



The attached .zip file contains a modified version of ContAcqVoltageSamples_IntClk. This modified version uses a .NET Queue object to pass data between the worker thread that the driver creates and the UI thread. I have annotated the changes that I made with comments that begin with drohacek to help you find the changes. In your actual implementation, you will probably want to tune the timer and/or the amount of data you pull out of the Queue each time to acheive the balance you want between time spent in your data acquisition operations and time spent updating the UI.
 
Let me know if you have any questions or if this wasn't what you were looking for.
Message 8 of 11
(6,254 Views)
how can i prioritize the worker thread?

Thanks.
Philip Newman
General Dynamics
Electric Boat
0 Kudos
Message 9 of 11
(6,161 Views)

Hi Philip,

Could you clarify what you mean by prioritizing the worker thread?  It sounds like you are looking for a way to make sure that the DAQmx driver, which creates the read thread, will give the read thread a high priority, thus ensuring quick data delivery.  I believe that the DAQmx driver already does this automatically.  So the only factor affecting performance would be the usage of the CPU by other threads and processes.  You could probably improve the worker thread execution speed by decreasing the CPU usage of the UI thread.  As David said, a simple way to do that would be to increase the timer delay on the example he provided.  Another idea might be to use a faster data display control than a DataGrid, or no UI at all. 

I hope this answers your question.

Message Edited by Hexar on 12-28-2005 04:42 PM

Hexar Anderson
Measurement Studio Staff Software Engineer
National Instruments
0 Kudos
Message 10 of 11
(6,122 Views)