Jump to content

Do you use LV2-style globals? Try LV2OO-style globals!


Recommended Posts

If you have LV8.2, I offer a new twist on an old idea. You've heard of LV2-style globals? If not, go search LV's online help for "functional global variables." They're very useful and dataflow safe. For everyone else:

I've put together what I'm calling a LV2OO-style global*. I'm not convinced that this implementation is ideal, but I want to get some conversation going about the possibilities. Basically, I suggest that classes make it possible to build a single LV2 functional global that doesn't need to be rewritten for every use, among other advantages.

:!: Download LV2OO Prototype

If you download and unzip this, open the project file and then open the top VI in the project (the only one not in a folder). Comments explaining what's going on are on the FP and BD. Many of the ideas discussed in this LAVA thread about references could apply to the LV2OO-style global. I'm interested to see what comes out of the brainstorm.

This is just a prototype, so there may be rough edges.

* LV2OO : As in "LV2 Object-Oriented".

Link to comment

This is very interesting indeed. I have downloaded the code and looked at it. It took mee some minutes to grasp what was going on :D but i *think* i'm starting to understand it. My first impressions are these:

LV2 style globals are the only way to efficiently store data in LV. Queues can be used, they are very efficient (for small and moderately sized data), they also are inherently syncronous and to make them asynchronous requires some work and the efficiency drops rapidly. However, the main advantage with LV2 style globals (also compared with all of the GOOPs) is that they are the only structure in which large complicated data can be operated on efficiently in an interacting fashion. For instance, you can have several large arrays, and arrays of clusters, and want to operate on them per element basis without unnessesary buffer allocations and copies - LV2 globals are the only programming style that can be used. In addition, you want to read some *some* of the data in the globals at varying frequency in other locations for storing to file, plotting in graphs, sending over internet etc, then reentrant LV2 style globals called by ref is unbeatable (i usually use queues to send the references at init to werever they are needed). The price for all this, is that making changes to a large and complicated global is a difficult and very error prone task, where it is very easy to add bugs allso to code that previously worked well. To simplify this will be a huge step forward.

I'm a bit concerned about efficiency of the LV2OO. I will try to have something up and going an compare it with a normal LV2 global (and dqGOOP :) )

Another thing is that LV2 globals are very straight forward, and therefore easy to use and understand. The LV2OO looked esoteric in comparison, but maybe i just need to get used to dynamic dispatching and stuff and it becomes less esoteric.

:thumbup:

Link to comment
If you have LV8.2, I offer a new twist on an old idea. You've heard of LV2-style globals? If not, go search LV's online help for "functional global variables." They're very useful and dataflow safe. For everyone else:

I've put together what I'm calling a LV2OO-style global*. I'm not convinced that this implementation is ideal, but I want to get some conversation going about the possibilities. Basically, I suggest that classes make it possible to build a single LV2 functional global that doesn't need to be rewritten for every use, among other advantages.

I like the Idea that the class is the way to select the function. A little more complicated and obscure than I would like but I get it.

The override SubVI implementation in LVOOP is starting to scare me though :o

My assumptions were that static wires were only about that class and you could only operate on it's data members.

It seems like the override vis are kind of breaking encapsulation.

The override SubVI is able to operate on child data even though every thing on the block diagram shows a parent wire and parent SubVI.

post-584-1156565216.png?width=400

post-584-1156565345.png?width=400

I am seeing this as possibly a huge debugging problem.

If there is a problem where a bad value gets written in a child class member do to an override vi how do find it since it happened in a vi I can't see or track down.

There could be potentially hundreds of overide "Do it" SubVIs that I can't see or access on the block diagram.

Unless I have the project open there is no way to know if the "Do it" SubVI is being overridden and no way to fix it if I did know.

For classes it seems like can

Link to comment

