Jump to content

MVC through class control - feedback required


Recommended Posts

Hi,

while redesigning a large scale app from an old non LVOOP code I thought about the next design for a quick upgrade that will include Model-View-Control architecture with an alternative to FGs.

Besides using frameworks like Hardware Abstraction Layer (HAL/DSSP) and the UI Framework along patterns like Factory and Singleton to allow abstraction and feature changes to my system I wanted to add the same abstraction to my inner modules so that the vi loaded into the UI Framework, for example, will be MVC and be used as a father for future versions without using external FG that can't be LVOOP.

The solution that I thought of and I need feedback for (since I never heard of anyone using it) was using the class control as the db (model) for the vis while the front panel is the GUI (view) and the block diagrams are the logic.

I'll try and explain through an example:

The old code used controls on the front panel as the state of the system while the block diagram used the front panel both as a db of states (some hidden from the user) and as a GUI (which is actually also a DB most of the time).

I created a vi that went over all the controls of the front panel (recursively looked inside each control) and created a typedef that contains all the controls names and refs. I then added this typedef into the class control and added to that class control all the front panel controls.

The next step was to automatically add member access read and write to all the elements in my control.

The result was about 90 vis of member access with the same names as the name of the front panel controls (LV currently can't handle property nodes for controls with label that contains two lines so I had to fix the names first).

By changing the template of the member access I was able to add to all the write member access vis the ability to update the front panel and even to signal value change or add request to a queue depending on any rule that I decide on while using the typedef that I previously described.

Now, all I had to do is to take the old code and remove all the references mess in it and replace it with a class wire that is actually my db. Any action on the db/class is handled through a property node (I like to set the option to no name to save space).

The result is a MVC since the logic doesn't interact directly with the GUI and update it only on request. Each user action triggers an event so the logic will update the model in the class control.

In this way I don't need FG since my class is the db and each member access implementation can contain protections like Singleton.

Many tasks become trivial this way like saving/loading the status of the system for each user or even documenting/logging an error and even recording operations to use later as a test vector with metrics above it not to mention the fact that using G# for example I can expand my class to implement another API/db.

I could go on and write the logic using the class wire, however, I might want to use the db in multiple loops and thus I converted the class wire into a DVR wire.

Besides that, if I would like one day to upgrade the system I simply have to inherit from this class, the child will have access to the same db and could override the member access vis.

The problem I have now is that I'm not protected from race conditions on my db and I wonder if the class control is fast enough and what will be the implications. For example, if I access the class db from a parallel loop or from a reentrant sub vi or if I pass the class dvr as a parameter for another class control or both a child wire and a parent wire try to access the same data etc...

The regular approach from the LVOOP examples (cars/bike/truck) along with FG, Factory and so on is also ok but I find it both less robust and harder to use for a system upgrade since the time it will take you to redesign an old non LVOOP program, so it will contain all the new standards, is unacceptable (just defining the classes and the inheritance takes months) while in my way most of the work is automatic, I simply recycle old code to be more expandable and user friendly.

To sum it all, I love the idea yet I'm afraid to use something that was never tested before and thus, I need feedback from you guys.

P.S. - thank for reading

Edited by 0_o
Link to post

If this wasn't long enough then I wanted to add that I pass the class between parallel loops as a DVR which is controlled as a singleton through ESF.

Thus, each section of the logic that needs access to the db will ask for permission and lock the db till it is finished doing what it wanted to do.

LV is great with classes inside a DVR, you can use properties on it just as if it was a regular class.

  • Like 1
Link to post

Finally!!! A reply!!! :book: Thanks!!! :thumbup1:

P.S. - on the way I found several LV bugs like, for example:

1. Property node of a control with label that has two lines - impossible.

2. Property nodes of arrays might cause memory crash because of access violations.

3. Changing the name of a class in an auto populating folder is a bad idea.

4. vi in a sub panel has less permeations in the vi server

5. Build reference -> property node (name) is read only and won't update once I change the build reference's tag that contains the name thus creating a build with one name in the project explorer and a different name in the build description wizard.

I’m still waiting for an input about the entire design.

Thanks in advance,

DD.

Link to post

Finally!!! A reply!!! :book: Thanks!!! :thumbup1:

Your initial post is a bit of a scattergun blast of concepts, patterns and acronyms, which, though all somewhat familiar to me, are not directly translatable into specific LabVIEW code in my head. And you didn't include any pictures! That, combined with the holidays, is why your not getting any responses.

Anyway, I'll give it a go.

The image I'm getting is of a past LabVIEW application written with the Front-Panel controls/indicators serving as the data-space ("state", "model") of the system. Perhaps with lots of local variables and Value property nodes. Your trying to partially automate the conversion of the data-space into a single "Model" object that maps onto the existing controls/indicators (there being dozens and dozens of such).

Personally, this is not how I would approach such a old program. I would instead look at how to upgrade the program part-by-part, bottom up, looking for natural encapsulation. For example, if the application uses a Camera, say, I would try and replace all the variables related to the camera with a single "Camera" class. I would try to get as much of the logic related to the camera in method VIs of the Camera class, and try to limit the number of actual "accessors" to the internal Camera data. When the "Camera" upgrade is working, I would look for some other subsystem that can be encapsulated in a class. This should slowly, step-by-step, lead to a simplification of the top-level program logic until the point that I could consider a rewrite of the application as a whole. This might contain a "Model" object, but it would itself be made up of a small number of component objects like "Camera", rather than be a huge sprawling "everything from the old program including the kitchen sink".

-- James

Link to post

Hey James,

I agree, in an ideal world I would do just that.

However, in order to decide what is a separate class, like a camera, inside an old project with >1000 vis I'll need a year of designing. It will be unrealistic since the time it takes both to create a new design and to go down and understand why the old code was implemented the way it was is not something most companies will consciously do.

Just trying to separate one class out of a non OO design without a proper design of the entire architecture is a door open for bugs and endless redesign.

Thanks for taking the time! You are right that without an example my post is very hard to understand.

I need to know if my way is simply possible or will I have trouble accessing the class control fast enough almost simultaneously.

I'll try and upload a simplified example till Monday.

DD.

Edited by 0_o
Link to post

However, in order to decide what is a separate class, like a camera, inside an old project with >1000 vis I'll need a year of designing. It will be unrealistic since the time it takes both to create a new design and to go down and understand why the old code was implemented the way it was is not something most companies will consciously do.

Just trying to separate one class out of a non OO design without a proper design of the entire architecture is a door open for bugs and endless redesign.

Is the old code really that badly designed? Any reasonable code should have some level of separation between components; OO Classes just allow that separation to be more complete and clear. I'm just suggesting what is mostly a cut-and-paste job: identify the variables related to the camera, drag-copy them into a "Camera" class control; find a bit of code that initializes the camera, cut and paste it into an "Init" method. Don't redesign the details, just get the applications components cleanly separated, so that in future you can do things like use a different type of camera, or test the camera separately, or improve the camera code without introducing bugs in unrelated components.

To use some jargon, what I'm talking about is "Abstraction Layers".

-- James

  • Like 1
Link to post

Hey James,

Not only that the code looks like a sample from the post about spaghetti code but it is also not just a case of cut and paste because of all the references, for example.

Most companies won't give you a complete list of requirements up front. They ask for one thing and keep changing/expanding their demands under a very short time schedule, resulting in a code that looks patched and glued all over with many versions for the same operations since you don't even remember that you once wrote a kind of patch that you just now need to write again.

The separation level of the old code is basically a separation between the highest level of tools in the product with all the resources being handled and shared from the main ugh product that has similar a bit changed and smaller versions of itself.

Anyhow, my question is what's wrong with the design I suggested?

Can I use the class control as a DB and just update the front panel as a GUI while the block diagram is the Logic?

This way each class has its own db, logic, gui separated and it can be inherited as if a db was an API as long as the FP labels are locked.

The final code looks like several parallel loops with a single db wire running in the middle of them all supplying whatever information the logic needs.

I know that the traditional way is different but I don't really understand the benefit of using it and I haven't heard of someone using my way, thus, if there is no LV architect here that can help, I'll have to find it's downside on my own.

The main problem that bugs me is: will the class be fast enough as a db (what happens if the class is big? will it be copied to sub vis even if I use DVR? Is the access to it fast as the access to a tdms/binary file or slow and serial like a text file? Will it remember past values and eventually make the app bigger? What should I watch out of while using it: rename/typedef...) and will the esf protect me from race conditions without slowing my code or even freeze it completely by pausing a code that is needed for the code with access to the db to finish for example or even some hidden issues with reentrant vis or something more devious.

Thanks.

Link to post

Uploading a few pictures would be helpful, such as a a screenshot of the old code and of one of your new accessors (complete with property-node update of the front panel). Otherwise I only have a vague impression of what your dealing with, though the technical term for your old code is "Big Ball of Mud". I worry that in attempting to tame the BBoM, your in danger of just adding a new layer of mud. On the other hand, you might successfully end up in a situation where new code can have much improved architecture, interfacing to the BBoM only through your "db" object.

Some thoughts:

1) the biggest speed issue is your property nodes, which are inherently slow. Don't put property nodes inside your In-Place-Element structures as other IPEs will be blocked while the property node is executing.

