Measurement Studio for VC++

cancel
Showing results for 
Search instead for 
Did you mean: 

UpdateData() function causing crash .. need an alternative

When I call UpdateData() function inside an event (buffer read) my application crashes. I am using VC++ .NET with Measurement Studio 8.0 (Daq-mx)

I read in the NI forums that event callsbacks are not likely to occure on user interface thread which is what is cauing the crash.

But I need to display the data that I process in this event on user interface. How do I do this?
0 Kudos
Message 1 of 8
(7,822 Views)

Hi,

If you want to view the data that you acquired, you can put that data into a DataTable or onto a Graph.  To put your data onto a graph control, there is a PlotY function that takes an array of doubles as a parameter.  Take a look at a few of the example codes that installed with Measurement Studio.

Data Acquisition Example:

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

User Interface Examples:

C:\Program Files\National Instruments\MeasurementStudioVS2003\DotNET\Examples\UI\Graph

0 Kudos
Message 2 of 8
(7,807 Views)
Terry,

My question is different. I have already coded data reading & ploting in event  handle function OnBufferDoneRead().

In this function OnBufferDoneRead(), I display the graph and also do some math on the data. Now I need to display the results on edit controls placed on dialog box of my main app.

But when I call UpdateData() event from OnBufferDoneRead(), my application crashes. This I guess is because the OnBufferDoneRead() function is running on different thread. Am I correct?

If so is there any work around so I can update variables on main thread of my app.

Thanks.

0 Kudos
Message 3 of 8
(7,805 Views)
Hi,

Can you post a simple example code of what you are trying to do, so I can test this out?


0 Kudos
Message 4 of 8
(7,799 Views)
Hi,

My program has grown a lot so I am sending a snippet with comments on where I have that code is added


// definations: in header file
-------------------------------------
{
    // Create Analog tasks
    std::auto_ptr<CNiDAQmxTask> m_AnalogTask1;
    // create reader
    std::auto_ptr<CNiDAQmxAnalogUnscaledReader> m_AnalogReader1;

    CNiUInt32Matrix     m_matAnalogData1;

    void OnBufferReadDev1(CNiDAQmxAsyncOperation asyncHandle, void* userData);
}

// code in .cpp file
// Setup data acquisition at start of program
----------------------------------------------
OnInitialUpdate() {

    m_AnalogTask1 = std::auto_ptr<CNiDAQmxTask>(new CNiDAQmxTask());

    // Create voltage input channels
    m_AnalogTask1->AIChannels.CreateVoltageChannel("Dev1/ai0:7","",DAQmxAITerminalConfigurationRse,-10, 10, DAQmxAIVoltageUnitsVolts);

    // Setup sampling clock
    m_AnalogTask1->Timing.ConfigureSampleClock("", 250, DAQmxSampleClockActiveEdgeRising, DAQmxSampleQuantityModeContinuousSamples, 250);

    // verify the task is correct
    m_AnalogTask1->Control(DAQmxTaskVerify);

    // setup unscaled channel reader
    m_AnalogReader1 = std::auto_ptr<CNiDAQmxAnalogUnscaledReader> (new CNiDAQmxAnalogUnscaledReader(m_AnalogTask1->Stream));

    // Set up event handler for Buffer Read event
    m_AnalogReader1->InstallEventHandler(*this, OnBufferReadDev1);

    // Start the acquisition
    m_AnalogReader1->ReadUInt32Async(100, m_matAnalogData1, NULL);
}

//function OnBufferReadDev1()
-------------------------
OnBufferReadDev1() {

    unsigned int l_Volt = 0;
    unsigned int l_Curr = 0;
    unsigned int l_Flow = 0;
    unsigned int l_Wire = 0;

    if (... something ... ) {
        for (int ctr = 0 ; ctr < 100; ctr = ctr + 5 ) {
            l_Volt += m_matAnalogData1(0,ctr);
            l_Curr += m_matAnalogData1(1,ctr);
            l_Flow += m_matAnalogData1(2,ctr);
            l_Wire += m_matAnalogData1(3,ctr);
        }
    }
    else {
        for (int ctr = 0 ; ctr < 100; ctr = ctr + 5 ) {
            l_Volt += m_matAnalogData1(4,ctr);
            l_Curr += m_matAnalogData1(5,ctr);
            l_Flow += m_matAnalogData1(6,ctr);
            l_Wire += m_matAnalogData1(7,ctr);
        }
    }

    // Avg the data values & then  display them in txt fields
    // all the c_e variables are associated with Edit fields on Dialog box
    c_eCurrent =  float(l_Volt /100);
    c_eVoltage =  float(l_Curr /100);
    c_eFlowrate = float(l_Flow /100);
    c_eWirefeed = float(l_Wire /100);

    // THIS IS WHERE THE CODE FAILS
    UpdateData(FALSE);

    // re read the data
    m_AnalogReader1->ReadUInt32Async(100, m_matAnalogData1, NULL);
}
0 Kudos
Message 5 of 8
(7,797 Views)
HI

You probably already know this, but one of the golden rules with Win32 programming is the following

"Thou shall not access UI controls from a thread other than the one that created the UI control in the first place"

The UpdataData method is not threadsafe and hence throws assertions in debug builds when called in the DAQmx driver worker thread. What you need to do is to have the main thread call this method and not call it in the worker thread.

You can do this by posting a a custom message to the main dialog window and then calling all your UI centric code from the callback that handles your message. There is nothing Measurement Studio specific about this, this behavior occurs because of the nature of MFC.

See this doc for how to set this up.

BTW, if you were to call methods on the Measurement Studio ActiveX control from non UI threads, it would work because we took some extra pains to make them easier to use in these cases. The only additional thing you would need for those is to initialize COM in the worker thread by either calling CoInitialize() or

CNiComInitialize com(CNiComInitialize::Apartment)

You can see that in the MStudio DAQ shipping examples. This is required because the Measurement Studio controls are COM controls and COM needs to be initialized manually in non UI threads.

Hope this helps.



Bilal Durrani
NI
0 Kudos
Message 6 of 8
(7,787 Views)
Bilal,

Thanks for the reply. I was aware of that fact but I thought  the  callback event was posted on main thread. Having a callback event posted on main threads que makes sense because then the other thread thats doing the data acquisition is free.

With the way it is currently setup it looks like there are atleast three threads running, 1st the primary application thread, 2nd the thread on which callback event is posted & 3rd the thread thats doing data acuisition. Am I correct?
0 Kudos
Message 7 of 8
(7,787 Views)
Nope, the callback message will be posted on the UI thread (the thread which owns the hWnd you passed to SendMessage), it won't spawn another thread.

You can see which thread the callback is firing in if you set a breakpoint in that callback and open up the threads window (Ctrl + Alt +H). You will see alot of other threads as well (spawned by MFC and DAQ internal threads)

So there will be atleast 2 threads. One would be the main UI one, and the other would be the DAQ driver thread.
Bilal Durrani
NI
0 Kudos
Message 8 of 8
(7,782 Views)