06-27-2012 01:22 PM
I've been using the OOP features of LabVIEW for various projects lately and one thing that I struggle with is a clean method to save and recall objects.
Most of my design schemes have consisted of a commanding objects which holds a collection of worker objects. Its a pretty simple model, but seems to work for some design problems. The commander and my interface talk to each other and the commander sends orders to his minions in order to get things done. For example, one parrent class might be called "Data Device Collection" and it has a property that is an array of "Data Device" objects.
The Data Device object is a parent class and its children consist of various data devices such as "DAQmx Device", "O-Scope Device", "RS-232 Device", etc.
When it comes to saving and loading data, the commanding class's "Save" or "Load" routine is called and at that time all of the minions' settings are saved or recalled from disk.
My save routine is more-or-less straight forward, although it still requires an overwriting "Save" and "Load" vi. Here is an example:
It isn't too bad in that it is pretty straight forward and simple and there also would be no changes to this if the data structure of the class changed at all. It also can save more generalized settings from it's parrent's class which is also a good feature. What I don't like is that it looks essentially the same for each child class, but I'm at a loss on an effective way to move the handling of the save routing into the parent class.
The load routine is more problematic for me. Here is an example:
Again, the desirability of moving this into the parent class would be awesome. But the biggest complaint here is that I can't maintain my dynamic dispatch input-output requirements because the object that I load is strictly typed. Instead I have to rely on reading the information from the loaded object and then writing that information to the object that exists on the dynamic dispatch wire. I also dislike that unlike my Save Routine, I will need to modify this VI if my data structure of my object changes.
Anyway, any input and insight would be great. I'm really tired of writing these same VIs over-and-over-and-over again, and am after a better way to take care of this in the parent class by keeping the code generalized but still maintain the ability to bring back the saved parameters of each of the children classes.
Thanks for your time.
06-29-2012 05:27 PM
Dang, I was hoping there would be some input. *bump* ... :S
06-29-2012 06:29 PM
Bear with me here, as I don't have a lot of LVOOP experience yet but I have some ideas, and am hoping maybe we'll both learn something from some discussion.
I can't tell exactly what you're doing in the parent class' version of the Load and Save. Are you saving two separate files - one for the parent class, and one for the child class? If so, is there some reason you can't simply only the child class? That should include all the associated parent data. If that's not what's going on, perhaps you could elaborate a little bit more.
07-02-2012 09:18 AM
Yeah, sure. Here are the Save and Load Settings from the parent class.
07-02-2012 10:10 AM
I have abondoned the classes that took advantage of the ability to write and restore a class from disk since it was hit by a change in LVOOP that I was very disapointed to find.
My current go-around with LVOOP save restore uses a "Save Thyself" approach where the obects know what they need so they get their internal data from file themsleves.
It has hlped my code more than I could forsee at first becuase it removed the requirment that the user of the classes know what they need and can give it to them. Pushing it down into the claass itself lets me init a DAQ class with the same parent method I use for GPIB Serial or whatever.
That is all I can add,
Ben
07-02-2012 10:27 AM - edited 07-02-2012 10:28 AM
I'm with Ben. Don't rely on the current ability to serialize an object. Create a save method and implement some form of data persistence there. If you modify your class you might be disappointed when you cannot load objects you previously saved. It mostly works but as soon as you reset the version information in the class, you can no longer load the old objects. This is fine if you know how to avoid resetting the history. One thing that will do this is if you move the class into or out of a library. It becomes a new class with version 1.0.0 and it no longer recognizes the old objects.
[Edit: I see that you are just writing to a binary file. I'm not sure you can load older objects anyway using that method but I have never tried it.]
This will not help you right now but there are plans for a nice robust API for saving objects.
07-02-2012 01:38 PM
There's either a hidden function or something I'm not understanding. Why does it appear that the data type input to Read from Binary File is a different type (different wire, anyway) than the data out?
07-02-2012 06:01 PM
@nathand wrote:
There's either a hidden function or something I'm not understanding. Why does it appear that the data type input to Read from Binary File is a different type (different wire, anyway) than the data out?
Because there is a difference between dynamic object data types ("dynamic dispatch terminals") and strictly typed objects. In this instance, the Read from Binary will only return strictly typed data and that is why the wire changes. That is part of my problem and why I have to use my accessor methods to read the data from the strictly typed object that is returned from Read from Binary and then write to my dynamic object.
07-02-2012 06:03 PM
@Ben wrote:
I have abondoned the classes that took advantage of the ability to write and restore a class from disk since it was hit by a change in LVOOP that I was very disapointed to find.
My current go-around with LVOOP save restore uses a "Save Thyself" approach where the obects know what they need so they get their internal data from file themsleves.
It has hlped my code more than I could forsee at first becuase it removed the requirment that the user of the classes know what they need and can give it to them. Pushing it down into the claass itself lets me init a DAQ class with the same parent method I use for GPIB Serial or whatever.
That is all I can add,
Ben
Essentially that is what I am doing, each object runs its "Save Thyself" method (what I am calling "Save Settings").
If my object data changes, I may have a hard time reading my previously saved objects, but I'm less concerned with this right now. My objects aren't necessarily data, more like setting information.
07-02-2012 07:54 PM
@Nickerbocker wrote:
Because there is a difference between dynamic object data types ("dynamic dispatch terminals") and strictly typed objects. In this instance, the Read from Binary will only return strictly typed data and that is why the wire changes. That is part of my problem and why I have to use my accessor methods to read the data from the strictly typed object that is returned from Read from Binary and then write to my dynamic object.
Ah, got it (I warned you I don't have too much experience with this), your situation is starting to make sense to me. I haven't tried this, and don't have a project set up that would allow me to try it easily, but does the Read from Binary File read the child class data and return it as the parent class? If so, can you use "Preserve Run-Time Class" to get the child class?