2) (1) is not a huge issue, as once you successfully weave "db" though the BBoM you can immediately go further is separating the UI from the logic by eliminating the direct update of FP controls from db's methods, and instead have a separate "UI loop" that periodically queries db and updates the UI. For example, if part of the code updates a state variable 1000 times a second, that would cause 1000 property-node updates/sec (which is a problem), but the UI loop could query db and update the control terminal directly 5 times per second.

3) at step (2), you can take the time to modernize your UI, since it is no longer tied one-to-one with program state variables. You could use all sorts of clever ways to present information. This could be a major improvement that you can show to your boss as payoff for your code upgrade.

4) You might have a speed issue with the DVR access (as only one IPE structure can act at any one time). I believe DVRs are very, very fast, but you are going to use it a very, very large number of times a second. And every access locks up every state variable. You could consider an alternate structure for "db": instead of a DVR of "db" that holds all the state variables, have db hold a set of DVRs of clusters of related state variables (i.e., all the "Camera" variables would be in one DVR). Then, any method of db only has to lock up the part of the state data that it is dealing with, and unrelated methods can operate in parallel. Even better would be if you could separate db into several objects corresponding to subsystems, but as you say the BBoM may not allow that.

-- James

will it be copied to sub vis even if I use DVR?

No, only the 32/64-bit DVR reference will ever be copied.

