LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Handle sequence of actions with callbacks

I am programming a measurement sequence involving readings from a DVM (Keithley 2002) and activating switches on the 2002 and on another instrument.

 

The readings are for temperature (potentiometric across a reference and a probe, so it involves several voltage readings and switch activations), as well as voltage across several channels. They use the same instrument in the same mode (DCV).

 

There is a specific measurment sequence I need to keep (voltage1, voltage 2, temperature, voltage 3, ...).

 

The DVM is set up with an integration time of about 1 s, so there is some idle time from triggering to reading ready, where processing or user events could be handled, but I need to be able to return to the point in the main sequence (or the temperature measurement sequence) from where I came.

 

So presently I have the reading routine just call the (hierachy) of subroutines for temperature and voltage, and when reading voltage, circle in a loop checking the STB, because I do not really know how to handle it by using a CallBack (how to "get back" to the correct place in the sequence).

 

In other situations I have used a global status variable and a switch statement in the main routine, and have the function getting the reading, change the status variable and then post a deferred call to the main sequence routine before it returns, but here it would require several levels and a LOT of states.

 

I have looked for a "standard" LabWindows way of doing this, but not really found anything.

 

Any help is appreciated, e.g. pointers to examples

 

Thanks

 

0 Kudos
Message 1 of 7
(4,727 Views)

Hi HDJ

 

 

I would say the normal and my recommended way to set up a communication between a switch and a DMM is via handshaking over the trigger lines. So when the measurement device is finished it will send a "measurement done" trigger event and that trigger event is then triggering the switching on the switch. You can in this case setup a switch delay which will help you with the integration time as well.

 

Most NI Instrument support this way of triggering, but I'm not sure if you can configure digital triggers in this way for you Keithley instrument. If not we will need to do the sequencing in software.

 

 

If you want to do the sequencing in software my best recommendations will be to look into TestStand, TestStand will have a lot of functionality to help you keep track of states, launch new parallel processes, wait for parallel processes to finish and so on. 

 

 

Offcourse you can do this manually in CVI, but it will be more work, and we don't have examples programs in the direction you want to go.

 

 

But let me know if you have some specific questions on how you would setup a function call in CVI, if you have questions on how to do events/callback and so on.

 

 

Best Regards

Anders Rohde

0 Kudos
Message 2 of 7
(4,675 Views)

Hi Anders

 

Thanks for the reply.

 

I'll have a look at Teststand and see if/how that can help me.

 

Setting up the switch to trigger the DVM is certainly possible, however I will still need to return to my measurement sequence when the DVM value has been delivered.

 

To get more concrete, presently my hierachy of routines looks something like this (indent used to indicate hierachy of function calls 

 

getdatapoint (called by a timer at regular intervals, e.g. 60 s)

    gettemperature

        switch to channel B (internal DVM switch)

        getvoltage

            trigger DVM

            'hand around' (looping Delay(.2); GetUserEvent() (filtered; to handle the most immediate events) for a total of about 0.8 s, until DVM measurement value is available

            return voltage

        switch to channel C (internal DVM switch)

        getvoltage (as above)

        calculate ratio, etc. and return temperature

    getvoltagerange D-F

        switch to channel A (internal DVM switch)

        switch to channel D (on external switch)

        getvoltage (as above)

        switch to channel E (on external switch)

        ....

    gettemperature (as above)

    getvoltagerande G-K (as above)

    gettemperature (as above)

     ...

return

 

etc. So the baseic problem is to "be idle" after the trigger in 'getvoltage' until a result is available (callback on some GPIB event), but then to get back into the sequence or at least back to the just after the trigger in getvoltage. In other situations I have used PostDeferredCall, status variables and a switch statement in the main call to "pick up the thread" - that was just impractical here.

 

I was thinking, if the position 'hang around' could be the return point for the 'handle-DVM-ready' callback, it would work out.

 

Anyway, any ideas are appreciated.

 

 

0 Kudos
Message 3 of 7
(4,664 Views)

Maybe you could use the

 

CmtScheduleThreadPoolFunction

CmtWaitForThreadPoolFunctionCompletion 

 

As mentioned in this article (Under using a Thread Pool)

 

Multithreading in LabWindows™/CVI

http://www.ni.com/white-paper/3663/en/

0 Kudos
Message 4 of 7
(4,657 Views)

Hello HDJ,

 

I don't know how your code (especially your main function) currently looks like, but there are a few useful things you do/use.

Using a state machine like approach would indeed be a good start, but of course you would like to maintain a responsive User Interface (eg. stop/abort buttons).

What you could do is the following:

- Implement your state machine in a separate thread (for example by using the functions Anders has shown).

- Make your "state machine thread/loop” driven by thread safe queues (http://zone.ni.com/reference/en-XX/help/370051Y-01/cvi/programmerref/threadsafequeue/)

  This allows you to:
  1) receive queue elements from the UI (callbacks)
  2) receive queue elements from the state machine itself
  3) receive queue elements from any other parallel thread (including other acquisition related callbacks).