Well, after some more thougts i think that when using LV2 style globals and LCOD programming style the reality is that the LV2 global is not just a storage buffer, the LV2 global(s) *is/are* the program. The reason this works is because you can have an abundance of shift registers for all the different data and operate on them in any thinkable manner according to the LCOD function. Adding and deleting data is just a matter of adding and deleting shift registers.

With the LV2OO you have to cramp all these data from shift registers into one VI. This will cause at least two problems. Adding data (for instance adding one more array and a few bools that you forgot to start with) becomes a serious task when you have to change maybe several tens of sub classes and their VIs. Another problem is that there just is not enough slots in the connector pane for input and output of data.

Besides, when thinking of the LV2 global in terms of LCOD style, since the LV2 global is the program, changing that LV2 global WILL be a serious and error prone task no matter how you look at it. But i dont think it will be more complicated than changing the code in a LV2 global than changing the same code in a child class. It will probably? be more work in LV2OO because you potentially also have to change a multitude of connector panes?.

But i have to try this LV2OO first because it looks very interesting :) . However, i have a feeling that LCOD is such a strong programming style on it's own (completely unbeatable compared with any GOOP when you have lots of data to manage and send around), that a truly strong concept would be to put LVOOP into the frames of LCOD, and not the other way around.

Link to comment

OK, I have converted my Pointer/ref system to LV2OO. I have not tested it performance-vise because i'm not sure if i have done everything correct yet. My experiences are these:

It was a somewhat of a pain to make all the classes and all the get/set functions needed for each class, especially when they all are virtually identical. I mean OOP, isn't that about code reuse? It was also a bit painful to stretch my head to think in "LVOOP way" about these actions instead of a simple case statement. Isn't this kind of programming actually called actor oriented (AO) and is more of a functional programming thing, or am i totally lost here??

When it all was finished i noticed a severe limitation (bug?) of the Project Explorer/inheretence functionality. Every inhereted function(vi) need to have the same name, but since they cannot have the same name in the same folder, i had to make separate folders for each and every inhereted vi. When there is no direct relation between the folders in the Project explorer and the real folders, this is *really* confusing and extremely irritating.

When that was fixed it worked at first try :D when looking at the code, and the ease of which things now can be changed, add functionality etc, i have to admit that LV2OO is a major leap in the right direction even though the initial amount of code needed at least doubles for this simple pointer class. The downside is that because the code is "granulated" down to its smallest units, it IS more difficult to get an overall picture of what the code actually does (at least it takes more time), but at the same time this granulation makes it much easier to test the code, and therefore make it work at first try.

All in all, i'm impressed so far. I still don't know how LV2OO scale up to be used for general LCOD style, and i am still a bit concerned about performance, but for normal LV2Globals it certainly is a very radical improvement :thumbup: :thumbup: . I have included the code.

Download File:post-4885-1156686272.zip

Link to comment

bsvingen:

Please keep a log of where your biggest time sinks are. The team is already aware of the gigantic sucking pit of time needed to edit icons, even if you're just doing text, and we have a way to fix some of that. Further, we know about the need for automatic (scripting) creation of get/set methods for member fields.

C++ has more syntax when classes get involved than plain C. It seems like most languages have this overhead compared to their non-class counterparts. With the graphical environment, we need to log what the biggest sinks are and try to provide the needed shortcuts.

Glad to hear that the LV2OO idea is working out for you. What I posted is a first attempt -- I've had the implementation for a long time, but I didn't have time to really clean it up or consider ways that it might be augmented.

Link to comment
Please keep a log of where your biggest time sinks are.

Debugging classes is #1 on my list. It is impossible to track down an error inside a class, since a broken member causes every VI in the class to be broken.

broken member VI ==> broken class ==> every member of the class is broken

I've talked to others who have spent enormous ammounts of time trying to track down trivial bugs in classes.

Link to comment
Debugging classes is #1 on my list. It is impossible to track down an error inside a class, since a broken member causes every VI in the class to be broken.

