Jump to content

Recommended Posts

Hi.

I'm working alot with a concept I call AutoQueues - basically named queues that name themselves in clever ways to allow for "automatically" connecting data paths across the LabVIEW instance. The naming logic depends on the context, and the queue scope might be much narrower than the entire LV instance, sometimes all the way down to within a single VI. I can explain a bit more about AutoQueues later, but as an experiment I'd like to share one of my toolsets that builds upon that concept, namely the VIRegister toolset.

VIRegisters:

The VIRegister LabVIEW toolset is an intra-process communications media. A VIRegister combines the performance and simplicity in use of a local variable with the scope of a global variable – without any binding to front panels or files. You simply drop in these VIRegisters wherever you'd normally drop a local variable to write to or read from, or rather wherever you'd wish you could just drop a local variable :rolleyes:. Even though the toolset consists of more than 160 VIs underneath, these four polymorphic VIs are the ones you drop in your code:

post-15239-0-55024500-1308856055_thumb.p

VIRegister features:

  • VIRegisters are independent of controls, indicators, projects and files – they exist in the block diagram(s) only.
  • The scope of a VIRegister is user configurable, from encompassing the entire LabVIEW instance all the way down to covering a single VI only. So you can use VIRegisters to share data between parallel structures within a single VI, to share data between subVIs, or to share data between entirely independent VI hierarchies for instance. The included documentation explains how the scope is defined - it's all very easy, and has to do with VI references and the call chain.
  • VIRegisters come with and without error terminals to support both parallel and dataflow bound execution.
  • VIRegisters are implemented as single-element named queues, using lossy enqueue for writing and preview queue element for reading. Don’t worry, the queue names are quite esoteric (and dynamic), so there’s extremely low risk of namespace collision with your own named queues (you wouldn’t normally use “eXatvx‚ƒt/</Rtpƒt`„t„t]p|t=…xnatvn_t_tƒt_tst@SXBA” for instance to name your own queue, would you? And no, it's not just random characters ;)).
  • VIRegister performance is normally in the millions of operations per second. They typically update 2-3 times slower than local and global variables, but since we’re dealing with lossy transactions absolute performance is seldom a bottleneck. Sub-microsecond latency is still pretty useful for most signaling applications.

31 data types are currently supported (very easy to expand, but I unfortunately need an "anything" type input to be able to support generic clusters and such - this has been suggested ad nauseam in NI LabVIEW Idea Exchange :rolleyes:):

post-15239-0-60562500-1308857730_thumb.p

The code is compiled with LabVIEW 2009 SP1, but should be quite easy to port back and forth. The toolset is released under a BSD license, see the included license.txt file. Any comments are welcome!

Cheers,

Steen

Edited by Steen Schmidt
  • Like 2

Share this post


Link to post
Share on other sites

Haven't tried the functionality, but the code and icons look very clean and nice!

Your comment on needing an "anything" type -- strikes me as a perfect case for writing an XNode, which would mean you could support any cluster (or any dimension array) without having to recode at all. It's not quite as easy as a true "anything" type, but it shouldn't be difficult.

Share this post


Link to post
Share on other sites

This looks like a smart take on of what NI has posted as a "Current-Value-Table Reference Library".

They have used functional globals to store the data instead of queues which makes it easy to access the list of available registers, debug etc. One of the weakest points of their implementation is the slow search it uses to look up the "Register Name" (they could use a binary search instead or use the search capability of variant attributes to dramatically improve this, but your approach gets that part for free from the queue functions).

Perhaps the two efforts could be joined?

PS. The polymorphic VIs do not have the "Allow Polymorhpic VI to Adapt to Data Type"-flag set so they did not work as I expected in my LV2010.

Edited by Mads

Share this post


Link to post
Share on other sites

Thanks.

You're right about XNodes, but I'm loath of using them since they are unsupported. I like to be able to move my code forward through LabVIEW versions without it breaking too much.

Another issue is that an XNode probably wouldn't be the fix all for VIRegisters - I might've been too quick there. For sure an anything-type XNode would enable me to make a generic write function instead of the polyVI, but since we have no upstream type propagation, I can't use the same trick for the read function (and there are good arguments for not implementing upstream type propagation in LabVIEW). The Value output of the read function needs to know its type at edit-time, and since there isn't even a requirement for a VIRegister write being present in the same edit session as we're using the read, I would have no way of knowing what the data type of the output should be before runtime. The programmer using the VIRegister read will know this data type of course, so some form of dialog could be used to select the output data type - but that wouldn't be any better than using a variant VIRegister and convert the variant to data after reading it.

And a data type dialog (like when setting up the data type for a single process shared variable) isn't as lightweight as I'd like with VIRegisters. They should just be drop-in-and-use. It'd be great if you could type the name into the node through, instead of inputting it by string:

post-15239-0-69220500-1308912090_thumb.p

So the best compromise is probably to live with the variant type when sharing clusters.

Cheers,

Steen

Share this post


Link to post
Share on other sites

The reason I didn't enable "Allow Polymorhpic VI to Adapt to Data Type" is that it could potentially lead to the wrong VIRegister being shared.

The VIRegister is unique by its name and type, so a DBL named "Data" is a different VIRegister from an SGL named "Data". Only the write function will have the possibility to adapt to its input (since Value in this case is an input, on the read function Value is an output, and again; we have no upstream type propagation in LabVIEW).

If you drop a write and a read, both named "Data", and configure those as DBL, you'd be able to write and read the values without problem of course. If you then change your data source to SGL type, the write node would automatically change the VIRegister type to SGL, if the polyVI is allowed to adapt to type automatically. But since there'd be no way to propagate this change to the associated read function, you'd no longer be passing data between the VIRegister nodes. And you'll have no warning of this at edit-time, since nothing is broken.

When the polyVIs do not adapt their type automatically you'd instead just get type coercion which I think is the better option.

If I remove type from the uniqueness constraint, I could enable automatic type selection on VIRegister write. You'd still have to manually select the proper type on the VIRegister read function, and if you selected the wrong type on the read, you'd get a queue type error internally in the VIRegister, which I'd have to somehow propagate to the user. Is this a better compromise? Could be...

Cheers,

Steen

Edited by Steen Schmidt

Share this post


Link to post
Share on other sites

Regarding NI's CVT toolset (I just took a look at it - thanks for pointing it out);

Which functionality of CVT do you suggest VIRegister should implement? VIRegisters should be much faster and more lightweight on memory (queues vs. FG-VI), and each variable has its own non-blocking queue vs. a blocking FG per data type in CVT.

Also the ability to setup scope per variable in VIRegister isn't present in the CVT toolset. And the interface to VIRegister is 4 VIs, where your API in the CVT toolset is around 34 VIs (much more complex).

But I'm open for specific suggestion of course - that's the reason for my post here :rolleyes:.

Cheers,

Steen

Edited by Steen Schmidt

Share this post


Link to post
Share on other sites

Steen,

Looks like a really neat toolkit that scores highly in the "ease of use" stakes.

Going to give it a real test over the next few days and will feedback on my experience.

Hope you're well. Are you attending NI Week this year ?

Regards

Chris

Share this post


Link to post
Share on other sites

Re: XNodes -- yes, I discovered the difficulty with the Read function when I started a quick attempt at an XNode. There would of course be exactly the same problem even if LabVIEW had an "anything" type. One alternative is to require a type wired into the Read function - given that you need that for a Variant-to-Data conversion, that's not so bad, but it may make things a little more unwieldy for the standard datatypes. XNodes do have a pretty easy option of defining a right-click menu, which could be used for the standard types as easily as a polymorphic VI.

Share this post


Link to post
Share on other sites

I had another near miss idea :lightbulb: with XNodes - and that was to use the drag and drop abilities to let you drop something that defines the type of the ViRegister onto the XNode - unfortunately it won't work very well as those abilities only accept strings, paths and ProjectItem references - so you could use it to set the name of the vi register using a string or to use a custom control on disk or in a project window to set the name and type. (If there are any of the LabVIEW R&D wizards reading this and feel like letting XNodes work with variant data for drag and drop.... :worshippy:).

Re: XNodes -- yes, I discovered the difficulty with the Read function when I started a quick attempt at an XNode. There would of course be exactly the same problem even if LabVIEW had an "anything" type. One alternative is to require a type wired into the Read function - given that you need that for a Variant-to-Data conversion, that's not so bad, but it may make things a little more unwieldy for the standard datatypes. XNodes do have a pretty easy option of defining a right-click menu, which could be used for the standard types as easily as a polymorphic VI.

Share this post


Link to post
Share on other sites
The reason I didn't enable "Allow Polymorhpic VI to Adapt to Data Type" is that it could potentially lead to the wrong VIRegister being shared.
<snip>
The VIRegister is unique by its name and type, so a DBL named "Data" is a different VIRegister from an SGL named "Data". Is this a better compromise? Could be...

I've built a similar library for myself over the years. Never released it or anything. I'm not sure I agree with your decision to put a verison in the palettes that doesn't have the error terminals. For one thing, even the Obtain could fail on a low memory system. For another, asynch behavior like queue writing often requires serialization with other operations. I'm one of those people who frowns on local variables not having error terminals, for similar reasons. If you always had the error terminals available, then you could allow the name to be the only identifier and successfully return the queue error when the types and names mismatch, thus allowing the polymorphism. For my money, the ease-of-wiring of the nodes trumps the compile-time-type-checking, which is a very odd thing for me to say considering my position on other similar LV features. I'm not sure what makes this one feel different, but, well, for some reason it does.

Share this post


Link to post
Share on other sites

Steen,

Looks like a really neat toolkit that scores highly in the "ease of use" stakes.

Going to give it a real test over the next few days and will feedback on my experience.

Hope you're well. Are you attending NI Week this year ?

Regards

Chris

I'm great Chris, thanks :rolleyes:. Hope you're too?

I won't be attending NI Week unfortunately, would've been cool to run into some of you from the CLA Summit again. Are you going? Two of my colleagues will go this year - Morten Pedersen and Henrik Molsen. Henrik will do a presentation at NI Week (Flexstand, a LV plug-in based TestStand OI).

Take care,

Steen

Share this post


Link to post
Share on other sites

One alternative is to require a type wired into the Read function - given that you need that for a Variant-to-Data conversion, that's not so bad, but it may make things a little more unwieldy for the standard datatypes.

I need the data type to obtain the correct type queue, but I think it'd make these variables more unwieldy than the other offerings (locals, SVs and such) if we needed to wire a data type to every read function. The data type comes for free on the write function, but I would only consider automatic type-selection on the read function lightweight enough if the data type could be taken from the wire on the output. As we've discussed at other times, this is probably not realistic to implement (due to type selection stability in the compiler).

So I'd rather keep the polyVI and manually select the data type on the read function, ideally with an additional "Other type" selection, which would (as the only instance in the polyVI) expose a data type input. Alternatively all the instances could instead expose a "Default value" input to prime the queue with (if no value already existed in the queue) - this input would then be configured as 'recommended', except for the "Other type" instance where that input would be 'required'.

But XNodes don't work in polyVIs, right? So in a polyVI we'd need a supported "anything" input to implement the "Other type" instance. I have a couple of ideas how the interface to specifying such an input could go. I might drop a note in the LV Ideas Exchange regarding an alternative polyVI form which could also implement an antýthing input, without maybe making such a terminal globally available in LV. I don't know if that would make it simpler/safer to release such an "anything" terminal (if they only live inside the configuration dialog to a polyVI).

Cheers,

Steen

Edited by Steen Schmidt

Share this post


Link to post
Share on other sites

I'm not sure I agree with your decision to put a verison in the palettes that doesn't have the error terminals. For one thing, even the Obtain could fail on a low memory system. For another, asynch behavior like queue writing often requires serialization with other operations.

The "often" (contrary to "always") in your second argument was the reason I included a version without error terminals, but I agree that your first argument trumps that. Alone for the sake of the Obtain (or the Enqueue) possibly failing on a low memory system only the version with error terminals should exist. That version could still be running in parallel with other code, either without wiring the error terminals (the programmer bearing the fallout for that), or wiring the error terminals and then merging error wires later.

I'm one of those people who frowns on local variables not having error terminals, for similar reasons. If you always had the error terminals available, then you could allow the name to be the only identifier and successfully return the queue error when the types and names mismatch, thus allowing the polymorphism. For my money, the ease-of-wiring of the nodes trumps the compile-time-tpype-checking, which is a very odd thing for me to say considering my position on other similar LV features. I'm not sure what makes this one feel different, but, well, for some reason it does.

The extra point of failure by enabling automatic type adaption to the write function isn't catastrophic, and might easily be found less significant than the added comfort of that setting. There are many other possible failure scenarios not nullified by not enabling adapt to type; for instance wrong type selection on VIRegisters reads and writes from the start, or diverging type selections in a main application and the same-named VIRegisters in a dynamic VI.

So I'm also leaning towards it being better to enable automatic type adaption on the write function, and only to offer the VIRegister functions with error terminals. I'll update the toolset and post it here as v1.1.

But I won't touch XNodes for now (due to their unsupported nature and my limited time). I'd rather assist NI in any possible way in making a supported functionality of that sort.

Regards,

Steen

Share this post


Link to post
Share on other sites

So I'm also leaning towards it being better to enable automatic type adaption on the write function, and only to offer the VIRegister functions with error terminals. I'll update the toolset and post it here as v1.1.

But I won't touch XNodes for now (due to their unsupported nature and my limited time). I'd rather assist NI in any possible way in making a supported functionality of that sort.

I'll try an XNode implementation once you've posted v1.1 - though like you, I also have limited time for this! However, I think trying to use XNodes is valuable if it shows NI that there are benefits to officially supporting them, or something similar. I haven't had any real issues in using them across several machines and versions of LabVIEW.

Share this post


Link to post
Share on other sites

I'll try an XNode implementation once you've posted v1.1 - though like you, I also have limited time for this! However, I think trying to use XNodes is valuable if it shows NI that there are benefits to officially supporting them, or something similar. I haven't had any real issues in using them across several machines and versions of LabVIEW.

Cool, I'm curious to see the XNode implementation eventually :). I'll get back with v1.1 soon hopefully.

Cheers,

Steen

Share this post


Link to post
Share on other sites

One thing I noticed is that you cannot use one write node to write to multiple registers without risking that the previous registers are lost.

As long as no read is opened on the given register the write is the only place the queue ref is open, therefore the queue is destroyed when the write node runs a release when it switches to another register. The release node does not have the force flag set, but the queue is still destroyed because no other reference to the queue exists (necessarily). If this is the case then I think it would be more useful if you the destruction had to be done explicitly.

The test scenario that made me see this behaviour (which I did not expect, but perhaps it is intentional?) was that I planned to do a double write to 10 000 registers and then a double read of the same registers, and time the 4 operations. The reads turned out not to return an error, but they would return all but one register with default values (i.e. a DBL would return with the value 0 instead of the value I had previously written to it).

Now I only ran through this quickly so I may have overlooked something and gotten it all wrong...However perhaps you can correct me before I come to that conclusion myself :-)

Share this post


Link to post
Share on other sites

VIRegisters should be much faster and more lightweight on memory (queues vs. FG-VI), and each variable has its own non-blocking queue vs. a blocking FG per data type in CVT.

Also the ability to setup scope per variable in VIRegister isn't present in the CVT toolset. And the interface to VIRegister is 4 VIs, where your API in the CVT toolset is around 34 VIs (much more complex).

Steen,

This is a very nice set of VIs. Thank you for posting. I'm one of the developers and current owner of the CVT library, so this is of special interest to me.

I really like the ability to dynamically create new registers by simply writing to them, which is a feature we've been planning on adding to CVT for some while. I also like how you handle dynamic and static names in the VI registers using two feedback nodes. I would like to borrow this idea for CVT to eliminate the need for dual dynamic and static name access VIs. With CVT we have not added a polymorphic wrapper, but of course you could eliminate the need for separate CVT VIs for each data type, as you have done.

One big difference I see is the low level read-write performance. One of our main use cases for CVT is LabVIEW RT on cRIO, which is a much slower processor than most LV programmers are used to from Windows. Doing a very quick comparison between VIRegister and the static access CVT VIs I see a 10x faster access speed for CVT. This is not surprising as when we designed CVT several years ago we benchmarked all the different possible implementations on cRIO and picked the fastest one we could find.

Currently the name lookup in CVT is very slow (linear search) as Mads has indicated and we plan on fixing that by using variant attributes to store the tag data and using its built in binary search.

Share this post


Link to post
Share on other sites

Steen,

This is a very nice set of VIs. Thank you for posting. I'm one of the developers and current owner of the CVT library, so this is of special interest to me.

I really like the ability to dynamically create new registers by simply writing to them, which is a feature we've been planning on adding to CVT for some while. I also like how you handle dynamic and static names in the VI registers using two feedback nodes. I would like to borrow this idea for CVT to eliminate the need for dual dynamic and static name access VIs.

Thanks :rolleyes:. And of course you may grab any ideas in VIRegister you find useful.

One big difference I see is the low level read-write performance. One of our main use cases for CVT is LabVIEW RT on cRIO, which is a much slower processor than most LV programmers are used to from Windows. Doing a very quick comparison between VIRegister and the static access CVT VIs I see a 10x faster access speed for CVT. This is not surprising as when we designed CVT several years ago we benchmarked all the different possible implementations on cRIO and picked the fastest one we could find.

When benchmarking VIRegister reads, have you made sure to have performed at least one write before the read? Reading an empty queue (even with 0 ms timeout) is very slow - on (my arbitrary) Desktop read performance falls from 1,600,000 reads/sec to about 1,000 reads/sec when the queue is empty. This will also be fixed in v1.1 of VIRegister, in which I'll see if I can prime the queue if it's empty (I must avoid the racing condition of a parallel write to the same queue - I have a few ideas how to solve that).

I mainly program LV Real-Time these days actually. I'm the architect behind a couple of large Real-Time applications, for instance this one: http://sine.ni.com/c...c/p/id/cs-12344, and a couple of these (sorry, some is in Danish, and others are without much description yet due to NDAs): http://digital.ni.co...62576C60034F338. I was actually not considering using VIRegister in deterministic code on Real-Time - here I'm contemplating an RT FIFO version of this toolset (RTRegister). Queues are often faster on Real-Time though, but exhibit alot more jitter of course as well as uncanny behaviour on low-memory systems (never deallocating once used memory for instance). If you are careful tuning the RT FIFO you can get slightly better performance than with queues too, while maintaining the very low jitter, but it takes some consideration before it gets perfect (as with everything Real-Time :D).

Currently the name lookup in CVT is very slow (linear search) as Mads has indicated and we plan on fixing that by using variant attributes to store the tag data and using its built in binary search.

Yes, variant attributes are quite fast when doing lookups like this (here's finally a good use case for that feature :yes:). But isn't the (next) biggest drawback the blocking nature of the FGs?

One thing I noticed is that you cannot use one write node to write to multiple registers without risking that the previous registers are lost.

As long as no read is opened on the given register the write is the only place the queue ref is open, therefore the queue is destroyed when the write node runs a release when it switches to another register. The release node does not have the force flag set, but the queue is still destroyed because no other reference to the queue exists (necessarily). If this is the case then I think it would be more useful if you the destruction had to be done explicitly.

The test scenario that made me see this behaviour (which I did not expect, but perhaps it is intentional?) was that I planned to do a double write to 10 000 registers and then a double read of the same registers, and time the 4 operations. The reads turned out not to return an error, but they would return all but one register with default values (i.e. a DBL would return with the value 0 instead of the value I had previously written to it).

Now I only ran through this quickly so I may have overlooked something and gotten it all wrong...However perhaps you can correct me before I come to that conclusion myself :-)

You're absolutely right, and this'll be fixed in v1.1 too. I included that explicit Release Queue to take care of the use case where you'd change the VIRegister name on consecutive calls to the same instance of a VIRegister write or read, since I considered the earlier register no longer used then. But I missed the next-door use case of using a loop to access more than one VIRegister with the same instance of the read or write function, by just cycling through names. using VIRegisters this way yields very sub-par performance (since you'll take the obtain queue hit on each and every call to the write or read function), but In this case the old queues may of course not be released between calls :frusty:.

The explicit release function has been in the todo-list from the start, so that'll probably also make it to v1.1 (I just grew tired of making all these similar instance VIs for the polyVIs :wacko:).

Cheers,

Steen

Edited by Steen Schmidt

Share this post


Link to post
Share on other sites

I just realized that people are looking at these VIs and planning to use them for something entirely different from what I had assumed. What I saw as a useful tool for good I realized could be turned to darkness in the wrong hands.

If you haven't heard my position on by-reference data in LV before, it is this: References sometimes necessary -- rarely, and only in well-defined situations with extreme limitations on their use. I do not claim that references are always bad; I do claim that they are overused by today's LV programmers and generally references create many more problems than they solve. And, no, I don't think users are stupid. I think most references are dangerous in the most experienced hands, mine included. Now back to the topic at hand....

As I said, I have built for my personal use VIs that are fairly similar to these. And mine have a string input for "queue name", just like these do. But when I use my VIs, the name is ALWAYS wired with a constant. ALWAYS. They're full reentrant and store the underlying refnum. If I ever released the VIs, I'd probably add an assert that the name passed on each successive call was exactly the same name as the previous call and return an error if that wasn't true. In fact, if I ever got ambitious enough, I'd probably make "name" something to configure on the node, not an input.

I never really considered that someone would look at these as an API for creating a misbegotten by-reference lookup-table. I always forget that various people keep trying to strip LabVIEW of its major assets by turning it into a mush of procedural, by-reference code. Wired with a constant, VIs like this can become a great addition to LabVIEW. Debug tools can be built to track the start and end points of these "off diagram wires". And there a number of code correctness proofs you can apply. Wired with a non-constant, they introduce code maintenance problems that are very hard to debug.

I know I'm not a full-time G programmer. I do theory more than practice when it comes to G. Those of you who actually have to produce working G code for your jobs are right to be suspicious of my opinions on topics like this. Most of the time, when someone who works G full time tells me they really need XYZ, I pay attention and try to see how LV could provide for that need. But when I read things like this idea -- Globals that can be created during run-time and accessed by name (i.e. native Current-Value-Table/VIRegisters) -- I just can't in good conscience help. That sort of architecture just should not be necessary. It might work. It might even work well sometimes. But my theory is that it will never work as well as building software that solves the problem in a more dataflow-like manner, and when it doesn't work, it will be much harder to figure out why than the comparable dataflow architecture. I really wish that all the time poured into tools to support references over the years could be poured into really nailing down dataflow-like architectures that scale. I know they exist.

Share this post


Link to post
Share on other sites

Hi Stephen.

I just realized that people are looking at these VIs and planning to use them for something entirely different from what I had assumed. What I saw as a useful tool for good I realized could be turned to darkness in the wrong hands.

If you haven't heard my position on by-reference data in LV before, it is this: References sometimes necessary -- rarely, and only in well-defined situations with extreme limitations on their use. I do not claim that references are always bad; I do claim that they are overused by today's LV programmers and generally references create many more problems than they solve. And, no, I don't think users are stupid. I think most references are dangerous in the most experienced hands, mine included. Now back to the topic at hand....

For sure references are harder to get right than by value/dataflow, but references enable us to do much more complete encapsulation and thereby code reuse. And belonging in the hard departement of programming is alot of other very useful stuff like inheritance, reentrancy, events, dynamic dispatching, polymorphism, recursion etc. I must say that I use all these techniques leisurely, and wouldn't want to go back to when they didn't exist (in LabVIEW nor in any other programming language). I also think I understand these techniques well enough to not be afraid of them, on the contrary I constantly find new uses for them.

I'm not sure how VIRegister classifies as a by reference component. What I need is an easy way to transfer signals (not data, since VIRegisters are lossy) between code segments that can't be connected by dataflow, e.g. parallel structures or even (sub)VIs. I most often use queues or events for this type of signalling. Named queues are convenient, since I then can skip the step of fetching an event refnum from some global storage. Since I don't want the restriction of purely hardcoded names, I combined the user supplied queue name with the VI instance name. Nothing new here, just fancy dressing for what NI (and common sense) recommends as signal transfer medium between the structure types I outlined above.

As I said, I have built for my personal use VIs that are fairly similar to these. And mine have a string input for "queue name", just like these do. But when I use my VIs, the name is ALWAYS wired with a constant. ALWAYS. They're full reentrant and store the underlying refnum. If I ever released the VIs, I'd probably add an assert that the name passed on each successive call was exactly the same name as the previous call and return an error if that wasn't true. In fact, if I ever got ambitious enough, I'd probably make "name" something to configure on the node, not an input.

That was my expected use case too, which is the reason for the 'release queue' bug in v1.0 of the VIRegister toolset - I didn't expect anyone to run through a list of register names in a loop, using the same VIRegister instance. That was also why I suggested a couple of posts ago to enter the register name into the node itself, but the official LV IDE does not allow me to create such a node - only stuff that fits within 32x32 pixels with a static icon (with a few exceptions). But why not allow the reuse of the node, with the name as input? It's not any different from the way we create hundreds, or even thousands, of queues or events.

I never really considered that someone would look at these as an API for creating a misbegotten by-reference lookup-table. I always forget that various people keep trying to strip LabVIEW of its major assets by turning it into a mush of procedural, by-reference code. Wired with a constant, VIs like this can become a great addition to LabVIEW. Debug tools can be built to track the start and end points of these "off diagram wires". And there a number of code correctness proofs you can apply. Wired with a non-constant, they introduce code maintenance problems that are very hard to debug.

Once you've tried to debug reentrant VIs on a Real-Time system you stop complaining about things-hard-to-debug ;). No, seriously, sure they are harder to debug. One of the biggest advantages of dataflow programming, and therefore one of the major reasons for the popularity of LabVIEW when people get introduced to it, is how easy it is to follow the data around ("debug" it). It's also the Achille's heel of LabVIEW. I can't keep track of how many times I've been called in to rake experienced programmer's b*lls out of the fire because they've painted themselves into a corner with LabVIEW. "LabVIEW is so easy" they were told by the NI sales guy. BUT, any of the features I mentioned at the top of my post, well basically anything non-dataflow, is hard like that. And again, I don't see how VIRegister differ from ordinary named queues in this regard?

I know I'm not a full-time G programmer. I do theory more than practice when it comes to G. Those of you who actually have to produce working G code for your jobs are right to be suspicious of my opinions on topics like this. Most of the time, when someone who works G full time tells me they really need XYZ, I pay attention and try to see how LV could provide for that need. But when I read things like this idea -- Globals that can be created during run-time and accessed by name (i.e. native Current-Value-Table/VIRegisters) -- I just can't in good conscience help. That sort of architecture just should not be necessary. It might work. It might even work well sometimes. But my theory is that it will never work as well as building software that solves the problem in a more dataflow-like manner, and when it doesn't work, it will be much harder to figure out why than the comparable dataflow architecture. I really wish that all the time poured into tools to support references over the years could be poured into really nailing down dataflow-like architectures that scale. I know they exist.

I don't think you have to put hide your light under a bushel when it comes to G :yes:. I usually tell people that LabVIEW is just syntax. Implementation may differ in the detail, but so does sunlight and shade, or the good idea, from day to day. Inside my head I don't see anything else but when I program in C++ for instance. I just have to do something different with the mouse and keyboard to make an application out of it. I agree that people with less experience sometimes take the sharpest tools and get hurt, but that doesn't mean the sharpest tools shouldn't be available - but maybe they should be licensed in some way? :lol:. So, no, references are king (ever try TestStand?), but that aside I still don't see VIRegister as anything but a mighty fancy wrapper of named queues (did I mention that?).

I'd like NI to put this into LabVIEW, but with the ability to enter the name directly into the node. This just to get rid of the unnecessary input to make it even cleaner, alas you then lose the ability to create the name programmatically (you will probably then be able to set the name with a property node, even though I'd expect such a feature to cost extra because it'd only be available through a module like the DSC-module). I'm not too fond of the blocking-FG limitation of CVT, VIRegister is simply without these limitations; Shared Variables (which I still need to spot a decent use case for) needs the project for configuration and drags the SVE around, Locals are tied to the FP, Globals are tied to a file... I hate that. Well, VIRegister is tied to the queue name lookup table, but at least that's transparent to the programmer, and lives in the same code space as your VI, and it has worked fine over the years.

But of course, in the spirit of the main topic of this year's CLA Summit, we shouldn't headlessly create new communication APIs. So, implement VIRegister and TCPIP-Link, keep queues, events and locals - ditch the rest :D (notifiers can stay too, but they are more like the evil cousin to VIRegister anyway).

Cheers,

Steen

Edited by Steen Schmidt
  • Like 1

Share this post


Link to post
Share on other sites

I think Steen sums it all up pretty nicely.

My own two cents: If you cannot lookup globals programmatically by their name (basically a simple internal database) then the only value added by such a library seems to be that you can add some flow control to the global (error terminals e.g.) and can hard-code things part textually by typing the name instead of using your mouse. The latter two are both nice features which should be part of the standard globals in LV, but they do not address the need for scalability.

Share this post


Link to post
Share on other sites

I think Steen sums it all up pretty nicely.

My own two cents: If you cannot lookup globals programmatically by their name (basically a simple internal database) then the only value added by such a library seems to be that you can add some flow control to the global (error terminals e.g.) and can hard-code things part textually by typing the name instead of using your mouse. The latter two are both nice features which should be part of the standard globals in LV, but they do not address the need for scalability.

"Looking up globals programmatically" is itself very high on my list of architectures that do not scale. I want to better understand the original problem was that made this feature desirable, because I cannot believe this is a good solution. At best, it is like treating heroin with methadone -- effective, but fraught with its own problems.

So tell me... why do you need this? Can you go into details about some concrete project that drove you toward such a solution? If you can, I think posting that as a new topic on LAVA would be appropriate, and then I'd like to discuss the architecture in this public forum.

Share this post


Link to post
Share on other sites

"Looking up globals programmatically" is itself very high on my list of architectures that do not scale. I want to better understand the original problem was that made this feature desirable, because I cannot believe this is a good solution. At best, it is like treating heroin with methadone -- effective, but fraught with its own problems.

So tell me... why do you need this? Can you go into details about some concrete project that drove you toward such a solution? If you can, I think posting that as a new topic on LAVA would be appropriate, and then I'd like to discuss the architecture in this public forum.

Describing architectures can be quite a task. I'm sure the designers behind the CVT e.g. could mention some intended use cases for you, but OK. Let me first give you an overview, and perhaps I can describe something more concrete later if necessary:

I typically make different types of monitoring systems that consists of a server that runs 24/7 on an oil rig somewhere and interfaces with the local control system - and remote clients that connect to that server to monitor the situation or do analysis and reports. The server (on a PC or PAC) is configured to poll data from a number of sensors through different types of communication interfaces, it does a number of calculations on the data (new calculations can be added during run-time), logs the trends and system alarms/events - and offers remote configuration, current values, and historic trends to a client counterpart - or via web pages through a web service. A bit like a SCADA/DCS, but more specialised.

Most of the applications came in their first version in the late 90's. Back then a main part of the server was its modbus interface to the main control system. All inputs and outputs of the system would have a place in the modbus registers that were read or written to by one or multiple control systems. The existence of such a central and globally addressable memory space made it easy to use it to share data not just with external units, but internally as well. However this would mean that everyone needed to know the modbus address of the data they needed - or be able to use a middle-man that would know that address if given a name e.g...Using modbus registers for something like this is not exactly ideal as there are data type restrictions, scaling is involved etc. but these are obstacles we could live with. I've since then moved away from using the modbus register this way as I do not want to couple things like that, but the idea of having a centrally accessible register of data lives on...

If a new calculation or device is coming online it typically needs access to data which may come from any part of the system. If all parts of the system had an interface on its own which allowed others to poll data from it when needed (and ideally this would be a generic interface so that it would be supported by all others), then that might be a way to go, but they do not (some could be redesigned, but it is not very practical to impose such a requirement on all components) - and even so the consumer should not need to know anything about who produced the measurement, just the tag (and data type) of the measurement. The only thing that can really be made sure is that the current data will be available in memory in a "database", and historic values will be available from disk in a log-file/database.

So let's say e.g. that the customers asks us to use data from sensor x, two values coming in via OPC, a value from the modbus link to system Y...and produce a calculated value from those variables that is accessible by all other parts of the system. We do this by providing a middle-man which can take in a list of tags and outputs the data we need, and the calculator writes back the results to the same middle-man by providing a tag and the value. This way the calculator (or other consumer/producer) only needs to be configured with a set of tags and a formula.

If only current values are required (most of the time this is the case), the middle-man is an equivalent to the Current-Value-Table, which is what I'm proposing that NI develops to be as efficient as it can be because implementing it with the tools we have available now results in a number of performance issues (as discussed here related to the VIRegister and on the CVT site).

I've also described this as a type of global that you can create when needed and access by name, but that description might confuse some.

Edited by Mads

Share this post


Link to post
Share on other sites

Almost there with v1.1 (only need to update the documentation):

post-15239-0-97976200-1309986779_thumb.p

And now I'm sick and tired of polymorphic VIs. I must've edited a thousand almost identical VIs the last two weeks :wacko:.

Cheers,

Steen

Share this post


Link to post
Share on other sites

Join the conversation

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

Guest
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.