LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

How do you interleave commands with timed reads?

I'm writing a program that interfaces to a few VISA instruments. It needs to periodically read data, but also send commands on-demand. My applications are mostly Actor Framework, so I'd send a Time Delayed message, but I'm curious how others approach the problem, especially outside the Actor Framework world.

 

Here are a couple ideas - what else is out there? What do you like using?

 

  1. Split the VISA ref to 2 loops (read & command); use a non-reentrant VI or semaphore for VISA access
  2. Actor Framework; commands are messages, read data via a Time Delayed message
  3. Dequeue or event loop; commands trigger an iteration, otherwise read data on the timeout
0 Kudos
Message 1 of 8
(665 Views)

@OneOfTheDans wrote:

I'm writing a program that interfaces to a few VISA instruments. It needs to periodically read data, but also send commands on-demand. My applications are mostly Actor Framework, so I'd send a Time Delayed message, but I'm curious how others approach the problem, especially outside the Actor Framework world.

 

Here are a couple ideas - what else is out there? What do you like using?

 

  1. Split the VISA ref to 2 loops (read & command); use a non-reentrant VI or semaphore for VISA access
  2. Actor Framework; commands are messages, read data via a Time Delayed message
  3. Dequeue or event loop; commands trigger an iteration, otherwise read data on the timeout

Oops, I didn't read the post correctly.

Bill
CLD
(Mid-Level minion.)
My support system ensures that I don't look totally incompetent.
Proud to say that I've progressed beyond knowing just enough to be dangerous. I now know enough to know that I have no clue about anything at all.
Humble author of the CLAD Nugget.
0 Kudos
Message 2 of 8
(624 Views)

Done that sort of thing before... The approach I took was to put the whole set of VISA funcitons in an "upper driver" (to build the strings I wanted) and then a lower driver for the R/W / R only  or W only functions.

Left the driver sitting in a consumer loop and had an enqueuer function to put a Read into the driver on a timeout, and poked the writes in front (and flushed the queue of reads when I wrote if necessary).
Processed the read data stream in a consumer of the consumer.

(Single VISA session as you can only perform a single coms action down the physical line at one time, there's no point in confusing yourself during debug by creating a potential race condition by splitting the VISA wire.)

James

CLD; LabVIEW since 8.0, Currently have LabVIEW 2015 SP1, 2018SP1 & 2020 installed
Message 3 of 8
(615 Views)

@James_W wrote:

Done that sort of thing before... The approach I took was to put the whole set of VISA funcitons in an "upper driver" (to build the strings I wanted) and then a lower driver for the R/W / R only  or W only functions.


Yup that's the way to go.  My only comment would be to be aware of how quickly you can perform write requests.  Because if you are reading in the timeout cases, but a timeout never happens because write requests happen so quickly, then you'll never read.  In that case I'd keep track of when the last read took place, and if it was too long ago, perform a read, even if there is a write waiting in the queue.  I was reading a power supply in the 500ms timeout case.  But at some point I needed to simulate a constant power on the power supply.  This means constantly adjusting the current setpoint in a feedback loop.  I was updating the current as fast as possible, so the 500ms timeout never happened.  Avoid race conditions, and deadlocks.

0 Kudos
Message 4 of 8
(594 Views)

Thanks James, that sounds fundamentally like #3 (dequeue commands, timeout for read), but I like that your Read is timed outside the consumer loop... otherwise the read timer gets reset anytime a command comes in. Your design is very similar to the Actor Framework with Time Delayed messages, but you have more control over the queue.

 

One thing I don't like about using timeouts (in Actor Framework, your design, and my #3) is that the Read timing will always drift. The loop time is always "timeout + execution" vs. using Wait or Wait until next multiple which can give more consistent timing.

 

How did your consumer's consumer know how to interpret the received data? For example if a power supply sent a number, how would it know if that's a voltage, a current, or a firmware rev?

0 Kudos
Message 5 of 8
(592 Views)

@OneOfTheDans wrote:

 

One thing I don't like about using timeouts (in Actor Framework, your design, and my #3) is that the Read timing will always drift. The loop time is always "timeout + execution" vs. using Wait or Wait until next multiple which can give more consistent timing.

 

How did your consumer's consumer know how to interpret the received data? For example if a power supply sent a number, how would it know if that's a voltage, a current, or a firmware rev?


(not James) but for me the request comes with a Enum that is the method of the thing needing to take place.  So the enum is "Read Firmware" and returned will be data that I know needs to be interpreted as a firmware, or "Read Current" will return current.  This can be wrapped in subVIs too, so a single VI can perform the action, then return a typed cluster of the data you expect.

 

As for the drift comment you are right.  But the system won't be deterministic anyway since it relies on this external device to perform an action which will take an unknown amount of time.  For this reason the response can come with a timestamp of when the response came.  That way if you are logging, or putting it on a graph it isn't just "Sample 1", and "Sample 2" but instead an XY with the timestamp on the X axis.  And when I log I log "Time" along with the sample of data.

 

If you do want to get closer to having a "Perform Action every 200ms" you can look at when the action was last performed, then have your timeout be the amount of time it will need to wait to get up to 200ms.  Still it won't be deterministic for other reasons but it can be better.  This starts to have functionality close to a timed while loop, but I try to only use those on RT and FPGA.

Message 6 of 8
(586 Views)

@OneOfTheDans wrote:

Thanks James, that sounds fundamentally like #3 (dequeue commands, timeout for read), but I like that your Read is timed outside the consumer loop... otherwise the read timer gets reset anytime a command comes in. Your design is very similar to the Actor Framework with Time Delayed messages, but you have more control over the queue.

 

One thing I don't like about using timeouts (in Actor Framework, your design, and my #3) is that the Read timing will always drift. The loop time is always "timeout + execution" vs. using Wait or Wait until next multiple which can give more consistent timing.

 

How did your consumer's consumer know how to interpret the received data? For example if a power supply sent a number, how would it know if that's a voltage, a current, or a firmware rev?


For me thats the difference between a Read and a W/R command.
Read is polling data and I know what to expect as the data is predefined by the last command (stored by an enum or variable that is in the cos
W/R I write then wait maybe 50ms for the write to be processed and return the Read command. then EnQ the knowns return string to expect and process

Basically, if you know what the last write was, you know what the expected read to process is (so you pass the command type down).

CLD; LabVIEW since 8.0, Currently have LabVIEW 2015 SP1, 2018SP1 & 2020 installed
0 Kudos
Message 7 of 8
(565 Views)

Thanks guys! I ended up sticking with AF for the module I was writing since it's fundamentally similar to this and I already have an AF environment. I'll definitely keep this approach in mind for future modules though... it'd be nice to cut down on all the boilerplate classes/messages required for the AF.

0 Kudos
Message 8 of 8
(530 Views)