LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
HenrikDueholm

Increasing class flexibility by adding a unique class identifier

Classes suffer from the following:

  1. Classes are identified by their name.

    1. Renaming a class results in a loss of identity history. The version history is reset

  2. The name consists of the file name and all the libraries the class is nested in.

    1. Moving a class into or out of a library counts as a rename of the class.

 

So what is the issue with this? Any method you use to save the class data to a file (datalog, xml, flattened string...) will contain the class name (including library position) and the version. If you move/rename the class this data will change and your externally saved class data will be unreadable.

 

Also using classes in packed libraries will result in the classes being renamed (lvlib-->lvlibp), but the classes will retain their version history. Still, the rename makes saved data unreadable.

 

I propose that we introduce a static class ID that persists through all the above operations. I'm thinking something like a Class GUID. This GUID would be created together with the class and would be read only. It would be good if Get LV Class Name.vi would return the Class GUID and if you introduced a Get LV Class Default Value By Class GUID.vi (Class must be in memory). All the operations that saves/flattens etc. and currently uses version and name should then use version, name and GUID. On load/unflatten the name would not be used at all, only the GUID and the version.

If this was implemented:

  • The version would never be required to be reset. As long as the GUID persists the version can remain.

  • Saved class data would not become corrupt upon trivial operations in the project. As it currently is you cannot move a class out of a library and restore it to the same position/name without corrupting your saved class data (unless the class version is 1.0.0.0 already).

  • You would get fewer complaints regarding the OOP implementation.

 

So there are some problems with doing this. I'll list possible solutions along with the problems:

  1. What should be done on a rename or library relocation:

    1. Version should be incremented retaining the old name. GUID stays the same.

  2. What should be done on a “Save as” - “Copy”:

    1. New GUID created for the copy. Save as will prompt you to retain version history or reset the version to 1.0.0.0 (if you can find find a way to do this), or just reset the version.

  3. What should be done when a class is copied in internet explorer and loaded?

    1. This is currently very very bad for labview, and thus never done.

    2. But now we can detect that we have multiple GUIDs and a pop up should be displayed with a list of all duplicates. There should be three choices for each group of duplicates:

      1. Retain GUID, only one can select this

      2. Get new GUID and prompt for class rename – version history handles as in 2.1

      3. Don't load the class and discard the operation adding it to the project.

  4. What should be done when both a class from a packed library and the library that was packed are trying to be loaded at the same time:

    1. They have the same GUID, prompt the user to choose which to load, only one can be loaded at any time.

  5. What should be done with the version numbers when packing a class in a packed library:

    1. Original has version incremented twice, all version have the same data. Example:

      1. Original class version = 1.0.0.4

      2. Packed class = 1.0.0.5

      3. After Packing original is = 1.0.0.6.

 

The major issue with this is probably that it'll increase the overhead on all classes, right? So it doesn't have to be a GUID, but some form of uniqueness should be attributed to the class because the system is extremely inflexible in the current implementation.

 

Please don't propose that I implement methods on all classes that saves the private data piece by piece as a work around. If that was the intention that NI had with classes then wiring a class to a flatten/save etc. node should result in a broken wire.

 

I might have been overly thorough with my explanation but this is a big issue for me due to CAR543330 where the work around is reducing the size of my libraries, which is really really hard when they contain classes that are also saved to a large number of external files.

 

Best regards Henrik

8 Comments
fabric
Active Participant

I recognise the administrative issues with such a change, but I still think there is some merit in the idea. (In theory the same system could be applied to all LabVIEW files but that would probably get out of hand much too quicky!)

 

The two main things I'd most like to see:

  1. Moving classes in and out of libraries should not result in broken links the way it currently does
  2. Clearing of version history should be a manual operation, not a (well-hidden) side effect of the rename process

For the record, I don't think it would be practical to use the internal ID as the primary class identifier (and Henrik gives most of the reasons above). I think the cleanest implementation would simply be to retain the name as the primary identifier and then defer to the internal ID when there is ambiguity in the name, such as when reorganising "similarly named classes" in containing lvlibs... Not sure how this would pan out in practice! Smiley LOL

tst
Knight of NI Knight of NI
Knight of NI

There is some discussion on this idea here - https://lavag.org/topic/12203-persisting-classes-and-geneology/

I don't think you'll find anything there that you haven't covered, but I don't remember the details.

 

Also, you might wish to look up AQ's character lineator. I believe it should be able to handle this use case, although I never looked at the code closely.


___________________
Try to take over the world!
AristosQueue (NI)
NI Employee (retired)

tst is correct. The Character Lineator was created to handle this case.

https://decibel.ni.com/content/docs/DOC-24015

 

