Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

"Synchronous" export to file from several actors

Solved!
Go to solution

I have a test manager that launches new test actors based on user input to run a test. The user picks a test in a dropdown, and they run it. If they like the result of this test, they can "keep" it and it goes in a listbox. Each test is shown in a "main" subpanel when it's first ran, and can be reviewed by selecting the "kept" test in the listbox. Each test is a separate actor. This all works great.

 

I need to be able to export the tests and reimport them later to add to the series, review data, or whatever. I've selected a TDMS format for now and all of that is working great.

 

My problem: What's the best way to export data from a list of Actors to the same file? Call the main actor "Main", which has an array of "Tests" enqueuers. The Export button is in Main, and I need Main to trigger the export of all of the subtests to the same TDMS file. Main should wait until this exporting is done before moving on.

 

Currently, I am using Reply messages to send an "Export" message to each Test actor in a For loop. This works fine but the error handling is a bit of a pain, as I want an Export failure to just show a simple popup. Right now, a timed-out Reply message sends an error to both the caller (in Send Message and Wait for Response) and the callee actor (in Do.vi). I can ignore the error in the caller actor, but I'll need to override Do.vi in the Reply message to suppress that specific error, and this is all starting to feel like a bad way to do it.

 

Initially I couldn't think of a good way to get this behavior without using Reply messages, since each actor needs access to a single resource (the TDMS file) and I need to know once they're all done. I could add a state machine into Main to send an "Export request" to each Test actor, but I don't want to have concurrent file access issues, so I'd need to have states to send an Export request, then go into "Wait" until it gets the "I'm finished" reply, then send the next one in the list, etc. but that honestly seems like more trouble than it's worth.

 

So, I'd love some advice on this one. My three ideas:

 

1- Stick with the Reply messages, and hope this is one of the corner cases that justify them

2- Switch to a complicated state machine inside Main

3- Create a new "export manager" reuse actor that gets a list of enqueuers, then sends messages to sub-actors and waits on them one at a time to reply with "Finished" (basically moving the state machine into a reuse actor instead of Main).

 

Any thoughts?

0 Kudos
Message 1 of 14
(1,929 Views)

Not entirely sure if this works for you from the description, but you could consider having each Test save its own separate TDMS file, and then sending an array of paths to a new Actor which then merges them.

This would allow the Main and Test actors to proceed with new work whilst the additional actor carried out file I/O to merge.

 