Is the access to it fast as the access to a tdms/binary file or slow and serial like a text file?

Way faster than either; it's a memory access, faster than any file access.

Will it remember past values and eventually make the app bigger?

???

What should I watch out of while using it: rename/typedef...) and will the esf protect me from race conditions without slowing my code or even freeze it completely by pausing a code that is needed for the code with access to the db to finish for example or even some hidden issues with reentrant vis or something more devious.

What's an "esf"? The issue with the DVR is that only one thing can access it at once, so you can't do anything slow inside an IPE structure without blocking other code. That requirement can conflict with the need to prevent race conditions by doing things inside the IPE. An issue you have is that you are putting the entire program's state variables in one DVR, so unrelated parts of the code will block each other without reason.

Edited by drjdpowell
Link to post

Thanks for the reply James!

The DVR+IPE blocking access to the DB along the use of vis to access the DB will indeed slow my app down.

However, any OOP program is based on using vis to access the data in order to allow inheritance.

As for locking the DB, when working with references a race condition over the DB is a real problem and I wanted to add to the DVR+IPE the ESF framework which is a singleton based on notifiers:

https://decibel.ni.c.../docs/DOC-12813

This is why Java with it's garbage collection is slower then a c code with the upside of quick application development.

Anyhow, as I promissed, I attached a sample of my design.

I ran into a few problems with it:

1. a member access vi with property node can't contain sub vis or other property nodes of that same class. DAMN!!!

2. I need to inherit the FP of the father, do some changes, integrate the changes into the member access vis. Such a change worked perfect on the father since I used bundle/unbundle in the member access vis, yet, since the child doesn't have access to that data, again, DAMN!!!

I can do some walk arounds like saving the needed data from the parent cluster localy after the initialization but it will break the inheritance and I'll need to manually change code.

Ideas anyone?

P.S. - extract and run the project -> run the parent main.vi -> run the test in pause mode on child 2 (works fine since I only changed a function and not the FP) -> switch to child 2 and see the error from the property node within the property node.

P.S.2 - there are some extra vis that list all the controls and another 2 that list all the wires. the example uses the first but not the wire listing vis. My intention is to add the ability to move labels from wires which are split from a simple tunnel, for example, in order to make it easier to upgrade BBoM.

P.S.3 - many structures in the parent in particular have description, open the description to understand the logic behind the design.

Thanks in advance,

DD.

MVC.zip

Link to post

My fault, I should have tried 7z instead of whatever Windows XP uses as default.