- Provide the necessary states in this state machine to handle both User Interaction (eg. stop/abort ) as well as the continuation from step 1 to step 2 and so on.
This includes but is not limited to an abort state, init state, DoStepX state, Waiting for response state(s)…

To be able to give you some more precise information I would like to ask you if you can give more information concerning the following things?
- Are you currently working on a fixed order and functionality sequence (always step A first, then step B , then step C, etc..) ?
Or is the order of the different steps (and if they should be executed or not) in your sequence dependent  on the result of each (or multiple) step(s)?
- Do you have multiple versions of the sequence that use the same steps (in a different order) or do you only have 1 possible sequence?
- How does a current (working) implementation of your sequencer look like?
  (this can be shown in pseudo code) This can give a better idea of what could be improved and which limitations are imposed upon it.

Kind Regards,
Thierry C - CLA, CTA - Senior R&D Engineer (Former Support Engineer) - National Instruments
If someone helped you, let them know. Mark as solved and/or give a kudo. 😉
0 Kudos
Message 5 of 7
(4,650 Views)

Thanks Thierry and Anders for your feedback.

 

I'll have a look at the multithreading approach, although I didn't view my measurement setup to be _that_ complex to need it. But the state machine model probably does benefit .... or maybe LabView is better for this type of task(?)

or are there examples of a state machine/sequencer implemented with multithreading?

 

 

Thierry, the present task is relatively fixed in sequence and steps and it is necessary to have the events occur in the right order, so straightforward sequenctial programming - but again, with a user interface you would like to be responsive although the choices when running are limited.

 

In other tasks where I used the state machine approach, it usually goes like this:

 

/* global */ int stage = 0;

 

timer/or similar_callback e.g. called to process a DVM reading

In more convoluted situation, a PostDeferred is used to queue up steps. 

 

switch (stage)

{

case 0:

   some initialisation, could be set "switches for next stage"

   stage = 1;

   break;

case 1:

    do the first step

    stage = 2;

    break;

case 2:

   do part of a second step

   which would continue into the next stage

   unless ...

   if (whatever) stage = 4;

   else break; /* continue into stage 3 */

case 3:

    stage = 3;

    more stuff

    if (something went wrong) stage = 2;

    if (things are good) stage = 4;

    break; /* otherwise repeat stage 3 */

case 4:

   ...

}

 

Could I have my original sequence (the previous post) "hang" by using thread-locking? And have the DVM reading callback release the lock when it's done. (or that wouldn't help with UI responsiveness and UI callbacks?

 

)

0 Kudos
Message 6 of 7
(4,642 Views)

Hello HDJ,

 

Sorry for my late reply.

I have been Out of Office for quite some time the last weeks (due to teaching courses & being sick).

 

Deciding which one (LabVIEW or CVI) is better is usually based on the personal preferences and experiences of the programmer.

I didn't directly find a CVI example in the Example Finder that shows this Producer/Consumer approach, but I could make a small one for you if you would like me to do that.

 

How much experience do you have (concerning the implementation of parallel tasks/loops) in CVI (or LabVIEW)?

In which environment are you planning to do further development? (I am guessing CVI)

 

You can definitely use threadlocking from inside CVI (see example ThreadLockTimeout in the NI Example Finder).
Two things I have to warn you about in advance:

- Make sure to provide an acceptable timeout value, so that your code/state machine doesn't just hang if something goes wrong (programmation error or perhaps a communication error with your measurement devices)

- You will also have to make your Lock handle globally available otherwise you will not be able to release it from inside the callback function.

 

Is there a specific reason why you prefer to use thread locking instead of other mechanisms?

In this case it seems like you are mimicking a queue/notifier based approach by using locks.

Please note that using locks in this case might inhibit your "freedom of implementation".

Kind Regards,
Thierry C - CLA, CTA - Senior R&D Engineer (Former Support Engineer) - National Instruments
If someone helped you, let them know. Mark as solved and/or give a kudo. 😉
0 Kudos
Message 7 of 7
(4,532 Views)