09-11-2023 04:49 AM - edited 09-11-2023 04:49 AM
Is there a low-level way to read a the DVR's data without knowing the structure of the data and just determining it at run-time? Maybe as a variant? I'm using LV 2018.
I would like a generic way of serialising objects that use a DVR as their private data so that I can write it to a JSON file with a key name format of my choosing. I can't use inheritance and override a "To JSON" method as the classes are already inheriting from something else which wouldn't always be appropriate to have this abstract method. The LabVIEW version I am using does not support interfaces directly.
Conversion to XML seems to work well for class hierarchy, but it does not dereference the DVR and just returns the 32-bit handle.
09-11-2023 06:31 AM
Hi Matt,
Unless your DVR already contains a variant at creation (which could be a bit impractical and inefficient to use), there is no official way to dereference a DVR without statically knowing its data structure. If you experiment by casting e.g. a cluster DVR to a U32, then to a string DVR to try getting the raw data, that leads to undefined behaviors such as crashes.
Your best option is for your classes to have a public method e.g. "Get Data" to return the dereferenced data. You could then programmatically call this accessor using VI server and proceed with the generic serialization. You will also need a "Set Data" method if you want to deserialize. A disadvantage of this is that by exposing the private data publically, you are "breaking" the encapsulation principle of OOP...
Regards,
Raphaël.
09-11-2023 07:07 AM - edited 09-11-2023 07:27 AM
@raphschru wrote:
Hi Matt,
Unless your DVR already contains a variant at creation (which could be a bit impractical and inefficient to use), there is no official way to dereference a DVR without statically knowing its data structure. If you experiment by casting e.g. a cluster DVR to a U32, then to a string DVR to try getting the raw data, that leads to undefined behaviors such as crashes.
Your best option is for your classes to have a public method e.g. "Get Data" to return the dereferenced data. You could then programmatically call this accessor using VI server and proceed with the generic serialization. You will also need a "Set Data" method if you want to deserialize. A disadvantage of this is that by exposing the private data publically, you are "breaking" the encapsulation principle of OOP...
If you have a class that manages general data, for instance for keeping an data store with run time defined data types, not exposing the private data in some way will make it useless.
There's no way a class will be able to get\set a I32 without exposing the I32...
It's a major improvement to exposing the DVR (as a variant?) and than trying to get it's data.
In this specific case, you could give each class it's own "To JSON" method. Of course, doing so violates the "Single-responsibility principle".
09-12-2023 03:58 AM
Thanks for the suggestions. I had another look and found the DVR refnum type which may have done what I wanted, but couldn't find a way of generating a valid one. Guess I'll go back to a class based approach for now and call it dynamically if the connector pane signature matches.
09-12-2023 04:13 AM
@matt.baker wrote:
Thanks for the suggestions. I had another look and found the DVR refnum type which may have done what I wanted, but couldn't find a way of generating a valid one.
Getting the type of a DVR refnum is trivial...
You can't (easily) generate a DVR with a dynamic type at run time. You can make a .vim that generates the type of a DVR at compile time.
@matt.baker wrote:
Guess I'll go back to a class based approach for now
You mean go forward 😋.
@matt.baker wrote:
and call it dynamically if the connector pane signature matches.
That sounds questionable.
Seems like you'd want to use polymorphism (e.g. dynamic dispatching) to automatically do that. Or is that what you mend?
09-12-2023 04:19 AM
@matt.baker wrote:
and call it dynamically if the connector pane signature matches.
That sounds questionable.
Seems like you'd want to use polymorphism (e.g. dynamic dispatching) to automatically do that. Or is that what you mend?
He explained why: "I can't use inheritance and override a "To JSON" method as the classes are already inheriting from something else which wouldn't always be appropriate to have this abstract method. The LabVIEW version I am using does not support interfaces directly."
09-12-2023 04:35 AM - edited 09-12-2023 04:44 AM
@raphschru wrote:
@matt.baker wrote:
and call it dynamically if the connector pane signature matches.
That sounds questionable.
Seems like you'd want to use polymorphism (e.g. dynamic dispatching) to automatically do that. Or is that what you mend?
He explained why: "I can't use inheritance and override a "To JSON" method as the classes are already inheriting from something else which wouldn't always be appropriate to have this abstract method. The LabVIEW version I am using does not support interfaces directly."
So in stead of creating an inappropriate (?) abstract method that would work well, you'd prefer matching the connector pane signatures? Wouldn't that require you to go through project scripting (to find the object's VI) and than VI Server (to get the CP)?
I just don't see how that would be an improvement, but I'm intrigued.
I guess the question is why it's that inappropriate to give a (each) class that needs to be serialized to JSON a 'to JSON' method (yes srp, I know). I do avoid that by having serialization code that takes any object. But I don't use DVRs. For a list of other reasons, I'll add this one.
09-12-2023 06:43 AM - edited 09-12-2023 06:52 AM
wiebe@CARYA wrote:
So instead of creating an inappropriate (?) abstract method that would work well, you'd prefer matching the connector pane signatures? Wouldn't that require you to go through project scripting (to find the object's VI) and than VI Server (to get the CP)?
This is much simpler than you think:
You could add a bit of error handling to return meaningful error description when the VI does not exist or does not match the generic connector pane. Also the class terminals of the called VI must be of the generic class "Object", otherwise it won't match.
Also a specific implementation of "Get DVR Data.vi" would look like this:
wiebe@CARYA wrote:
I just don't see how that would be an improvement, but I'm intrigued.
This is not an improvement, this is an implementation of an interface mechanism, which is not present natively in LV2018.
Regards,
Raphaël.
09-12-2023 09:13 AM - edited 09-12-2023 09:23 AM
@raphschru wrote:
wiebe@CARYA wrote:
So instead of creating an inappropriate (?) abstract method that would work well, you'd prefer matching the connector pane signatures? Wouldn't that require you to go through project scripting (to find the object's VI) and than VI Server (to get the CP)?This is much simpler than you think:
You could add a bit of error handling to return meaningful error description when the VI does not exist or does not match the generic connector pane. Also the class terminals of the called VI must be of the generic class "Object", otherwise it won't match.
It's is a little simpler than I though, but not much...
I'm missing the part where the connector pane signature is compared... I suppose that would be used to convert a specific DVR type to the variant.
@raphschru wrote:Also a specific implementation of "Get DVR Data.vi" would look like this:
The input class would be a DD input. The to more specific wouldn't be required.
If to more specific is required, unbundling wouldn't be allowed.
You'd still have to implement this "Get DVR Data.vi" method for each class with a DVR. Why not convert to JSON right there and then.
@raphschru wrote:wiebe@CARYA wrote:
I just don't see how that would be an improvement, but I'm intrigued.This is not an improvement, this is an implementation of an interface mechanism, which is not present natively in LV2018.
If it isn't an improvement, why use it?
09-12-2023 10:03 AM
I didn't like the idea of using a generic object type for the inputs for each of the To JSON methods, so I created the following monstrosity instead:
It finds the matching class member using the Application -> LabVIEW Class -> All Methods Of LVClass method, populates the nearest matching terminals on the connector pane, runs the VI and collects the outputs.