The sequencer design pattern is a way to organize code on the block diagram without hiding code behind subVIs.
In LabVIEW we often find ourselves writing code that extends beyond the width of our monitors. For example, VI Server or VI Scripting calls can become very wide. Classically we have addressed this problem by creating subVIs to organize our code. A subVI's main benefits are to enable code reuse, encapsulate an idea in a single unit, and enable unit testing. But to use subVIs to simply make code fit on one screen means you pay the cost of creating subVIs (documentation, unit tests, connector pane styles, etc) without the benefits of code reuse. Further, you hide implementation details from the block diagram and create unnecessary depth to your VI hierarchy.
I believe that creating subVIs for code organization versus reuse isn't organization at all; it is obfuscation. So I've created a very simple pattern that makes your code very readable and abstract while maintaining the low level implementation details all in one diagram. I call this pattern the "Sequencer".
The sequencer is a modified state machine where each state affects the sequence only through means of returning an error. On error, the machine stops. The sequencer is based on CLAD level syntax and is implemented with arrays, case structures, for loops, a shift register and clusters.
Below you see the Sequencer TemplateSequencer Pattern
Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.
Do you ever find it painful to put references inside of the cluster if you need them in another state?
jiggawax,
I'm curious why you ask that? I would think that references would work like any other piece of data.
"I believe that creating subVIs for code organization versus reuse isn't organization at all; it is obfuscation."
This is quite the statement!
Surely refactoring for the sake of readability and maintainability (and not reuse) has value in many use-cases.
@jiggawax I don't... no more than other datatypes as @Taggart has mentioned.
@RaymondFarmer, you mentioned
"Are you saying that rather than have a load of VI's on your block diagram you have you have the code from each of the VI's pasted into the each case statement?"
Kind of. I'm saying that I prefer to abstract where necessary. Hiding details behind subVIs is cool so long as I get the benefits of subVIs, such as reusability. However, in some types of code I find myself writing just sequencing a simple series of steps. Sometimes those steps are subVIs and sometimes they are property nodes and invoke nodes (in the case of VI Scripting as David More points out in his article at http://www.mooregoodideas.com/technical-discussion/vi-scripting-pattern/).
I find that summarizing the different stages of my VI as a sequence helps me see, at a 10000 foot view, what's going on by looking at the array of constants on the left side of the screen. I can also quickly see what data each sequence is using and editing by virtue of the cluster. I'm not saying subVIs are useless (in my current project I have over 3000 subVIs). Rather I'm saying that subVIs that are not part of an API or intended for reuse seem to hide my ideas rather than make them obvious.
@niNickC , I often find architecting is just as much art as is it science. But as I write more and more code, it seems that long sequences of subVIs can be abstracted to an even higher level of thought by virtue of sequences. Almost like a TestStand sequence. So, as I mention above, I'm in no way advocating that we not use subVIs. Rather using subVIs to organize screen real estate (wide VIs spanning multiple monitors and beyond) seems to cause me more pain than help because the implementation details are buried one layer down from the usage of that idea. That's at the heart of my statement.
Eric M. - Senior Software Engineer
Certified LabVIEW Architect - Certified LabVIEW Embedded Systems Developer - Certified LabWindows™/CVI Developer
Neosoft Technologies inc.
Maybe I misunderstand the purpose of this architecture but it seems like it would be painful when dealing with nested objects. For instance, let's say that I get a VI reference in Case A and then in Case B I get a reference to the Front Panel. Then in Case C I get a reference to a Control and then in Case D I get a reference to some object the control contains. Do I now have to add references for each of the objects to the cluster so I can access in later cases?
I see this a lot dealing with Word or Excel where you have to drill down the containment chain to get a single cell or object. Then at the end you have to close all of the references.
Typically I'd use a SubVI to get the object I wanted to inspect. However, it seems the purpose here is to get rid of SubVIs and shorten the width of your top level VI????
Am I missing something?
@ Eric.M
That's a good catch! There's really no need to have the error cluster in the locals cluster. I think I originally put it in there just to demonstrate how you might add \ use some local data. I didn't want to ship a broken VI because of an empty cluster. But I think I agree with you. The error can simply be passed out of the for loop using the "last value" tunnel. I'll probably update the pattern and replace the error cluster with something like a string titled "<Replace me>". Thanks!
In order for a case to have access to some data, the data will have to be added to the locals cluster. I don't know if I would agree that "nested objects" is an accurate description however. The VI Reference, Front Panel Reference, and Control Reference can each be stored in the locals cluster in a flat data structure (not nested).
As for the overall purpose, I would reword your summary as "The sequence pattern is designed to represent a sequence of function calls in a more abstract way without unnecessarily adding to the VI hierarchy's depth". Benefits are
The goal is not to avoid the usage of subVIs but to avoid the unnecessary creation of subVIs for a given type of application (a sequencing application \ algorithm) while making the code easier to ready.
As for closing references, this pattern does not change how you would close references. Hope that helps!