Random Ramblings on LabVIEW Design

Community Browser
Labels
cancel
Showing results for 
Search instead for 
Did you mean: 

A Good Design ...... Does One Thing Only

Active Participant

Hello Peeps,

Hope you are doing well in these trying times.

If you follow this blog you would have been exposed to the term cohesion quite a lot and in computer science it is a means to group together related functionality. You would use it to help manage complexity in the modules of your system. In text based languages it could be as simple as grouping like functions in a library file. For SSDC we view the block diagram as our method of expressing the design logic and our methodology wants to group functionality in a single VI. Whereas other designers want that as a class, actor or DQMH module. All methods are striving to achieve this management of complexity. So far so simple.

 

For me there's always been a question mark against this definition and I now feel it is incomplete.

 

Let's look at a Oscilloscope as an example.

 

ScopePalette.png

 

These VIs are all successfully doing one thing (mostly...), woohoo we have cohesion, jobs a good'un.

 

If you follow our LCOD design principles that's not enough, at some point we want to wrap these functions in another VI, because at some level of abstraction we don't care about initialising an oscilloscope, we just want to get a reading. An important benefit of doing this is that now we have an interface for our implementation to hide behind. This protects our software from changes below this level of abstraction. Simply put, we can change the oscilloscope type, but the software just gets a reading.

 

So what point is appropriate for moving from our NI-SCOPE palette VIs to a wrapper, it's pretty easy...we don't want to repeat calls to a NI-SCOPE palette VI in more than place.

 

ScopeWrapper.png

And here it is being used.

 

ScopeWrapperUsage.png

 

So in summary here's a more complete definition of cohesion ...

 

A module should do 1 thing only and that thing should be appropriate to the level of abstraction.

 

Lots of love

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Comments
Active Participant

"view the block diagram as our method of expressing the design logic"

 

No design documents other than the code? 


Certified LabVIEW Architect, Certified Professional Instructor, LabVIEW FPGA expert
ALE Consultants
LabVIEW Programming (make LV more popular, read this)

LabVIEW FPGA for High Throughput Applications | VI Week 2020
Active Participant

For us it depends who we are communicating the design to. If our customer is not interested in the LabVIEW design, we use the source code as our design documentation internally and that is all. The exception is if a complex problem that needs describing. This is on the basis of a known design methodology and a template we all use.

 

For customers we will use enough documentation to describe our design intentions. If they are taking over support of the source-code we will use state transition diagrams, ERDs for databases, generally we don't use UML as our object/component identification is simple enough to be described in LabVIEW.

 

Every time you leave LabVIEW to clarify or document a design is an indication that there is a lack of clarity in that design. So extra design documentation is viewed as a necessary evil at SSDC towers.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Active Participant

@swatts wrote:

...at some level of abstraction we don't care about initialising an [some-device], we just want to get a reading


Literally, word for word what I said yesterday in a customer meeting 🙂

 

I completely and wholeheartedly agree.

 

An opportunity to learn from experienced developers / entrepreneurs (Fab, Steve and Brian amongst them):
DSH Pragmatic Software Development Workshops
Automate the analyzing, testing, documenting, building, packaging and publishing of your projects via CI/CD:
Release Automation Tools for LabVIEW

Member

When you write a good design does one thing only.  Do you mean it makes for a program that is easy to understand?  Or a program that functions as intended?

            All customers are looking for a software solution to monitor something using a bit of math.  No matter how complex; they want to know they are getting what they asked for.  Some customers rely on stories; some want to see the code. 

I had a customer ask me to see the code for software written by NI.  Due to abstraction and being 8-10 layers of SubVIs they decided they would rather have it written again, which I did using 2 subVIs. It is because they wanted to see the code and confirm it matched a known formula.  That is not a regular request for me, but it is not uncommon either. 

 

It is a balancing act.  Burry some functionality but not so deep that it makes no sense.  With regard to device drivers, I want a measurement I don't want an initialize.  So I write my measurement functions to check if a device is initialized and handle and then take a measurement.  Then when someone looks at the code they see where the measurement is.  There is no call to initialize in the top level VI, because that functionality is not what the customer wants.  I have never had a customer say, I want all devices initialized when the program starts or when the device is called.  They don't think that way.  They are looking for features.  If someone cares about how the measurement is made they can dig deeper.

 

So therein lies the problem.  How deep does a person need to dig to know what is going on.  Personally I think 3 layers is about as deep as it should be.  If it is truly complex, then four layers.  That is not to say I write software where the entire program has no SubVI more than four layers below the main VI.  Just that math functions are three layers and driver functions are 3 layers and reporting functions are three layers. Event handling is 3 layers.

 

In order for each function to do abide by the rule “only one function per vi” a case structure would always be the top level VI.  Then for each case to call a SubVI. that seems excessive if a SubVI does a single process like concatenate two strings. Or multiply an array by a single coefficient. 

 