broken member VI ==> broken class ==> every member of the class is broken

I've talked to others who have spent enormous ammounts of time trying to track down trivial bugs in classes.

I will have to second that.

I got VERY frustrated trying to debug simple LV class.

A wire connected to the wrong input and the whole class is broken and the error windows return a huge amount of errors completely irrelevant.

In some instance I could not even figure out what was going on and I had to recreate the class from scratch (what a pain).

To make thing ever worse try to put a class in a lvlib... Enjoy the nightmare.

PJM

Link to comment
I will have to second that.

I got VERY frustrated trying to debug simple LV class.

A wire connected to the wrong input and the whole class is broken and the error windows return a huge amount of errors completely irrelevant.

In some instance I could not even figure out what was going on and I had to recreate the class from scratch (what a pain).

To make thing ever worse try to put a class in a lvlib... Enjoy the nightmare.

PJM

Take a close look at the Error List Window. The items at the top (scroll to top) have a Red X next to them. These are where the real breaks are. Everything else is broken because of one of these.

This is a fix that applies even to subVIs in deep hierarchies. This UI feedback should help with a lot of the "drill down the hierarchy" type problems.

Link to comment
Take a close look at the Error List Window. The items at the top (scroll to top) have a Red X next to them. These are where the real breaks are. Everything else is broken because of one of these.

This is a fix that applies even to subVIs in deep hierarchies. This UI feedback should help with a lot of the "drill down the hierarchy" type problems.

I know about the red X, but why does VIs in the class that have no dependency whatsoever (whith the broken VI) are broken?. Basically, every member in the class is broken (and if you follow the error path [from VI to VI] you end up in a recursive list of errors). Why?

Also, I end up in situation with several VIs with a red X where I was not able to figure out what was wrong.

In my opinion, the class debugging is so poor that this greatly limit LabVIEW OOP usability.

PJM

Link to comment

In order for any VI in Class X to run,

A) every dynamic dispatch VI in the Class X must be runnable.

...because we have to build the dynamic dispatch table of compiled VI pointers in order to have runnable data on the wires. Trying to only reserve certain positions in the dispatch table turned into a nasty mess that wasn't really maintainable. When you hit the run button, we were traipsing through call hiearchies, including all possible dynamic dispatches, searching for any call that might invoke a given subVI and then figuring out which position in the dynamic dispatch table that subVI occupied and thus all classes needed to be able to reserve that position.

B) X's parent class has to be runnable.

...because the child is a parent and depends upon the functionality of the parent. By induction, this implies all ancestors of X must be runable.

C) all classes that are members of Class X must be runnable

...because those classes are part of the data of X.

D) all classes that are children of Class X must be runnable

...because controls of Class X can contain child data and there's no way to detect when we hit the run button whether or not such data exists.

E) we have to lock all members of Class X (even the static ones) so that you cannot edit them because if you were to edit a static VI to be dynamic while the classes were running, this would change the indexing of the dynamic dispatch table.

This is a burden for LV that I'd like to decrease, but I would like to point out that in most languages the entire hierarchy has to be runnable for anything to work. We were able to maintain the individual runnability of VIs, but everything has to be *able* to run.

Also, I end up in situation with several VIs with a red X where I was not able to figure out what was wrong.

I'm very concerned about these cases. A lot of stuff breaks when one VI breaks, but that one VI should be readily findable with the red X and I'd hoped that the errors there were sufficiently explicit. Was the error list window not descriptive? What were these errors?

I know about the red X, but why does VIs in the class that have no dependency whatsoever (whith the broken VI) are broken?. Basically, every member in the class is broken (and if you follow the error path [from VI to VI] you end up in a recursive list of errors). Why?

See above for why so much breaks. As for the cycles, the dependencies list is cyclic and its hard to get descriptive error messages propagated so that everything is reporting the real reason for its breaking, especially when there are multiple things really broken. Again, that's why the red X modification was so important to get in this version.

