Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Multiple data types with telemetry writer

I'm am developing a hardware abstraction layer with Actors. I have an actor called Telemetry Writer that will handle the permanent storage of all telemetry items from the various peices of HW. Each HW Actor is given a reference to the Telemetry Writer's enqueuer. There is a send Telemetry message to transfer the data to Telemetry Writer.

Since I'm using a variety of instrument types, the data types also vary significantly. The types for the instruments I'm using are U8, U16, I32, Dbl, String, and in some cases arrays of each of these (to start - possibly others later). Yes, I can probably reduce the total number of types by taking a little storage inefficiency and rolling up some types into others, but I'd rather not, given the magnitude of the data I will eventually store.

My challenge at this point is coming up with an elegant way to transfer the telemetry data points from the HW Actors to the Telemetry Writer. I've come up with a few notions, all of which have downsides (although any could work).

1. Create a telemetry class. It includes timestamp, source string, data type enum, and variant. The HW actor converts the data to variant and assigns the appropriate value to the enum so it can be re-cast in telemetry writer. Downside is the recasting of varient to appropriate type.

2. Create a telemetry class with same as above except no enum or variant. Create children of this class for each data type. Cast as parent to transfer to the same telemetry queue. Downside is similar to the variants, the parent has to be cast down to child. Each telemetry record would have to be type checked.

3. More "old school", but tried and true. Just use a cluster that includes a value field for each type of data. An enum let's the telemetry writer know what type is in use for a particular record.

My coworker who works in C++ and C# suggested more elegant architectures but unfortunately they are not currently supported in LV (like interface inheritance).

To get started, I stuck with #3. Any other suggestions for more elegant and exstensible solutions?

Thanks!

Jason

LV2014

0 Kudos
Message 1 of 8
(6,771 Views)

I have a data logger actor that has messages (signature, interface) that is something like this:

Log Double (Double, Channel Name)

Log Double Array ([Double], Channel Name)

Log String (String, Channel Name)

Log Error (String, Channel Name)

Log Time (Time Stamp)

Log Error (Error Cluster, Channel Name)

etc.

When I launch things that need to have there data logged I give them the enqueuer to the data logger. Then they send the appropriate message to the logger.

To go another step I have an abstract parent data logger (defines the signature and has the messages and uses dynamic dispatch with pure abstract methods) and then I have a CSV data logger child and a TDMS data logger child that implement the methods.

Hope this helps.

Casey

Casey Lamers


Phoenix, LLC


casey.lamers@phoenixwi.com


CLA, LabVIEW Champion


Check Out the Software Engineering Processes, Architecture, and Design track at NIWeek. 2018 I guarantee you will learn things you can use daily! I will be presenting!

0 Kudos
Message 2 of 8
(4,684 Views)

I have implemented a similar system using case 2. It works fine and efficiently.

In this case your Telemetry class could have for example a type enum that stores the actual object type. I found it much faster than reading the class name or path. You could have a dynamic dispatch VI that returns the actual type of the object, call it "GetType". The type can be hardcoded into these "GetType" VIs in each child.

You can store all Telemetry objects casted up to parent Telemetry object. You don't have to cast these down. You can store these for example in a datalog file or a binary file.

You only have to know the actual type when you load these objects for analysis or so. In this case you can use the type enum stored in the object to know which child to convert to. You can read the type with the "GetType" VI.

I hope it helps and it's all understandable. If not, just ask:)

0 Kudos
Message 3 of 8
(4,684 Views)

I hadn't thought to embed the object class itself...makes sense. I like this approach. Thanks for the suggestion.

0 Kudos
Message 4 of 8
(4,684 Views)

Just a note, but you never need to include a type enum with a Variant, because you can get such an enum using vi.lib\Utility\VariantDataType\GetTypeInfo.vi.

0 Kudos
Message 5 of 8
(4,684 Views)

drjdpowell wrote:

Just a note, but you never need to include a type enum with a Variant, because you can get such an enum using vi.lib\Utility\VariantDataType\GetTypeInfo.vi.

I don't know if you are referring to my comment, but if yes, than I am sorry. It seems like I didn't express my thoughts detailed enough. If you are not referring to me, than let it be just a simple explanation of what I meant in my previous comment

When I speak about "GetType" VI, I mean to have a method in each telemerty class (overriden in all  children classes) that returns the "class name" (as a type enum) to help downcasting the object when we load it from file to the correct child class. This type enum can be used for the case selector of a case structure that downcasts the loaded objects to the correct class.

I guess that the "Get LV Class Path" or "Get LV Class Name" methods would do the same, I just didn't find those fast enough in the past, and in case of class renaming/moving these will probably break the case structure.

0 Kudos
Message 6 of 8
(4,684 Views)

Hi komorbela, I was referring to the OP’s option #1.  Regarding your suggestions, wouldn’t it be better to query the objects for whatever is needed from them, rather than having a type enum?   Want to display a list of current values to the User?   Have a “Get as Text” method.   Plotting?  Have “Is Plotable Numeric” and “Get As DBL” methods.

0 Kudos
Message 7 of 8
(4,684 Views)

I see, so there was no need to explain my point

By the way what you say are probably useful functions and makes sense to use if needed.

What I mean by type enum is the class type. I would use that If I need to downcast the object to use a method that is only implemented in that class. Probably it can be avoided with well organized dynamic dispatched methods (If the slow speed of dynamic dispatching is not an issue).

0 Kudos
Message 8 of 8
(4,684 Views)