LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Code Review request: data acquisition/control at dual frequency

Solved!
Go to solution

The attached code all works, but I’m hoping some LabVIEW gurus can give me a high-level review of the code structure before I rebuild a similar code.  I work mainly with text programming, so I’m sure there are many LabVIEW best practices or alternative structures that could improve on what I’ve got. 

 

Overview:  The code controls hardware to operate an experimental burner apparatus and record data.  A summary of the function moving from top down in the wiring diagram:

  • The main user inputs are used to calculate operating parameters in the top formula node.
  • Sample code from the hardware manufacturers is adapted for most of the work (e.g. the Alicat sub-VIs).
  • The main loop repeats without delay to monitor and control hardware with highest frequency possible, while the nested conditional runs data reading/writing at a fixed 1 Hz rate.

 

Questions: I would value any feedback on LabVIEW best practices or suggested improvements.  In particular:

  • Is there a better way to accomplish the dual rate operation: with a higher frequency for control functions, and a lower frequency for data read/write?  (As it is, the first loop every second takes ~0.4 sec for the DAQmx read and write operations, and then it loops ~20 times during the remaining 0.6 sec without the conditional block.)  I don't want to separate this into two separate VIs, as I need parameters and data from both loops to be written to a single output file.  
  • The Alicat hardware interface includes initialization, runtime communication, and shutdown blocks, so I don’t see any more efficient way to handle multiple modules.  (My next version will include six identical blocks for six Alicat flow controllers.)
  • I’ve ended up with a lot of very long connection lines, and it seems like writing/reading named variables would be one way of clearing that up.  Do you see any other relevant strategies for reducing the clutter?

 

The attached laser_table_4-4 is an earlier, less cluttered version, and the _5-4 is significantly bloated with additional hardware interfaces.

0 Kudos
Message 1 of 10
(1,166 Views)

Well at first glance I will tell you this One Big Loop (OBL) is never a good programming architecture.

 

Consider a Producer/Consumer architecture. Have the Producer Loop handle your data acquisition and the Consumer loop handle analysis and writing to a file.

 

I might have time to take a closer look later and give more specific advice. 

========================
=== Engineer Ambiguously ===
========================
0 Kudos
Message 2 of 10
(1,151 Views)

Hi d-thomas,

 


@d-thomas wrote:

Questions: I would value any feedback on LabVIEW best practices or suggested improvements.  In particular:

  • Is there a better way to accomplish the dual rate operation: with a higher frequency for control functions, and a lower frequency for data read/write?  (As it is, the first loop every second takes ~0.4 sec for the DAQmx read and write operations, and then it loops ~20 times during the remaining 0.6 sec without the conditional block.)  I don't want to separate this into two separate VIs, as I need parameters and data from both loops to be written to a single output file.  
  • The Alicat hardware interface includes initialization, runtime communication, and shutdown blocks, so I don’t see any more efficient way to handle multiple modules.  (My next version will include six identical blocks for six Alicat flow controllers.)
  • I’ve ended up with a lot of very long connection lines, and it seems like writing/reading named variables would be one way of clearing that up.  Do you see any other relevant strategies for reducing the clutter?

 

The attached laser_table_4-4 is an earlier, less cluttered version, and the _5-4 is significantly bloated with additional hardware interfaces.


I can also recommend to use some better code architecture than "one big loop"…

  • What about separating the IO routines into subVIs to handle one device per subVI (DAQmx, "IColM", Alicat, TCP communication, …)?
  • What about using more/better subVIs? Your "extract mfc data array" handles 3 identical clusters by using duplicated code: why not use a loop instead?
  • What about applying StyleGuide recommendations like "left-to-right wiring" and "code limited to one screen size"? (Hint: using subVIs might help!)
  • Try to stay away from ExpressVIs! There's no need to use ToDDT to create data for a waveform chart!
  • Code simplification, like resizing IndexArray to handle more than one element!
  • The tab control terminal doesn't need to be inside the loop when you don't read it…
  • That large formula node also should be placed into it's subVI to allow viewing all formulas at once and to place some additional code comments…

So much room for improvements!

 

Btw. your "v5.4" VI is an empty file in that ZIP, so we cannot comment on that one.

 

Best regards,
GerdW


using LV2016/2019/2021 on Win10/11+cRIO, TestStand2016/2019
Message 3 of 10
(1,108 Views)

@GerdW and @RTSLVU,

 

Thank you both for the replies and the suggestions!  It will probably take me a while to work in these changes -- including the needed subVIs.  But you've given me many good points to work on.  (Several were things I never considered: I didn't think of "ToDDT" as an Express VI!)

 

The biggest structural change would be splitting this into producer/consumer parallel loops.  In this case, I would have the controller loop at 20 Hz and the DAQmx at 1 Hz.  But I would need to write variables from both loops (at 1 Hz).  In the past I've looked at the queue or channel wire options for this, but didn't find any examples that seemed to fit what I'm trying to do.  What do you see as the best approach?  And are you aware of any examples that involve merging data from two different frequency loops?

 

