Real-Time Measurement and Control

cancel
Showing results for 
Search instead for 
Did you mean: 

Efficiently Passing Data into Timed Loops via Shared Variables with RT FIFO Enabled

Solved!
Go to solution
Highlighted

Hello, this is my first post on the NI forums, so please let me know if I'm doing anything wrong here.

 

I'm working on a motion control application, where I have a Host windows PC communicating with a Target PXI. The PXI controls a servomotor through the Softmotion module. The user of the program sets several parameters (doubles and Booleans) on the Main Host front panel that define a cyclic motion trajectory for the servomotor. All servomotor commands are sent from a Timed Loop on the PXI.

 

Everything is up and running smoothly, but there is one step in the data flow feels terribly hacky and is not easy to change as the program grows. The path for the input parameters from the host PC to the Timed Loop is as follows:

 

  1. Entered on the front panel and read by a "User Input" while loop that polls the controls on every loop iteration.
  2. When the user presses "Send Parameters to Target" button, the parameters (packaged as a cluster) are sent to a "Data Out" while loop via a queue.
  3. The "Data Out" loop sends the parameters cluster to the PXI via Network Stream.
  4. On the PXI, a dedicated "Data In" loop reads the cluster from the Network Stream.
  5. Within the "Data In" Loop, a custom "Packaging" function is called that converts the parameter cluster to an array of doubles so that it can be sent to the Timed Loop via a Shared Variable with RT FIFO enabled. Once the cluster is repackaged as an array, it is send to the Timed Loop as an array via the shared variable.
  6. Within the Timed Loop, the array is read from the shared variable. The array is then repackaged as the original cluster for use within the Timed Loop.
  7. The Timed Loop does all of the safety checking, measuring sensors, interpreting input parameters, sending control commands, etc. The measurements and initial control parameters are repackaged again from clusters to a large array of doubles and are sent out of the Timed Loop via a Shared Variable with RT FIFO enabled.
  8. The array is read within a "Logging and Lossy Data Communication" loop, and is repackaged from an array to a cluster again. The cluster is sent pack to the host for plotting via a Network Published Variable and is occasionally logged on the PXI as a TDMS file.

I have chosen to access all of my control data within the PXI as clusters because I feel that it is inherently safer to refer to a variable as "Control Data.Motor Velocity" rather than accessing "array element 3", for example. Safety is critical here as this is a research system that will be physically interacting with people.

 

What I don't like about this code is the packaging and un-packaging steps. I've included a screenshot of the packaging that happens in Step 6 above. I from this figure I think it is clear that this process doesn't scale well. What if I want to send 25 parameters from the front panel? 50? How do I keep this from becoming a jumped mess? Additionally, adding a single parameter or measurement becomes really tedious, because I need to go back and alter all of these packaging functions. Even though I'm using TypeDefs, I still need to go in and connect the wires to convert the cluster to an array and vice-versa.

 

Is there a well known best practice for this situation? How do I more efficiently get data in and out of my timed loop while retaining the ability to refer to variables by name within the loop? Is there a way to automate the process of conversion from cluster to array and back again? Should I be doing something else entirely? Thanks in advance.

0 Kudos
Message 1 of 3
(293 Views)
Highlighted
Solution
Accepted by topic author ajanders

I'm going to suggest that you never use polling to detect front panel user interactions.  Such interactions should probably be handled using an event structure inside a while loop.  Call this your "UI Loop".  In that case, the event only executes when a front panel control is changed by the user.  Separately, have an "Operation Loop" which handles commands generated by the UI Loop.  Your queue should use a cluster datatype, containing an enum for "Command" (case to execute), and a variant for "Data" (to be parsed with e.g. a Variant to Data function based on the command case).  In the Operation Loop cases is where you would act on the new information, and send new tag data to the target using RT FIFO-enabled shared variables, or open new Network Streams for lossless streaming, etc.  So, you interact with the front panel, the event for that control executes and enqueues a new element including whatever data you wish (cast to variant) and indicating which operational case to execute, and then in the other loop an appropriate case executes which parses that data and does something with it.  If you TypeDef your clusters, you can create a shared variable with DataType "From custom control...", selecting that TypeDef as the DataType, and then you don't need to convert to arrays and back.

NI Alliance Partner
Message 2 of 3
(195 Views)
Highlighted

Hey, thank you so much for your response. I've held off on responding because I wanted to read up a little on your recommendations and try to implement a few sandbox vi's to test things out.

 


@CFER_STS wrote:

I'm going to suggest that you never use polling to detect front panel user interactions.  Such interactions should probably be handled using an event structure inside a while loop.  Call this your "UI Loop".  In that case, the event only executes when a front panel control is changed by the user. 


Done. This is great advice and I'll build all of my UI loops this way from now on.

 


@CFER_STS wrote:

In the Operation Loop cases is where you would act on the new information, and send new tag data to the target using RT FIFO-enabled shared variables, or open new Network Streams for lossless streaming, etc.  So, you interact with the front panel, the event for that control executes and enqueues a new element including whatever data you wish (cast to variant) and indicating which operational case to execute, and then in the other loop an appropriate case executes which parses that data and does something with it.  If you TypeDef your clusters, you can create a shared variable with DataType "From custom control...", selecting that TypeDef as the DataType, and then you don't need to convert to arrays and back.


This is also great advice. I'm now sending clusters containing an enum (type of test to execute) and a variant (cluster of test parameters) to the RT Target. Like you said, the data pathway is from the UI Loop to an Operation Loop through a queue. The Operation Loop sends the cluster to the RT Target via a network stream. On the target side, a dedicated loop reads from the network stream. The loop then casts the variant to a cluster of numbers (based on the case encoded in the enum) and sends its to the Timed Loop via a shared variable with RT FIFO enabled. Each type of test has its own shared variable with RT FIFO that is based a TypeDef of the cluster in question. The enum is also sent to the Timed Loop via a shared variable, and the Timed Loop only reads from the RT FIFO for the specified test.

 

Obviously you've provided a lot of help, and I think the single biggest issue that complicated my old format was that I was trying to use a single shared variable (an array) to pass multiple different types of tests into the Timed Loop. Thanks again!

0 Kudos
Message 3 of 3
(160 Views)