I have no point to this whole story other than to say when it comes to programming etiquette there is no rule that works universally.  But I guess you said the same thing "appropriate to the level of abstraction."  Just that we as programmers have to decide what is appropriate at every step and it can change from customer to customer.

Active Participant

Hi Richard, 

"When you write a good design does one thing only.  Do you mean it makes for a program that is easy to understand?  Or a program that functions as intended?" <--- Generally they are related, my prime concern is easy to understand, functions as intended is a given.

"So therein lies the problem.  How deep does a person need to dig to know what is going on.  Personally I think 3 layers is about as deep as it should be.  If it is truly complex, then four layers." <--- 180 - Seeing the moving parts  talks about exactly this, and I completely agree with you. I think my limit is 3 or 4 layers too. I think this can be increased for drivers within drivers - so for something like databases you won't be digging deep into the driver unless there is something very specific. So for project level debugging and general navigation of your code is my caveat.

"I have no point to this whole story other than to say when it comes to programming etiquette there is no rule that works universally.  But I guess you said the same thing "appropriate to the level of abstraction."  Just that we as programmers have to decide what is appropriate at every step and it can change from customer to customer." <--- you call it programmer etiquette, I call it design, I rather like programming etiquette as a term tho'

 

To summarise, I agree. I also think layers of abstraction is an interesting area to consider some more!

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Active Participant

Funny, I never really worried about layers of subvis. As long as they were cohesive and appropriately named with a nice icon and description, it never really seemed to be a problem. i probably naturally end up with somewhere around 3-5 layers, but never really paid attention to it. I feel like when you use OOP you do end up with potentially a few more layers.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
automatedenver.com
GCentral
Active Participant

on that same note I feel like if you need to drill down into a subvi to figure out what is going on at a higher level you are doing something wrong. Typically the only time I am drilling down into subvis is when I am troubleshooting. In that case granularity is generally a good thing.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
automatedenver.com
GCentral
Member

Sam,

Don't we all have to drill down to figure out why something is wrong?  That is a given.  The responsible thing is not to make a program so overly complex with abstraction that it is impossible to figure out the source.  

In an ideal world, we right something that works for all situations and we put a wall around it so nobody can break it including ourselves.

Active Participant

Well I guess there are 2 types of reading code. One is just generally figuring out what is going on. Perhaps in preparation to add a feature. The other is debugging.

 

When I am just trying to figure out what is going on, it is a code smell when I have to dive way down into a lower level subvi to understand what is going on in the top level vi. Typically it means I didn't do a good job of naming and documenting my subvis.  In that case I don't care how deep the subvis are nested because I am generally not digging that deep, 1 to 2 layers at most. 

 

When it comes to debugging you have to dig down deeper. For whatever reason in that case multiple layers of subvis doesn't really seem to bother me. I dig down as deep as I need to . If it happens to be 5 layers, so be it. If I can stop at layer 3, great!

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
automatedenver.com
GCentral
Active Participant

There is a big difference between abstraction layers you can trust and those you cannot trust.  If you can trust it to work as expected then you don't need to dig into the details.  Dig all the way down to the Silicon and we are all depending on a very large number of abstraction layers.  It's the number we cannot trust, and thus have to understand the details of, that matter.  

Proven Zealot

The grouping of 3-5 units is a given based on human object tracking ability.

 

https://jov.arvojournals.org/article.aspx?articleid=2121950

 

It seems to be a limit which is somehow supported by experimental evidence.  Although the above link describes moving objects, I think the handling of levels of VIs is not SO different, LabVIEW is a visual programming language after all.  I think this was mentioned in a talk at GDevCon#2, I don't remember whose talk it was.

 

I would argue that any given abstraction layer should be 3-5 VIs deep at most (more 3 than 5 to be honest). Of course the final VI call chain may be larger, but it should be spread over multiple layers of abstraction, clearly identifiable as such and where each abstraction makes "sense".  Any more and the blob of neurons inside your skull starts cutting corners when digging deeper.

Active Participant

That'll be me at NIWeek as well as GDevCon

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Active Participant

The 3-5 rule applies to more than just abstraction layers.  There's interacting modules/actors/loops.  Regardless of the complexity of the application, any programming task should only require you to hold the details of no more than 3-5 things in your mind.  That's a big reason to favour a tree-like structure of components, so no individual node in the tree requires understanding more than 3-5 other components.

Active Participant

@drjdpowell wrote:

 

There is a big difference between abstraction layers you can trust and those you cannot trust.  If you can trust it to work as expected then you don't need to dig into the details. 

 

This is the point I was trying to make.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
automatedenver.com
GCentral
Member

Abstraction provides a useful tool eliminating the need to know how a system works.  We all create and rely on its benefit.  But I just happened to come across a scenario a few days back where it was a layer inside a layer inside a layer.  And if it was not there, it would have been a lot easier to understand functionality.  And translate it for the customer.

 

  It definitely has value, but when it is used inside other layers of abstraction there needs to be documentation.  A common belief is that the code speaks for itself.  And yes of course it does.  But when we create layers we need to add documentation about what goes underneath.

 