In my opinion, the class debugging is so poor that this greatly limit LabVIEW OOP usability.

There are certainly improvements that can be made in the future.

Link to comment
I'm very concerned about these cases. A lot of stuff breaks when one VI breaks, but that one VI should be readily findable with the red X and I'd hoped that the errors there were sufficiently explicit. Was the error list window not descriptive? What were these errors?

Unfortunately I did not keep the code since I had to rewrite everything. But as best as I can remember it happen when, out of frustration, I try to copy 2 VIs from a class residing in a lvlib into another lvlib (note: I was working on a copy of the lvlib with the same class data). After reopening the new lvlib and remarking the copy VIs as belonging to the lvlib, I got 3 VIs with red x (the original one that was broken before + the 2 I add to the lvlib). I think I fix one of the red x, but eventually I could not fix the other 2 red x.

In order for any VI in Class X to run,

A) every dynamic dispatch VI in the Class X must be runnable.

...because we have to build the dynamic dispatch table of compiled VI pointers in order to have runnable data on the wires. Trying to only reserve certain positions in the dispatch table turned into a nasty mess that wasn't really maintainable. When you hit the run button, we were traipsing through call hiearchies, including all possible dynamic dispatches, searching for any call that might invoke a given subVI and then figuring out which position in the dynamic dispatch table that subVI occupied and thus all classes needed to be able to reserve that position.

B) X's parent class has to be runnable.

...because the child is a parent and depends upon the functionality of the parent. By induction, this implies all ancestors of X must be runable.

C) all classes that are members of Class X must be runnable

...because those classes are part of the data of X.

D) all classes that are children of Class X must be runnable

...because controls of Class X can contain child data and there's no way to detect when we hit the run button whether or not such data exists.

E) we have to lock all members of Class X (even the static ones) so that you cannot edit them because if you were to edit a static VI to be dynamic while the classes were running, this would change the indexing of the dynamic dispatch table.

This is a burden for LV that I'd like to decrease, but I would like to point out that in most languages the entire hierarchy has to be runnable for anything to work. We were able to maintain the individual runnability of VIs, but everything has to be *able* to run.

See above for why so much breaks. As for the cycles, the dependencies list is cyclic and its hard to get descriptive error messages propagated so that everything is reporting the real reason for its breaking, especially when there are multiple things really broken. Again, that's why the red X modification was so important to get in this version.

There are certainly improvements that can be made in the future.

From your detailed answer (thank you), I gather that in all instance (while debugging classes) the only errors to pay attention to are the one marked with a red x and everything else is basically broken because these red x errors; if this is correct, then why do I need to see all the other errors? this just add to the noise and does not make my job any easier.

PJM

Link to comment
From your detailed answer (thank you), I gather that in all instance (while debugging classes) the only errors to pay attention to are the one marked with a red x and everything else is basically broken because these red x errors; if this is correct, then why do I need to see all the other errors? this just add to the noise and does not make my job any easier.

I made the same argument. I was forced to conceed the necessity of showing all the broken VIs, though I keep hoping for a better idea to come along.

Basically, when a VI is broken, you can click on the broken run arrow and see a reason why the VI is broken. Now, if the VI is broken because of a subVI, my thought was, "Well, then take me to the broken subVI... why am I messing around here?" But although sometimes you might want to fix the subVI, it was pointed out that sometimes you you may want to know which subVI node is the problem so you can delete that subVI call.

With LV classes, I think 90% of the time you're in the situation of "please, just take me to the subVI." That's because so many barely related VIs break in response to a single broken VI. With LV in general, I'm not sure what percent of the time you want to fix the subVI vs change the call to that subVI. But since few developers are LabVOOP developers at this time, we stay with the current convention of scrolling the Error List Window to whatever subVI you clicked the broken run arrow on... you can scroll up to the Red Xs. I did at least get those sorted to the top of the list.

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