Oh, and no need to see the 5.4 version -- I fixed a few things, but mostly it just gets bigger and uglier 🙂

 

Thanks,


Daniel

 

0 Kudos
Message 4 of 10
(1,094 Views)
  • I'm glad to see that you are working with a "more modern" version of LabVIEW than LabVIEW 2014. 
  • I went looking for the LabVIEW Project file (possibly called "Laser Table.lvproj"), used to organize your LabVIEW code, including the devices you are using, is "missing".  Did you not create a Project?  [You really should do this].
  • Usually the place one finds the Dynamic Data Wire is as the output of the Dreaded DAQ Assistant, which you are not using (a Kudo to you for using DAQmx directly).
  • LabVIEW (as you may know) is a Data Flow language, which means that two While loops that don't have a "wire" going from the output of one loop to the input of another loop (think about the loops organized from "top to bottom" instead of from "left to right") basically execute in parallel.  This means that you have have two loops, both started at the same time, but one taking data at 1 kHz, while the other takes data at, say, 10 Hz.  Of course, the amount of CPU time to acquire, say, 1000 points at 1 kHz is almost 0 (the hardware does all the work, taking no CPU time until it is time to dump 1000 points into "LabVIEW memory", maybe a few microseconds). 
  • Similarly, following acquiring the data, you need to do something with it -- process it, apply a formula, save to disk, etc., which takes who-knows-how-much time.  A good thing is to "hand the data off" to another loop, also running in parallel, that "wakes up" when it gets your data, processes it, and goes back to sleep until you hand it more data.  This is the Producer/Consumer Design, which has been mentioned.  As long as, on average, the Consumer can "consumer" faster than the Producer "produces" (i.e. it can process 1000 points in 1 second, which doesn't seem hard on modern PCs), you won't have any problems.
  • In LabVIEW 2018, I strongly recommend that you look at Asynchronous Channel Wires (a.k.a. Channel Wires -- for some reason, the "Asynchronous" is silent).  The "Stream" channel wire is tailor-made for Producer/Consumer.  I think I have a demo somewhere on the Forums of this design -- if you can't find it, send me a Private Message.

Bob Schor

 

0 Kudos
Message 5 of 10
(1,080 Views)

Bob_schor -- thank you for the note and suggestions!  I currently don't have this in a Project file, so that is another thing to correct.

 

I did find your demo code here: https://forums.ni.com/t5/LabVIEW-Channel-Wires/Channel-wires-vs-NI-QMH-and-DQMH/m-p/4197406  Unfortunately, this was in LV 2019, and I only have 2018.  Any chance you could send me a screenshot?

 

- Daniel

0 Kudos
Message 6 of 10
(1,042 Views)
Solution
Accepted by topic author d-thomas

Daniel,

     If I did this right, here is the Lenny of Pisa demo in LabVIEW 2018.  Let me know if you it works for you.

 

Bob Schor

Download All
0 Kudos
Message 7 of 10
(1,015 Views)

Hi Bob -- Thanks for sending that!  I'm able to open it in LV2018, but I get an error message:

 

SubVI 'Write.vi': SubVI is missing

 

Is that a standard LabVIEW component that I can replace?

 

Daniel

 

dthomas_0-1673562236233.png

 

0 Kudos
Message 8 of 10
(965 Views)

Ah, yes.  That is the Messenger Writer -- it's "broken" because I "saved for 2018", but don't have 2018 installed, so I couldn't build the Messenger writer.

 

Delete the writer.  Right-click the Cluster (where the Cluster-wire is coming out to connect to the Channel Writer) and choose "Create", which will create another Dropdown, choose "Channel Writer", and then choose "Messenger".  If you do this right, some scripting will run, and the Messenger Writer will appear.  This will ultimately generate (someplace you can't see it easily, but don't worry about that) the "Write.vi" that is missing.  You'll have a similar problem (probably) with the Channel Reader.  There is also a Stream Channel Writer/Reader pair.  This picture shows the Writers (the Messenger Channel is a cluster, so the Pipe is Pink, while the Stream Channel is an I32, so the Pipe is Blue.  The corresponding Readers (which you create sort of the same way, except you need Channel Reader) are the un-arrowed items that resemble the Writers, but with the Data Direction arrow indicating data flowing out from the right edge of the function.

Lenny of Pisa Pix.png

 

Bob Schor

Message 9 of 10
(948 Views)

Thank you all again for the helpful suggestions.  I'm accepting the Channel Reader/Writer details as the 'solution' as that made the biggest difference in improving my code.  For my situation, I ended up with: 

  • Channel > Write  for each producer loop, and Channel > Read multiple for the slower consumer (datafile writing) loop, which averaged each of the read values. 
  • Tag > Write and Tag > Read for passing data between producer loops.
0 Kudos
Message 10 of 10
(690 Views)