LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Array of objects in class private data and calling overridden VIs on these objects?

Hello

 

I am developing part of an application using the actor framework and have run into problems.

 

I have a several actors and will try briefly describe them and their intended functionality. All actors are started by a Controller actor and in the actor core for the controller I have the possibility to do a lot of intiliazation etc.

 

Logger:

Has a message (Send Log Message) that other actors can use to write to the log. It is supposed to take a string input and a log level (error, warning, debug etc). This message chain sends data to the actor core with a user event.

The Actor Core of Logger is supposed to save the incoming message to a log, but should be able to do it in several different ways (file, database, email or whatever).

 

 

Logger Output:

Abstract class that has a dynamic dispatch vi called "Write Output" that all it's children are supposed to overwrite.

 

Logger Output File

Child of Logger Output and overwrites "Write Output" to save the string to a file on disk.

 

 

 

Problem:

I want to be able to set up the actor core for Logger once and for all but still be able to create new children of "Logger Output" and have them be handled in the Logger actor core.

 

My idea was to have Logger use an array of objects as private data and initialize this array in the Controller actor core.

In the logger actor core I could then auto index the array and use each element (each Logger Output child) and the abstract "Write Output" vi to get the correct functionality.

 

HOWEVER, I cannot get this to work properly and I think I have misunderstood something or stared myself blind on this problem. I have tried 3 different methods when it comes to private data for Logger.

1. Labview Object

2. Array of Labview Object

3. Array of Logger Output Object

 

Of these 3 methods, I can only get the first one to work and that doesn't accomplish what I want in the end (being able to add more classes without changing private data / actor core for Logger).

 

I have included 3 screenshots that show snippets of 2 of the actor cores and the private data. File names should correspond to my description above.

The screenshot "Logger actor core.png" shows a very fast test of the 3 different methods I described above. Each of the 3 tunnel inputs to the event structure can be wired to the "Reference" input of "To More Specific Class", but only method 1 (Labview Object) works.

 

 

If you need additional information / screenshots or whatever please ask.

 

Thanks in advance

 

0 Kudos
Message 1 of 8
(5,686 Views)

If I'm understanding you correctly, then the correct option is #3 and it should work. You need that because that's the asbtract class with the method that you want and you need an array of those to iterate over. The key would be the actual objects inside the array, which need to be up to date (file path/reference, DB info, etc.). One cause for concern is that it looks like you're splitting the actor wire to go into a parallel loop to the core loop, which is generally a big no-no - the actor wire is supposed to hold the update actor state inside the core loop and splitting it means that you don't get any of the updated information in the parallel loop. I don't know if this is the problem in your case, but it's certainly a potential issue. It's hard to tell without seeing all of the code and without hearing what's actually wrong (which you didn't explain).

 

One option for handling this might be to make the logger outputs actors as well. The controller will launch them and then iterate over them and send them a message whenever there's a new line to log. That way, each actor can decide on it's own how to log and at what rate (e.g. maybe the DB connection takes time or maybe the file logger wants to buffer the messages and flush them in groups).

 

Also, you might wish not to set these loggers up in the controller, but instead give the controller a message which can launch a new logger output actor (each with its relevant details) and add it to the list of loggers. That way, the controller doesn't have a static dependency on the list of potential loggers, but rather only knows about the abstract class.


___________________
Try to take over the world!
0 Kudos
Message 2 of 8
(5,628 Views)

Thanks for the reply and sorry for the confusing OP. It was meant as a quick test and a way to skip making 10+ screenshots :).

 

I updated the actor core for the Controller and Logger to be less confusing (hopefully) and attached 2 screenshots.

 

I agree with you that it would be a good idea to make a message for the controller that can launch new children of the L" abstract actor, but for these tests I have just launched it the easy way (dragging it to the controller actor core).

 

Both Logger Output and Logger Output File are actors and in Logger Output there is a dynamic dispatch vi called Write Output that I want to override in all its children.

 

The problem is when I run the actor core of Logger (see the screenshot) only the abstract version (the one in Logger Output) of Write Output is executed, not the overridden version from Logger Output File.

When I did the same thing with the actor cores looking like the did in my original post, then the correct overridden version got executed when I used the method with one Labview Object in private data (not any array or an array of Logger Output objects).

 

 

 EDIT: I just tried to have the Logger private data be a single Logger Output object and writing the Logger Output File to that piece of private data in the Controller and the correct Write Output override method gets called now. So apparently I have done something stupid when it comes to creating the array of Logger Output objects and writing them in the controller? The private data is in the Logger actor so I have simply right clicked and chosen "New VI for Data Member Access" and chosen "Write" for "Element of Array of Logger Output Objects".

 

 

Download All
0 Kudos
Message 3 of 8
(5,623 Views)

Without actual code it's really hard to tell. As far as I can tell from the images, you are putting a logger output into the actor before launching it, so even you split the wire inside the actor core, it should still work (although again, that's a bad design. There are some discussions in the AF group in the communities about why this is bad) and if I understand your last part correctly, it does actually work now.

 

If there's still something you don't get, upload actual code we can play with.


___________________
Try to take over the world!
0 Kudos
Message 4 of 8
(5,605 Views)

I have stripped away as much as I could in a reasonable time and made a minimal example of the functionality I want to achieve (but can't) and attached the code.

 

The two projects are in two folders called Logger nonaray and Logger array.

 

If you remember my earlier post I have 3 different actors that have to do with logging.

 

Logger: In private data it has a single object of type Logger Output and an array of the same type with write accessor methods for both.

 

The projects are identical except for 2 places:

 

Controller Actor Core: Here the data is written to the Logger private data in the form of a single object of type Logger Output (really it's a child of this class) or the same data is written to index 0 of the array of objects.

 

Logger Actor Core: Here a log message is constructed and the method Write Output which is overridden by all children of Logger Output is called. It is either called on the single object or on element 0 in the array of objects.

 

__________

 

If you open the project and execute the vi called Launcher.vi you will see the actor core of the GUI actor which has 2 buttons; Log something and Quit. Pressing Log something will give you a prompt for a file location to log to and then save a time stamped message to that file. Quit is supposed to shut down the actors correctly.

 

Due to time constraints / for simplicity you will get the prompt every time you press Log something and there are 1 button dialogs to debug.

 

Pressing the log button in the project WITHOUT the array will actually log something and you will see 2 dialog prompts with Write output abstract and Write output file to indicate that both methods have been called.

 

 

Pressing the same button in the project WITH the array will only give you the prompt for Write output abstract and nothing else (therefore no prompt to open/create the log file)

 

 

Thanks for taking the time to help out.

 

Message 5 of 8
(5,571 Views)

As far as I can tell from a quick look, your problem is that the array is empty, so indexing out the first element returns the default value, which is an object of the abstract class. When I used Build Array inside the Write Element... VI, I did get the prompts, because now the array had one element.

 

You could do some range checking to add elements or return an error if the index you chose is not in the array. And like I said, it would probably be better if you could just send a request to add a logger.

 

And again, having the parallel loop is a problem because you no longer have access to the state data. I didn't look closely at the design, but it's probably better to get rid of second loop and the user event altogether and just use a standard message and dynamic dispatch VI to handle things. You can put the shared code in a static dispatch VI in the parent class and call the DD VI from inside that VI.

 

In general, if you want to debug things like this, you should start from the symptom and work backwards. If the symptom is that you only get the dialog from the parent, make sure it's actually a parent. If it is, see what goes into that. If you had put a breakpoint at the beginning of the VI and then a probe on the array wire, you could have seen that it was empty.


___________________
Try to take over the world!
Message 6 of 8
(5,562 Views)

I think I get most of your points and thanks again for the reply.

 

So if I understand correctly I can create a DD vi in the controller that takes a logger output (abstract) object as input and then uses "to more specific class" and "launch actor" to start the specific output actor. But which actor should actually use this message? I can't quite figure out how to make so that I don't have to intrude in either Controller or Logger when I want to add new output actors.

0 Kudos
Message 7 of 8
(5,529 Views)

Here's a very quick mockup of one way to do this. The details may be wrong, so take it as what it is:

 

Logger Actors.png

 

 

The top VI launches a new logger actor and adds it to the list. You don't have to touch it when you create a new logger type, just call it once more with the new logger and its details. The controller should actually also be an actor, but I did this quickly. The same applies to the other classes.

 

The middle VI is the VI that your code actually calls to log data. It will send a message to all the loggers.

 

The bottom VI is essentially the Do VI of that message and it's in the abstract logger output class. It does the shared stuff and then calls the DD VI which does the actual logging. That subVI is what you have to modify with every logger class that you add.

 

 

Again, this is just one way of doing this. It might not fit what you want to do, it might have problems, etc.


___________________
Try to take over the world!
Message 8 of 8
(5,520 Views)