> Moving classes in and out of libraries should not result in broken links the way it currently does

 

Yes. It should break links. There is no room to debate this -- that breakage is the entire reason libraries exist. Libraries provide a unique namespace. Moving the class is the same as deleting one class and creating a completely new class. It is not the same class any more than moving it to a different library would be the same class.

 

> Clearing of version history should be a manual operation, not

> a (well-hidden) side effect of the rename process

 

If you want to preserve your history for some reason, you can use the VIs that exist to do so, copying it off before the rename and adding it back afterward. It generally won't help you because the class isn't the same class -- any object that tries to unflatten won't find its class because it is looking for "LibName:ClassName" and not just "ClassName", unless you also patch up all your flattened strings everywhere. Good luck with that.

vi.lib\Utility\EditLVLibs\LVClass\Get Mutation History.vi

vi.lib\Utility\EditLVLibs\LVClass\Set Mutation History.vi

 

Serialization and deserialization of objects as the class definition changes is an EXTREMELY HARD PROBLEM TO SOLVE. The Character Lineator is ~900 VIs that provides an excellent solution to the problem with very minimal overhead to the classes being serialized. You may not need all of this for your personal situation... many users get by with the partial solution provided by the mutation history. The mutation history is meant to handle the most basic cases. It does not handle the more extreme cases. For that, you need infastructure. Sometimes lots of infastructure. Sometimes 900+ VIs worth of infastructure.

AristosQueue (NI)
NI Employee (retired)

Henrik: Just so I address your idea directly. What you propose was actually done in an early draft of LV classes. It's a disaster and a half. You have this magic GUID that no one really sees or manipulates, so someone copies a file on disk and the GUID stays the same but the file gets renamed and then things start colliding that no one can explain, and that's just the start of the nightmare. It's pretty trivial to get really tangled results.

 

Ultimately, after lots of experiments, we went with the pattern followed by every other OO language: the name of the class is the name of the class. Period. If you want something else to identify your objects, encode it in the objects in your own custom serialiaation. 

Darren
Proven Zealot
Status changed to: Declined
HenrikDueholm
Member

Argh. Alright I get what you're saying but I really don't like it. 

I didn't want the GUID to be hidden, just read only. If it isn't hidden and you made conflict resolution on multiple loads it could be solved. And external renames wouldn't be an issue if the class had name changes in the mutation history (the class would know that it had been renamed and make a fuss! You'd still only be allowed to load one though (or promted to generate a new GUID for the duplicates), but you'd know what went wrong. Anyway you know how it is, I like my idea so I defend it a little.

 

It's just so anoying that you can accidentally move the class and boom, all externally saved class data are invalid. The only undo you have is to close the project without saving. But there's no warning that it'll happen so why not just move the class back and continue? It hasn't happend to me, but I'm glad I learned the importance of this before it happend.

 

So basically what I've learned from this is:

  1. Never put a class into a library unless you're absolutely certain that you'll never want to remove it again.
  2. Never put classes in packed libraries unless they were created to be there (no refactoring into packed libraries).
  3. Think really hard about your class name before you create it.

I have some major issues with point 1 and 3. I havel about 450 classes in three major libraries containing several sub libraries (sometimes nested down to the fourth level). No it isn't smart, but it's how I started out, and it was to late to go back when I realized it :(. And some of my names are just dumb (Instrument.lvlib:Data_Pressure_Pressure.lvclass).

 

I've checked the Character Lineator. It seems that you'll have to design your application around it, right? Making it great for starting projects, but not so much for an almost finished project

 

Thank you for the reply.

 

AristosQueue (NI)
NI Employee (retired)

Your three lessons are true in pretty much every programming language IF you have data persistence between revs of your class. If you don't need data persistence -- and most classes do not -- then these are not significant issues. It's only where you're going to preserve data across edits.

 

> ... Character Lineator ...  It seems that you'll have to design your application around it, right?

 

No, it can be dropped in later, but it doesn't help with recovering already-serialized data. If you've already got serialized data in the field that you need to keep live, that's when you're in a bind. But if you don't already have serialized data, then you can just

a) change the classes that you want to be serializable to inherit from Serializable.lvclass

b) Add the To String and From String methods

c) Replace any existing calls to Flatten To String prim and Unflatten From String prim with the Serializer and Deserializer functions.

AristosQueue (NI)
NI Employee (retired)

PS: Serialization is a fascinating topic to me over the last couple years... I hope someday to integrate more of the concepts of the Lineator into LV proper, but everything I've read or brainstormed on still leads to the conclusion that anything that will be serialized must be carefully considered before the first release or you can easily hamstring yourself when you work on the second release. I haven't found any way to reduce the work, only ways to make the work manageable and to raise awareness that the work is required.