I am new to the use of action engines in my code and am hoping to learn and improve my use of action engines. The first one I have created is to control a set of relays which will open and close air-operated valves using the NI 9474. I was hoping that I could get some general feedback on DOs and DONTs with this action engine and that it will help me with future creation of AEs for my code. What should I be doind differently from an efficiency standpoint, etc?
I have attached the VI.
Solved! Go to Solution.
Just from the picture, I would say your biggest issue is that you should have the Init case create the DAQmx Task, initialize the digital outputs, and then store the task reference in a shift register. Then you only need the list of channels at the initialization and you are not constantly creating and destroying a reference to the same IO.
And you should also have all of your outputs on the outside of the case structure. There is a thread floating around called "Clear As Mud" that goes throught specifics of why.
Yes, If you are going to have an AE handle this functionality then you should have it handle the functionality for all possible use cases. Especially as it seems from your picture that you at no stage pass a reference to any of this out of the AE.
This might be a tad off topic...
Considering the size of this AE would you consider moving over to a class? You'd be able to keep this functionality and really reduce your visual complexity. Not an option for everyone, but might be something worth looking into.
Would you then create a case to destroy the reference that you call upon ending the execution of your main vi?
Yes, but also consider making two Action Engines, one to handle the Hardware (Valves), with three Actions: Init (which takes Channels In, configures the Channels, and starts DAQmx), Set Valves (which takes an Array of Booleans and does the DAQmx Write), and Close (which stops and clears the Task). Note that the Task constant created by Init should be carried on a Shift Register in this Action Engine for use in Set Valves and Close, and should be set to a Null Task when Close exits. This will "error out" if you try to Set Valve before Init, or after Close (which is, I presume, "desired behavior").
The other Action Engine only manipulates the Valves. I'm a little confused why you have both an Enum (to control a single valve at a time) and an Array of (Valve) Booleans -- I'd think about which (single) method you want to use and stick with it. Let's consider an Action Engine that handles the "Valve as Enum", i.e. you specify a single Valve as your input. Here is a possible Action Engine for you (all having Valve # (your Enum) as the Input, and the Array of Valves as the Output):
You might notice that Init is the same as All Off -- the reason to put it there is to remind you when you Init the Hardware, you should follow up by Init-ing the Valves.
Now put these two together in some top-level code. A typical sequence might be Init (H), Init (V), Set Valves. What do you do if you want, say, to turn both Valves 3 and 5 on at the same time? You call Valve On (3), Valve On (5), Set Valves. Note that you are separating the enumeration of which valve(s) to turn on or off from the actual operation of the DAQ Hardware.
Another thing to note is that the Hardware Action Engine returns Valves Out, the actual State of the Valves. if you wire this in your Top-Level program to an 8-element Array of Boolean Indicators (which often look like Green LEDs), you will have a visual indicator of which Valves are On (and Off).
So Bob, I have taken your advise and set it up with two separate AE's. Is this what you were talking about basically?
Initializing in the Main Producer
Event Case if a User manually changes Valve State
Event Case if User Wants to set All Valves
Don't critique the use of everything else, the main VI is still in the works.
Largely. I would get rid of the U8 quantities and stick with an 8-element Array of Boolean (initialized using Initialize Array in the Init action of Valve Control. Valve Control should have no Valve Hardware VIs inside it. Note that if you are dealing with arrays, and if Valve to Control is basically an index into the array, setting Valve On corresponds to setting that index element to True, i.e. it becomes a Replace Array Subset (element).
The missing "Third Element" is the routine that puts it all together. When the Top Level routine starts, it calls Init for the Valve Control, calls Init for the Hardware, and calls Set Valve (to turn all the valves off). The Valve Array should be carried in a Shift Register in both Action Engines. However, the Hardware AE should ignore Valves In unless it was Set Valves (in contrast, Valves Out should always be wired).
I am not seeing the point of making 2 AEs, especially since you should not be calling the hardware's "set" case from the main VI. And you are duplicating effort by having both hold the DIO state (and the Valve AE could have "old" data if the HW AE was called). So what is wrong with just having all of the functionality in a single AE? It makes the API A LOT simpler. So the states I see are: