Jump to content

Request: name any VI you've written where you wish special behavior for unwired terminals was available


Recommended Posts

There is a feature I've wanted in LabVIEW for a long time, pretty much since I started programming in G: The ability to have an unwired input terminal have meaning.

Currently, if you leave an input terminal unwired, you set it to Recommended and you give the control a default value. That doesn't really mean the user left the terminal unwired... they could have wired the default value. That doesn't really help when, for example, all possible integers are valid values for the input. You end up having to add a second Boolean terminal for "should pay attention to integer?" or something like that. Even when you do have a valid sentinel, you may have to add code to check "is the input value the sentinel?" in the middle of your executing code, which is a small but annoying performance hit, and one that can ooch up over time. Think of how many times we check error input terminals and how much of that code could be dead code eliminated if only subVIs generated code specific to their callers. That's a *minor* example. There are a lot more interesting algorithmic changes that are possible for an unwired terminal... some of the built-in primitives of LabVIEW have very different behavior when inputs are left unwired.

Currently, if you leave an output terminal unwired, LabVIEW doesn't care... the value of that output gets computed anyway when the subVI is called because the subVI has only one code path. This has lead me to have to have extra terminals on input for "should compute output X?". You can fix this sometimes by marking the subVI to be "inline" and then when you turn off debugging, the computation code likely gets dead code eliminated. But not always. If the computation involves a refnum, it won't get dead code eliminated. And if there are interim data structures along the way that you populate, those will still get built up if they're part of a downstream terminal that is wired. The compiler probably (IMO) will never be smart enough to analyze and do that level of elimination until the compiler is sentient. So some sort of computation here would be valuable, I think.

I've investigated this before and always hit some limits. I'm thinking I might make another run at figuring out how this could work. No promises, but there's some new ideas out there on code optimization that I might be able to leverage.

The unwired output case is pretty straightforward... I'm not too interested in unwired output cases unless you have one that is radically different from "this output is expensive to compute so I only want to do this part of this larger algorithm when that output is wired".

I am interested in unwired input use cases.

So as part of my brainstorm on this topic (and to gauge whether or not this would have benefit to users other than myself!), I'm curious to hear about any VIs y'all have written where being able to program in unwired terminal behavior would have been useful for you.

There's no deadline on this. I am just sort of musing on this topic lately. I don't know if it will lead to any LV features. I just want to explore this particular topic more.

Link to comment

I'm not sure I'm understanding this fully. Programmable inputs? How would they be programmed-isn't that what our code is for?

Going off half-cocked, as per usual, I could perhaps see a benefit of dynamically loading (plugins, DLLs etc) whereby you could define a list of files/names (so a programmable path input control) and somehow it would figure out which one to use. For example a path input on the CLFN that would load a 32 or 64 bit DLL depending on the LabVIEW version - so a kind of conditional path. But like I said, I'm not sure I know what you are getting at so perhaps give a concrete example in another language or a piece of LV code that would benefit from this feature.

 

Link to comment

I've wanted this feature on several occasions.  I had a discussion on LAVA about it a while ago that I can't find and the suggestion by Darren was to use the VI analyzer to find when subVI terminals weren't wired...he clearly didn't understand what I was requesting.

So in my project today as a lone developer I probably have 10-20 VIs that I could use this feature on if it existed.  Instead I'm usually forced to do something like set the default value and check against it.  For doubles this isn't a big deal, I just set the default to NaN and check for that.  Arrays I look for an empty array, strings I look for empty strings.  But integers I don't have a good solution for and will sometimes make the input a double, and set it to NaN.  Admittedly in my cases most could be resolved if wanted to bother with polymorphic VIs.  So I may have something like convert a typed cluster to a string that is human readable.  But I also might want to convert an array of those clusters into a scalar string.  So I'll have a VI that has both the scalar and the array of clusters and look to see if the array is empty, and then operate on the scalar cluster instead.  Most examples I have are derivatives of this.  Having a path, VI refnum, and string input is another one I remember.  If the VI refnum is valid perform some VI server call on it.  If it isn't but the path control isn't empty then open a VI reference then operate on that.  If the VI refnum is empty, and path are empty, do a open VI reference on the VI name.

There are a few advanced string functions I wanted to create for getting string subsets.  Where things like unwired inputs meant the rest of the string, mirroring some of the LabVIEW primitives.  Of course in these cases I usually have a numeric input that has the default set to -1 and then have the check for <0 and if so do something special.  This one is pretty common for getting subsets of other data too like arrays or circular buffers.  I would use this function if it existed to make more intuitive code that mirrors some of the palette functions today, and I'd use it to help skirt around the weaknesses of polymorphic VIs.  Once you realize how the delete from array function works, you like the fact that it behaves differently when some inputs are unwired.  I'd like my code to have similar abilities some times.

Link to comment

I'm fully on board with not wanting to check flags to see if an input is valid.

I dislike the idea of relying on a compiler to decide if it can throw away dead code paths since I have no feedback on what the compiler does-- it's too opaque. It also creates a weird pseudo dependency inversion (not having a CS backround I'm unaware of a name for what I'm thinking). If I rely on the compiler I can't benchmark a VI without considering the context of how that VI is called.

I also can't see a way to resolve this when you consider dynamically loaded VIs. What happens if after analyzing all the statically linked callers to a VI the compiler decides some chunks of code can be discarded and not computed. Then I load a VI which wasn't part of the original application which requires the discarded code. Clearly some alternate implementation needs to exist that either has the fallback unoptimized VI or the compiler has to create a multitude of implementations for different optimization paths (b l o a t). Regardless, debugging what's going on would be fantastiaclly difficult if all of this is hidden under the hood of the black-box compiler.

Similar issues creep up when you consider executable distributions of libraries. How can I create optimized versions of a library if I'm unaware of the contexts from which it will be called? Relying on the compiler then requires my library to be distributed as source code such that the consuming application can make the optimizations. Maybe I don't want to do that?

TL;DR: I've had the initial reaction of wishing I could be aware of unwired inputs before, but when I think about it I ultimately go down the path of VI (not object) polymorphism because I believe it to be a better solution.

VI polymorphism allows me to clearly define implementations with varying input parameters and the execution path is fully resolved at compile time. It also allows me to clearly define multiple implementations which can be executed by other VIs which may have dynamic linkage. While the grammar of using a polymorphic VI isn't quite what you're after, I believe functionally the existing language is already there.

Edited by mje
Link to comment

I'm also onboard about not having to check and branch for an invalid default input, but an use case where that would really have been a hindrance never really occurred to me. Putting aside for a moment the considerations about use case ("would a polymorphic implementation be leaner?"), it seems to me the problem splits into two different parts. This is how I would envision using them, but I don't really know what is going on under the hood to understand if it they make compiler-wise sense.

a) a scripting Property node like say VIserver/Generic/ConnectorPane/IsTerminalWired[], returning an array of booleans

b) a new special Case/Conditional Disable hybrid, allowing a control terminal (unlike the Disable) connectable to these booleans, but imposing the elimination of dead code (unlike the Case).

Now, a)'s results would be determined at compile time, looking up at the calling context, and perused by the programmed code. I suppose the wire could be as well probed for debugging, and if the VI is used multiple times, called by ref or whatnot, the result would reflect the called instance / clone being run and probed. It's b) that looks to me quite awkward. ("a new special frame, seriously? accepting only compile-time determined boolean inputs? Are boolean operations on compile-time booleans permitted?"). a) would just tell about terminals and b) would make explicit (?) the optimization, but?

Link to comment
20 hours ago, Aristos Queue said:

You end up having to add a second Boolean terminal for "should pay attention to integer?" or something like that.

This sounds like a code smell. I don't think I have ever done that. Is this something to do with classes requiring the same connector for overrides?

Link to comment
On 1/4/2017 at 4:39 PM, ShaunR said:

I'm not sure I'm understanding this fully. Programmable inputs? How would they be programmed-isn't that what our code is for?

As in, when caller leaves terminal unwired, subVI has behavior X. When caller wires the terminal, subVI has behavior Y. This is distinct from simply using the default value of the terminal.

Link to comment

Steven, I think this would be a useful feature. Don't we do this all the time on inputs by checking their values? Sometimes we are checking the values of inputs on VIs because we are actually concerned with the incoming value and sometimes we are checking because we want to know if the user of the function set the value (wired the input). Unless we actually comment our code well, it might not be immediately obvious what our intent was for checking the incoming values. The feature you describe would enable us to be more explicit about our intentions without actually adding code comments. Self documenting!!

I remember sitting with Mike (VI Shots) once and he was wishing this feature existed. It was a couple of years ago so I'd be curious to see if he remembers saying that.

Eric

Link to comment

I'd quite like this feature for cases where the natural type for an input is a ring or enum, but you also want a "don;t change" as the default option. Yes, you can have "don't change" as one of the enum/ring values, but that then gives an ugly API. There's certain instrument driver type subvi's I have where the instrument has a mode e.g. a waveform shape, and some common parameters - amplitude, offset, duty cycle. One could have separate subvis for each of these parameters, but it's also nice to have just the one vi that would change only the parameters that were wired up and would leave alone those that weren't. 

Link to comment