Had a quick look. Only useful comment yet is that all your property VIs are non-reentrant. Using "reentrant, share clones” setting on your property VIs may solve your “recursive call” problem. I switched Child1 Write Power to reentrant, share clones, and ran its “Main” UI without error.

— James

Link to post

Another suggestion:

Your current accessors use some repeated code that should be in subVIs. Also, you want to make all these VIs as fast as possible. Consider using a method like this:

post-18176-0-92933500-1326128618_thumb.p

This looks up the control reference by name without making a copy of (potentially quite large) arrays. Your current method of reading the arrays by property node makes a copy. Also, having the code in a subVI will allow you to eventually upgrade from a linear search to a binary search method that would be much faster for large arrays (a good reference if you want to do this).

Oh, and make methods like this static dispatch, rather than dynamic, if they don’t need to be overridden (less overhead and you can consider making them “inline”).

— James

Link to post

Wow!!! So cool that it worked for you with no error!!!

I thought I tried making them reentrant+share and it didn't help me.

I'm probably doing something wrong since I tried again doing exactly what you said and still got the same error:

post-18598-0-12366600-1326140049_thumb.j

post-18598-0-24515600-1326140077.jpg

What am I missing? I knew this should have been the solution and I got angry at my LV when it didn't work for me the first time.

Moreover, I can't find the inline option in my execution menu as you can see in the first picture.

As for code optimization, I stated out with a sub vi in all the property nodes yet LV gave it the same error as in the second picture saying that this sub vi was called recursively.

Making that vi reetrant+share didn't help and since I didn't find the inline option I had to copy it's code into each of the property vis.

I was familiar with the in place optimization but the variant red/black tree search was new to me.

I always thought the day will come when I'll implement all the classic data structures in LV.

There are more optimizations and features in my real code; the issue that I'm trying to solve here is the basic OOP requirements.

If I'll succeed in reproducing your error free execution of child 1: main I'm almost at my goal.

Please help :)

I’m hoping that the issue is me and not LV2011 ver. LV2010.

Yours truly,

DD.

Link to post

Strangely, I couldn’t get it to work again with reentrant settings. But I did get it to work if I changed the Read FP Controls and Read Signaling Property VIs from Dynamic dispatch to Static. But I don’t understand why that would be. I’m afraid I mostly use LabVIEW 8.6 and have minimal experience with DVRs and Class Property nodes. So it’s just trial and error. Try making you accessors static dispatch.

— James

Link to post

I guess you ran the parent main by mistake or else LV is crazier than I though.

Frustrating :(

I can't have them static dispatch since the whole point is allowing the child to override the parent's implementation.

I remembered I tried making them reentrant already to no avail...

There are some guys at NI, like ElijaK, that I bet can explain all the quirks. Yet, the fact that only the most basic behavior is documented, is wasting a lot of my time.

Why this error simply closes the app instead of propagating an error or breaking the arrow?

If a property node is just an easy to use template vi why can't I use another subvi in it?

Thanks for the help James!

I wrote a nice long example for an interesting case that once finalized could help many people create an easy to use Model-View-Controller that even replaces the use of globals and AEs.

You can find there code for recursively going over all the controls and wires of a vi nevermind how deep they are and I can add many other tricks that improve the code, but it needs to implement OO first.

Please help :)

1. Is there a reason why my property node doesn't work?

2. Will writing my own accessor vis as a regular dynamic dispatch vi help?

3. What is the right way to make the FP changable so I can load a vi with the same FP controls but that looks differently and even add/remove controls to it in a chils.

4. Can the typedef control that holds the commands be overriden so that a child will have more commands for it's new controls while not having to change much in main.vi?

For now, I'm going to send NI a question about the property node limitations and try converting to G#.

Good night.

Link to post

Thank you! Thank you! Thank you!

If I didn't know that child 1 worked one time for you and that it gave you the same error it gives me the second time you ran it I wouldn't know what to tell NI support when it seemed to be working for them too :throwpc:

It appears after I close LV something is changed that causes the child1:main.vi, for example, to stop working.

My guess is that there is a vi called property node that calls the vi member access which is not reentrant or has some problem working with hirarchies since it is not dynamic dispatch.

What ever it is, it seems my problem is an LV bug or else how would you call a program that sometime works and sometimes doesn't.

If they manage to fix it and it is not a problem with my design then I think I have a great design that can upgrade any non LVOOP program to be MVC with all the OOP benefits easily.

I'm just afraid of the common reply: we opened an internal CAR and we think it will be solved around LV2022 LOL.

Link to post

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.