If you wanted them to be synchronous specifically (you mention this in your post, but I didn't quite understand why it was a requirement, maybe just to avoid file conflicts? Since you mentioned that too...) then I would suggest not using an Actor but instead having a normal method in Main that merges the array.

 

It might be a bit slower due to more I/O, so if that's critical, maybe not good, but if that isn't a limiting factor it might be a simple solution.


GCentral
Message 2 of 14
(1,923 Views)
Solution
Accepted by topic author BertMcMahan

How much does the order of the results matter in your exported file?

 

What about creating a "Test Results" actor that launches before any "Test" actors run that stores the results in a set/map or something similar? Then when an export is requested, "Main" sends it the export request and that single actor (which has the results of all the tests) does the TDMS write. Worded differently, instead of having each "Test" actor hold onto its own results until the export is requested, send their results (in a class?) out whenever that data is available for another actor to manage. Would that work?

CLA CLED AF Guild
Message 3 of 14
(1,920 Views)

And in this scenario the "Test Results" actor doesn't even need to be an actor, it can just be a helper utility/class that stores the results and has another method to export them all to file.

CLA CLED AF Guild
0 Kudos
Message 4 of 14
(1,919 Views)

My inclination would also be to have a Test Report Actor.   

 

One way would be: Main would send a the test actors a "Tester.Report" message along with the enqueuer of the Test Report Actor, the test actors would consolidate all their data and send a "Test Reporter.Report" message to the Test Report actor.  However, Getting a message from the Test Reporter to the Main actor is tricky under this scenario because Main would be sending a message to the Testers and Expecting a response from the Test Reporter.

 

Another possibility is Main would send Test Reporter a "Test Reporter.Generate Report()" message along with an array of Enqueuer's to the various Test Modules and a response queue for the error response.   Then the Test Reporter would send requests to each of the Testers, receive their message, when it received all the messages Test Reporter would generate the report and finally send the error message back through the queue which Main was waiting on.

 

 

0 Kudos
Message 5 of 14
(1,910 Views)

@cbutcher wrote:

Not entirely sure if this works for you from the description, but you could consider having each Test save its own separate TDMS file, and then sending an array of paths to a new Actor which then merges them.

This would allow the Main and Test actors to proceed with new work whilst the additional actor carried out file I/O to merge.

 

If you wanted them to be synchronous specifically (you mention this in your post, but I didn't quite understand why it was a requirement, maybe just to avoid file conflicts? Since you mentioned that too...) then I would suggest not using an Actor but instead having a normal method in Main that merges the array.

 

It might be a bit slower due to more I/O, so if that's critical, maybe not good, but if that isn't a limiting factor it might be a simple solution.


Ah, I didn't consider merging the TDMS later. I'd still need a way to know when all of the Tests are done exporting, but a separate actor could handle that logic.

 

The need for "synchronous" behavior is that I don't want Main to allow further user interaction until all of the saving is done. Consider it like saving an Excel file; I want the "Save" behavior to be finished before accessing any of the tabs. In my case, each Tab is a separate test with completely different data saving needs.

 

There are no I/O speed limitations here. Tests are non-trivial but aren't enormous, and the user can wait until an export is finished before initiating another test. There's nothing time-critical happening between tests.

 

 


How much does the order of the results matter in your exported file?

 

What about creating a "Test Results" actor that launches before any "Test" actors run that stores the results in a set/map or something similar? Then when an export is requested, "Main" sends it the export request and that single actor (which has the results of all the tests) does the TDMS write. Worded differently, instead of having each "Test" actor hold onto its own results until the export is requested, send their results (in a class?) out whenever that data is available for another actor to manage. Would that work?



Order of results doesn't matter at all; they're timestamped and organized that way. Regarding the storage actor, each Test has very different data storage needs, and needs to be responsible for its own data I/O. For example, one Test may generate a bunch of arrays of waveform data. Another might just be a boolean. Another might be a picture. Implementing the Save this way may be tricky to do without coupling everything together. Oh, did I forget to mention it's a plugin interface? 🙂

 

Thinking out loud- if I define a "Saveable" interface, then I could have each of my Tests contain a "Test results" object that implements "Saveable" which defines its "Save" method. Each time a test finishes running, it could compile its data into a "Saveable" object and send it to the "Results Manager", which calls each element sent to it, and each one implements its own "Saveable.Export to TDMS" function. (And yes this doesn't have to be an Actor; Main could just store an array of Saveables).

 

I think I like that method the best. That way, Main knows once it's done exporting all of the Saveable data.

 

However, I would love to hear- I know that Reply messages are best avoided, BUT, they DO exist in the toolkit. When is an actual appropriate time to use them? This all feels like a way to make Main act synchronously. Since a Main::Export All Saveables will block until it's done, then the behavior is very similar to just using synchronous Reply messages. I recognize the potential for deadlocks with Reply messages, but if they're used responsibly... is there an actual problem using them here? The current method with Reply messages is a little complex regarding error handling, but it's not any more complicated than introducing another class of Test Results objects. The existing Reply messages don't even return actionable data, just "yeah it finished" or an error.

0 Kudos
Message 6 of 14
(1,887 Views)

@BertMcMahan wrote:

@cbutcher wrote:

Not entirely sure if this works for you from the description, but you could consider having each Test save its own separate TDMS file, and then sending an array of paths to a new Actor which then merges them.

This would allow the Main and Test actors to proceed with new work whilst the additional actor carried out file I/O to merge.

 

If you wanted them to be synchronous specifically (you mention this in your post, but I didn't quite understand why it was a requirement, maybe just to avoid file conflicts? Since you mentioned that too...) then I would suggest not using an Actor but instead having a normal method in Main that merges the array.

 

It might be a bit slower due to more I/O, so if that's critical, maybe not good, but if that isn't a limiting factor it might be a simple solution.


Ah, I didn't consider merging the TDMS later. I'd still need a way to know when all of the Tests are done exporting, but a separate actor could handle that logic.

 

The need for "synchronous" behavior is that I don't want Main to allow further user interaction until all of the saving is done. Consider it like saving an Excel file; I want the "Save" behavior to be finished before accessing any of the tabs. In my case, each Tab is a separate test with completely different data saving needs.

 

There are no I/O speed limitations here. Tests are non-trivial but aren't enormous, and the user can wait until an export is finished before initiating another test. There's nothing time-critical happening between tests.


Order of results doesn't matter at all; they're timestamped and organized that way. Regarding the storage actor, each Test has very different data storage needs, and needs to be responsible for its own data I/O. For example, one Test may generate a bunch of arrays of waveform data. Another might just be a boolean. Another might be a picture. Implementing the Save this way may be tricky to do without coupling everything together. Oh, did I forget to mention it's a plugin interface? 🙂

 

Thinking out loud- if I define a "Saveable" interface, then I could have each of my Tests contain a "Test results" object that implements "Saveable" which defines its "Save" method. Each time a test finishes running, it could compile its data into a "Saveable" object and send it to the "Results Manager", which calls each element sent to it, and each one implements its own "Saveable.Export to TDMS" function. (And yes this doesn't have to be an Actor; Main could just store an array of Saveables).

 

I think I like that method the best. That way, Main knows once it's done exporting all of the Saveable data.


So I'd be tempted to have each Test save its own data and then have the Main actor merge the results, potentially by a method as simple as creating/opening the desired file, then in series opening each of the returned files and copying the data in, then deleting the temporary individual file.

You could expand on this by (when you send the "Please save your data" message) storing a random/sequential number/string in a Set. When you receive the "I'm done, here is my number/string identifier", you can a) start adding that file to the target file, potentially saving some waiting time, and b) remove the ID from the Set.

When the set is empty, you can move on. Although now I write this, I realise that arrangement wouldn't work without some sort of state-like arrangement for the Main actor, which might be a little undesirable if you don't currently have an available/unavailable toggling state or some other state-based arrangements (this could just be an event for the FP to lock/disable controls etc, but you I think would need something...)

 


@BertMcMahan wrote:

However, I would love to hear- I know that Reply messages are best avoided, BUT, they DO exist in the toolkit. When is an actual appropriate time to use them? This all feels like a way to make Main act synchronously. Since a Main::Export All Saveables will block until it's done, then the behavior is very similar to just using synchronous Reply messages. I recognize the potential for deadlocks with Reply messages, but if they're used responsibly... is there an actual problem using them here? The current method with Reply messages is a little complex regarding error handling, but it's not any more complicated than introducing another class of Test Results objects. The existing Reply messages don't even return actionable data, just "yeah it finished" or an error.


I think you could use Reply messages here, but you then can't have the targets act in any meaningful parallel fashion (or at least, it's harder).

If you send each of them in turn the TDMS reference (or class wrapping the file, or whatever) and have them tell you when they've finished, that would work fine, I think.

But if you want to simultaneously do things with the Main FP, you're more likely to have stuff block at awkward times.

After a few attempts, I try to avoid Reply messages if the message will take more than a very short time to execute. I'm not sure how likely that is in your case, it may depend on the size of your larger data sets (and I suppose, the speed of your storage system...)

 

I just looked through my largest current project and found only one child of Reply message - it is in my case an abstract message used to get an integer value the basically facilitates finite-duration connections for results messages. The connection handling Actor sends the message to what is basically a glorified counter, and receives the current count.


GCentral
0 Kudos
Message 7 of 14
(1,876 Views)

@cbutcher wrote:

I think you could use Reply messages here, but you then can't have the targets act in any meaningful parallel fashion (or at least, it's harder).

If you send each of them in turn the TDMS reference (or class wrapping the file, or whatever) and have them tell you when they've finished, that would work fine, I think.

But if you want to simultaneously do things with the Main FP, you're more likely to have stuff block at awkward times.

After a few attempts, I try to avoid Reply messages if the message will take more than a very short time to execute. I'm not sure how likely that is in your case, it may depend on the size of your larger data sets (and I suppose, the speed of your storage system...)


Non-interaction is something of a feature here. If I'm on a particularly slow network connection (like the edge of wifi) the Write operation may take 5 or 6 seconds to send to the network. I'd rather my users not try to change a setting or try to initiate a new test before the Save of the old test is done. The Main FP just serves as a place for some common settings and to hold the "Current Test" subpanel, so there's no other interactions that need to take place (and again, I want to be confident the Save operation is finished before anything else can take place, hence "synchronous" in quotes. It doesn't have to actually use synchronous messages, just block until done).

 

Right now I'm passing around the TDMS reference to each Test actor when they reply that they're done, and it does indeed work fine. My big issue is "Hey, I know Reply messages are to be avoided... but it kinda seems to work fine here, what anti-pattern am I missing, or is there a better way?"

 

Sounds like yes, the Test Results Manager is likely a better way to do it, but given that I got this far and it's working well I don't think I can spare the resources to re-code the Export functions. I wish I had thought of that at the beginninng 🙂

 

I guess I'm not hearing a real "You forgot about XYZ" reason from the experts here to remove the Reply message here, so I think I'll stick with it for now. I do really welcome anyone telling me why I'm wrong though. If I had a nickel for every time I said "Maybe just this once..." and regretted it I'd be a rich man 😄

0 Kudos
Message 8 of 14
(1,860 Views)

@BertMcMahan wrote:

Right now I'm passing around the TDMS reference to each Test actor when they reply that they're done, and it does indeed work fine. My big issue is "Hey, I know Reply messages are to be avoided... but it kinda seems to work fine here, what anti-pattern am I missing, or is there a better way?"😄


I'm going off on a tangent here, but as soon as I see a need to start sharing references (be it file, hardware resource, etc.) I start questioning whether or not it'd be easier (better?) to make a single actor that manages said reference. Often I do that and end up sending data to that actor rather than trying to synchronize use of that reference through Reply messages or something else.

 

In general that tactic has worked well for me and I don't think I've ever used a Reply Msg in any of my AF projects over the past decade or so.

CLA CLED AF Guild
0 Kudos
Message 9 of 14
(1,849 Views)

Some comments from someone using a different messaging framework:

 


@BertMcMahan wrote:

My problem: What's the best way to export data from a list of Actors to the same file? Call the main actor "Main", which has an array of "Tests" enqueuers. The Export button is in Main, and I need Main to trigger the export of all of the subtests to the same TDMS file. Main should wait until this exporting is done before moving on.

 


The messaging pattern you are searching for is called "Scatter-Gather": sending requests to multiple Actors and do a follow-on action when all replies are received. The Actor Framework doesn't have any implementation of the "Gather" part, unfortunately.  If it did this would be easy (and I use this pattern all the time in Messenger Library).

 


@BertMcMahan wrote:

 

Currently, I am using Reply messages to send an "Export" message to each Test actor in a For loop. This works fine but the error handling is a bit of a pain, as I want an Export failure to just show a simple popup. Right now, a timed-out Reply message sends an error to both the caller (in Send Message and Wait for Response) and the callee actor (in Do.vi). I can ignore the error in the caller actor, but I'll need to override Do.vi in the Reply message to suppress that specific error, and this is all starting to feel like a bad way to do it.


Two suggestions about errors in Request-Reply interactions:

1) is consider never timing out (timeout=-1).  This is a clearly defined action that will certainly complete and can't take forever, so why timeout?  If you call a subVI to save stuff there would be no timeout, so why have timeout logic for a Request-Reply?

 

2) is that an error in handling a request is an error to the Requestor/Caller.  If you only want one error handler, that should be the Caller, not the Callee.  The Callee's job is to reply to the request appropriately, and replying with an Error message is appropriate.

 


3- Create a new "export manager" reuse actor that gets a list of enqueuers, then sends messages to sub-actors and waits on them one at a time to reply with "Finished" (basically moving the state machine into a reuse actor instead of Main).


Call that reusable actor "Scatter-Gather" maybe?

Message 10 of 14
(1,829 Views)