Great example Gavin.  I had one recently where a subVI would be used to log data into a report.  For all most all logged data we have a pass fail criteria so one input is an enum of the different limit types (Greater, Greater or equal, Less, Less or equal, Equal, Not Equal, In Range, Out of Range, or Within Tolerance).  But there are a few times when we want to log the data, but not have a limit condition.  I could have made a separate function that logged but didn't have a limit, and I could have had a case where my enum had a "None" option, but this enum is used in other places where the None doesn't make sense.  So I had my limit set to NaN and I check to see if it is a number and if it isn't then I assume it is unwired, and to not have a limit.  It would have been nicer to be able to detect the limit enum was unwired, and to therefor log the data but don't perform any pass/fail checking on it.

Link to comment
11 hours ago, hooovahh said:

Great example Gavin.  I had one recently where a subVI would be used to log data into a report.  For all most all logged data we have a pass fail criteria so one input is an enum of the different limit types (Greater, Greater or equal, Less, Less or equal, Equal, Not Equal, In Range, Out of Range, or Within Tolerance).  But there are a few times when we want to log the data, but not have a limit condition.  I could have made a separate function that logged but didn't have a limit, and I could have had a case where my enum had a "None" option, but this enum is used in other places where the None doesn't make sense.  So I had my limit set to NaN and I check to see if it is a number and if it isn't then I assume it is unwired, and to not have a limit.  It would have been nicer to be able to detect the limit enum was unwired, and to therefor log the data but don't perform any pass/fail checking on it.

That is pretty weak. For non enums (rings etc) the default can be anything that's not in the list and use the default frame of a case structure. It is at least visible to the maintainer by inspection of the diagram. For enums, adding an extra entry is non-sensical? (You could have used "OFF", "N/A" or any number of words to make more sense) The proposed alternative is a "setting" on terminals that changes the behaviour so that you have to know has been set.? Does this mean that "style" would then be to have a statement on the digram that the setting is in play since there is no code indication?

Edited by ShaunR
Link to comment

In the specific example I had in mind, keeping the input at the same value would cause the instrument to reset, whilst not changing the input doesn't - so 'No Change' is different from 'the same again'. You can then either expose the "No Change" option to the end user - but that might be more confusing. The alternative (which is what I normnally end up doing) is to have separate enums/rings for the UI and the underyling driver - but then you have to keep them consistent otherwise it gets very confusing(!). (there's other alternative strategies like maintaining state either in the caller or subvi - or possibly hardware -  of course).

In the subvi, I'd argue that code that detects sentinel values e.g. by looking for NaN in floats or default values for rings would be less transparent than each control having a property node IsWired? that is only true if that control was wired when that subvi was called. Bounds testing on control values is then just about making sure its a valid value and doesn't get overloaded with a "value was supplied" meaning as well.

Link to comment
2 hours ago, gb119 said:

In the subvi, I'd argue that code that detects sentinel values e.g. by looking for NaN in floats or default values for rings would be less transparent than each control having a property node IsWired? that is only true if that control was wired when that subvi was called. Bounds testing on control values is then just about making sure its a valid value and doesn't get overloaded with a "value was supplied" meaning as well.

There is also != with a shift register/feedback node. That is the common method for detecting change in a sub VI - poor-mans On_Change event.

Edited by ShaunR
Link to comment
On 1/4/2017 at 11:35 AM, Aristos Queue said:

I am interested in unwired input use cases.

I've done this several times in recent memory:
-Writing to a file with a timestamp in the header, if input timestamp is not wired use current time else use wired timestamp.
-I made a little cluster unbundler helper function. I have a cluster of basically hardware resources and then each hardware resource has a 'measurement' associated with it. If you leave the measurement selection unwired it returns all measurements from all hardware, if you wire up N it returns measurement N, if it exists, from all hardware.
-Made a function which returns a set of strings, for example, then later added a filtering feature. If I implemented the filtering feature straight I'd have to require that the filter array always has .* by default. So if that particular field is empty, I treat it as the old unfiltered function and if that array of filters is not empty I do the new pattern match feature.

Obviously all of the above work just fine, but there are occasional hoops you have to jump through (for case two for example I have to build what I know to be a single element into an array so the loop concatenating output will not be mad at me). Its also difficult to indicate in a very obvious way to the consumer of such a function that the behavior is as stated, unless you encode a long description into the terminal name.

Link to comment
5 hours ago, ShaunR said:

There is also != with a shift register/feedback node. That is the common method for detecting change in a sub VI - poor-mans On_Change event.

Indeed - this is what I had in mind as 'keeping state in the sub-vi'. The OpenG library polymorphic vi is a nice little wrapper for this.

Link to comment
23 minutes ago, gb119 said:

Indeed - this is what I had in mind as 'keeping state in the sub-vi'. The OpenG library polymorphic vi is a nice little wrapper for this.

I've always wanted the Val (Sgnl) property node to fire when new data arrives at the control as well as when the user clicks on it so that I could use events instead of a VI.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.