Am I the only one that has icon bias.  I trust code that has nice icons.  I believe if the creator spent the time to create a beautiful icon they also spent the time validating their code?

 

 

 

Active Participant

@richardscheff wrote:

Abstraction provides a useful tool eliminating the need to know how a system works.  We all create and rely on its benefit.  But I just happened to come across a scenario a few days back where it was a layer inside a layer inside a layer.  And if it was not there, it would have been a lot easier to understand functionality.  And translate it for the customer.

 

  It definitely has value, but when it is used inside other layers of abstraction there needs to be documentation.  A common belief is that the code speaks for itself.  And yes of course it does.  But when we create layers we need to add documentation about what goes underneath.

 



There are definitely architecture astronauts out there that create systems where you have to click through 20 layers of dynamic dispatch to find a VI that actually does something. At that point you have to ask what purpose all dynamic dispatch serve and evaluate if it worth  the price in terms of code readability.

In most codebases clicking through subvis doesn't usually bother me and isn't an issue. In fact I find a much bigger issue is the opposite, lack of subvis or as Fab likes to call it THE VI.

 

There are edge cases for every scenario.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
automatedenver.com
GCentral
Active Participant

As an aside, I note that "drilling down through subVIs" is not the only method of navigating code.  Personally, my favourite navigation method is "open diagram from subpanel in running code".  That's quick, intuitive, and handles dynamically-loaded, shared-reentrant dynamic-dispatch clones in some fancy-smancy plugin architecture without complaint.

Member

Interesting discussion! I get the points you all made about abstraction in general, and levels thereof.

 

However, going back to Steve's opening post, I'm still grappling with the benefit of a wrapper VI. Sure, the 'customer' or layer closest to the UI would not want to be bothered with details; except for the measurement. Where is the specification of the resource ID (name) and its configuration (say serial port, baud rate, measurement-specific setting, etc.) coming from?

 

The pre-packaged 'easy' VIs (those first seen in an instrument palette without going into 'Low Level' or 'Advanced' sub-palettes) seem to either have constants in the block diagram for configuration or they have a bunch of cluster inputs with default values. More often than not, I end up using these VIs as an example to understand how an unfamiliar instrument works.

 

Do you folks use MAX (or equivalent?) to do all the configuration, thereby simply providing only the resource I/O refnum? Do you use global virtual channels, which eliminates the resource specification in LabVIEW altogether? Any other approaches?

 

I use LVOOP generally. I try to define my abstractions from the caller's point of view as you folks have discussed above. (E.g. ledCountDetected.vi is a method which actually captures a camera image and determines the LEDs detected based on a pre-defined configuration, then returns the count.) My need for an abstract layer is, however, predicated by customer requirements for swapping devices/instruments or my ability to test code without access to hardware. I typically use an INI file, where the section name corresponds to the object ID from the application's perspective. The key-value pairs then have all the required 'class' configuration parameters.

 

Vishak

 

Active Participant

Hi Vishak,

The fact it is an interesting discussion and there are multiple points of view indicates to me that there is nuance to the discussion. So I'll say my bit and justify it, someone else may have an equally valid different opinion. So there is a level of personal comfort here...

 

That said here's my company position.

 

The wrapper is a barrier against change, so it would be very unusual for SSDC to leave any hardware without a wrapper. That said we're working on large projects and this type of design is to help manage complexity in large projects.

 

We do not use MAX, our reason is that we have configuration in our software, so find it hard to justify carrying around another set of dependencies to configure our hardware.

 

Wrapping our hardware also gives us some independence from a particular vendors tools.

 

"My need for an abstract layer is, however, predicated by customer requirements for swapping devices/instruments or my ability to test code without access to hardware. I typically use an INI file, where the section name corresponds to the object ID from the application's perspective. The key-value pairs then have all the required 'class' configuration parameters."

 

Please note, when I'm talking about abstractions I'm not specifically talking OO here, just general design. In the quote about you give an excellent use case and mechanism for wrapping your hardware.

Other reasons mentioned here - less to think about - a good abstraction is easier on the brain

Protecting your code above the abstraction from change, the abstraction interface will change less often than the internals and this simplifies the management of your codebase.

Changes should be localised - you're not changing constants all over your code (I have foundational stories about this!)

Navigation is easier - moving around your code should be more direct, if it isn't it might be worth considering your layers.

 

I'm sure there are more..

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile

Member

I completely agree, Steve.

 

I seem to have gotten stuck on the notion of 'wrapper VI' rather than the more important message of abstraction.

 

Vishak

Active Participant

I probably don't use the right language either, so it's a good exercise for me too.

Steve


Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop


Random Ramblings Index
My Profile