Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Using inheritance to act on different data in the same way?

Solved!
Go to solution

Apologies I know this must be simple, but I'm misunderstanding something somewhere with inheritance and though I've been digging I can’t find anything that clarifies this for me.

 

I have two DAQ actors, one acquires Voltage, and is in charge of further methods relating to that. The other acquires Temperature, and is in charge of further methods relating to that.

This is the launch tree:

DAQ tree.jpg

I feel like I should be able to create a DAQ parent class, with all the basic acquisition methods in it, and then I can add the specific methods that I need to occur after the acquisition in the child classes.

 

But I’m struggling because while I want the children to have the same pre-launch init (creating the task) and actor cores (helper loop with DAQmx events), I need the details of their acquisitions (eg the channel, sample rate) to be different. In my mind, the children will have their acquisition details in their private data, and use the parent methods to act upon this different data in the same way.
But the call parent method doesn't carry through the task created by the child, say, so I can't utilize the DAQmx event structure of the parent, because it doesn't know the task of the child.

 

So is inheritance only for when you want the child to be using and extending upon the exact functionality of the parent methods, right down to the parent’s class data being used in its method? Or am I missing something here?

(Is this what people mean by the open-closed principle..?)

0 Kudos
Message 1 of 11
(2,392 Views)

Hi!

 

You've stumbled across one of the first big hurdles to understanding object oriented programming. To start, the open-closed principle means open to extension, closed to modification. The point of this is, once you create an interface for an API or set of functions, that interface should never change, but it should be created such that you can add additional functionality without breaking the old. For classes in LabVIEW, that means consistent connector panes. What is stored on your class wire shouldn't change, but the options for what to do with it can be added to.

 

I don't exactly follow the structure you've proposed. However, I think I understand your goal. You are trying to build a hardware abstraction layer to handle your data acquisition using the DAQmx protocol. Your parent class is of generic type task, your children are of specific type such as strain gauge or temperature acquisition, each implying a particular sample rate, acquisition type, etc. The structure you've described sounds like the children all just call the parent method for their initialization, so, why implement a child method for that at all? Just run the parent method directly. If the only thing that is different is the child class data, pass the child class data to the parent method via its connector pane (not class data) and you're done! That's the benefit of inheritance. You could also include that class data in the parent if it is the parent method using it anyway. If you need each individual task type to do something completely different, override the parent method with the completely different code. That's the benefit of overriding. If you need each individual task type to do something extra, override the parent and use a call parent method node as the first call in the child method. That's the benefit of extension. Extension can then be set up for fancy things like dynamic dispatch so that your upper level code operates on an array of parent tasks using the child's implementation where it exists and the parent's when no child implementation exists.

 

I hope this helps!

Philip Bear
Certified LabVIEW Developer
Message 2 of 11
(2,363 Views)
Solution
Accepted by topic author Shiv0921

"But the call parent method doesn't carry through the task created by the child, say, so I can't utilize the DAQmx event structure of the parent, because it doesn't know the task of the child."

 

Add a DAQmx ref to the parent's private data. Create a protected accessor  (or public if you like).

Then in the child's prelaunch init, set the ref.

 

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
Message 3 of 11
(2,354 Views)

Of courssse! Thanks Taggart, that makes a lot of sense.

Can I just confirm that this is the right way to be implementing said accessor? (all public)

project view.pngvis.pngcrash.png

 

Based on traces, the actor core crashes when it gets to the event registration. Definitely not asking you to diagnose it, just I had the same crash the other week that turned out to be because I’d put an interface in the wrong library, so I’m wondering if I’m somehow putting the accessor in the wrong place. Would be great to rule that out.

 

At this point the timeline on this project is tight, so I’m inclined to stick to a simple no inheritance AF if I’m going to keep introducing access crashes (due to my own lack of understanding about access scope presumably), it just seems a shame.

0 Kudos
Message 4 of 11
(2,309 Views)

Thanks Philip for your detailed response.

 

My feeling was I wanted to have the children exist so that they could own their own specific methods, as there’s a chance voltage or temperature will be cut from this program, and I so I wanted to just cut out the relevant actors when that happened, rather than having multiple instances of a more generic actor that owned all of the methods and acted according to task type. I see that that’s where I can be using extension though, which sounds great.

 

I’m glad you’ve called it a hurdle, it certainly is something to get one’s head round, and I can see now why people recommend a solid foundation in OOP before using AF. I thought I was comfortable with the concepts when it was still all “a ‘cat’ and ‘child’ are both ‘mammals’ so they can both ‘breathe’ but then the child can also ‘speak’”, but then it comes to applying that in code and passing through data and things get harder. I look forward to utilising it more though, providing I can workout how to stop it being another source of crashes in my code! *despair*

0 Kudos
Message 5 of 11
(2,306 Views)

My pleasure!

 

I mean, a child can have it's own methods, that's another form of extension, but if it's siblings all do the same thing a slightly different way it should probably be a parent method with overriding child implementations as good coding practice.

 

It really is. That was the hardest one for me personally for sure, once I got past LabVIEW's flavor of OOP in implementation Actor Framework comes pretty natural.

 

As you're setting this up, you might look up example HAL code (Hardware Abstraction Layer) using AF and/or DAQmx. You might find some useful starting points or ideas.

Philip Bear
Certified LabVIEW Developer
0 Kudos
Message 6 of 11
(2,300 Views)

I can't say why the event registration is crashing LV. But I noticed a couple things to watch out for in your snippets.

 

Any error in your Child pre launch init will be dropped. To avoid this, I usually put an error case structure around the call parent method Pre Launch Init.vi. If there's no error, I call parent. If there's an error, I don't call the parent and just pass the error through.

 

In your Parent actor core, recognize the call parent method for Actor Core.vi is the Actor Framework's message handler. The code you showed will wait for all the DAQ capturing to finish before launching the AF message handler (because of the error merge beforehand). Normally you want your helper loops (DAQ event handler) in parallel with the AF message handler, not in series (before or after).

 

Good luck getting to the bottom of the crashes.....

Message 7 of 11
(2,294 Views)

I'm not sure why you are getting that error. It should work.

 

Phillip - good catch on the parallel vs .serial thing. Missed that at first glance.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 8 of 11
(2,288 Views)

I'm going to take a stab at that access violation... I bet you had an error upstream somewhere that didn't propagate, and you somehow wound up with an empty task wired the event registration node.

 

Apparently, that's... expected behavior 😞

 

https://forums.ni.com/t5/LabVIEW/Access-violation-when-registering-for-DAQmx-Event-with-empty/td-p/3...

 

I submitted this to NI and I don't think they even made a CAR for it. Just let me know that yeah, usually you shouldn't register an empty event 😞

 

Some notes:

 

I would remove the error case structure on the accessor. You can keep the wires (especially so you can use property nodes) but do you really need the case structure to NOT wire that value if there's an error?

 

If I had to guess, you had an error starting the task. Since your accessor Error Out is NOT wired to the Pre Launch Init (PLI) Call Parent Method node, the Error Out for PLI didn't show an error. Since that error wasn't detected, the actor successfully launched. (Errors in PLI will not launch the actor, and will show up in the Launch Actor node when it tries to launch). Since this actor probably launched with an error, and the accessor doesn't fill in a value tor Task Out if it sees an upstream error, then when you launch your Actor Core you try to register an empty task to the Register Events function- giving you the access violation.

 

BertMcMahan_0-1625673776406.png

 

Next, you don't need the 5ms wait in your helper loop in Parent Actor Core. The events therein will handle your timing, and the 5ms wait isn't doing anything to help (and could hurt you if there are lots of samples coming in).

 

Next, I'd remove the Event triggered popup. It'll fire every time new data shows up, but then it'll block until you click the button, which will cause events to queue up. It's fine for debugging but I'd recommend using a Boolean indicator that toggles each time the event comes in. That won't block but it'll let you know when events show up.

 

Last, you could consider placing Start Task in the parent Pre Launch Init, since you'll be doing that every time since this is a DAQmx based API. This isn't a big deal though.

 

Edit: Yes, the serialization from your DAQmx Error Out going into Actor Core is a problem- it will not work at all this way. Errors in that should send an Actor Stop message to the Actor Core. See Tom's LabVIEW Adventure on Youtube for some good AF tutorials, including best practices for error handling in AF.

Message 9 of 11
(2,287 Views)

Thank you so much for your replies everyone, apologies for my delay responding.

 

BertMcMahan in particular, thank you for taking the time to give me pointers on the crash. Crashes are so despair inducing, so I really appreciated your input.  I spent some time this morning implementing the fixes you’ve suggested and also trying some other things and it seems it came down to the start task being put before the register for events? This seems bizarre but moving it reliably caused a crash in non actor code and the example version too so I guess that’s that?

crashcrashnew actor corenew actor core

 

 

I also now see how illogical my error wiring was, thanks all. I imagine I’ve fallen into the trap of not questioning the automatic placement of the error case structure elsewhere, so I’ll be looking at all of my error handling more closely now (plus the serial point!). Thanks too for the suggestion of those videos.

 

So cheers for the changes you suggested, they lead me to looking closer at the whole setup, and I think finding the solution! At any rate the way I’d written it exposed me to a crash had there been an error in the task creation, so thanks for pointing that out, no doubt it would have caught me out down the line!

0 Kudos
Message 10 of 11
(2,214 Views)