I’ve been adapting our team code to be more like the code in the LV Mastery videos. I’ve watched many of them, many times, and am trying to follow the advice of not actually “doing” anything within the Teleop.VI
I’ve added a shift register to the primary While-Loop, calling it Main Shifter.
I’m toying with different architecture decisions now, but the TipJar videos don’t necessarily address my questions.
Primarily, I’m torn between a couple choices that are interrelated … namely the Main Shifter, and the Robot Command Global.
If I make the Main Shifter a Global, and write to it just before the main loop cycles, then I don’t necessarily need a Robot Command, as long as those commands are in the Main Shifter. Right?
I know that I can get it to work either way, but I’m just wondering which one might be more efficient and/or more wasteful.
Storing data in shift registers is always more preferable than a global variable. Whenever you call a global you are making a copy of your data… Also with global variables because you have copies you can get into race conditions. Here is an article on NI’s website regarding global variables.
After working on the structure a little more, I think that I answered my own question …
(Yes, Tanner, that’s exactly what I’m talking about)
I think I’ve come up with a few rules of thumb to follow:
ShiftReg’s
for Right-to-Left data flow (memory between loops)
used to control the logic and flow of the state machine (case structures)
A variable that isn’t READ from the ShiftReg, to control the logic, probably doesn’t belong in the ShiftReg
Globals are more Up/Down based, to immediately get data into other VI’s
Write to the Global within the cases of the state-machine
I’m probably NOT going to put my Drive Command Global into the Main ShiftReg and I’m probably going to remove the Main Shifter from my list of Globals.
In actuality, I’m sure that either method would work, I’m just trying to write efficient code that doesn’t waste resources and/or clock cycles.
I’m so glad you’re enjoying our videos… and asking (then self-answering) very astute questions.
You could put the main shifter into a global. The reason I didn’t was because it was simpler to only have what you needed, and better to reduce the quantity of data. Also, I wanted to be able to use it in an autonomous independent.
Some general comments:
What we have here is the biggest problem when working in LabVIEW - breaking out of the dataflow paradigm - efficiently.
In a nutshell, I use this rule of thumb:
Shift register only when you have a single loop: no locals or globals
When you have parallel loops, you can pass small amounts of data around using globals. Only write in ONE place. Read in many. That can be tricky, and is what motivated last year’s approach of the command signal to go from teleop (or autonomous) --> other loops. Remember that when the other loops needed to send info back, they used their own global, so that it was only written in one place.
When you have large amounts of data to pass around, use a functional global variable. That’s a tricky but very useful beast. I don’t think I’ve brought it up in any videos, nor do I think you need it for FRC, but it is interesting. In 10 words or less: an uninitialized shift register in a while loop which only runs once in a subVI will remember stuff. (OK that was more than 10).
Thanks very much for the outstanding videos; they have been a huge help. I’m using the shifter example for our kicker and it really simplifies things.
I don’t quite understand how we access the main shifter in autonomous independent though. I have set up a shifter like in your video, and have it implemented in teleop.vi. But Robot Main calls Autonomous Iterative during the Autonomous Enable case, and from what I understand Autonomous Independent runs in parallel if it is selected in Begin (although I am really fuzzy on this one). Do you know how I can use the shifter in Autonomous Independent and then retain that state to be used during teleop.vi (so I don’t have to reset the kicker at the start of teleop)? Thanks