Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Using the same object in two actors at the same time

Hello,

I put this question on general LabView Forum, but later i found this group, maybe this is better place, so i dupicate my question. Sorry for that.

I'm in doubt.  In my application, for example i have three Hardware classes - Database, DAQ1, DAQ2.

Database class has one main method (except initialization methods) - Log Measure.

DAQ1 class also has one method - Get Voltage, and DAQ2 class has one method - Get Current.

Then i define two HAL (Hardware Abstraction Layer) classes - Measurement1 and Measurement2.

Each of those classes have two main methods, respectively - Measure Voltage and Measure Curent. They also have some initilziation methods.

In code, i create three objects of Hardware classes - Database, DAQ1 and DAQ2 and initiaze it. For example in Database private cluster i have DB RefNum, in DAQ1&2 i have device addresses etc.

Next I initialzie Measurement1 with two hardware objects - Database and DAQ1, and in the same way - Measurement2 with Database, and DAQ2 objects.

What Measure Voltage and Measure Curent (methods of Measurement1 and Measurement1 objects) do?

Measure Voltage method call DAQ1::Get Voltage method, then Database::Log Measure.

Similarly, Measure Current method call DAQ2::Get Voltage method, then Database::Log Measure.

Till now it's clear i think. It should work properly.

But now i incorporate Actor Framework, and define Measurement1 and Measurement2 as an actors.

Now i can run Measurement1 and Measurement2 at the same time, simultaneously, but this two actors share one object - Database. So my question - what is when actors want to use Database::Log measure method at the same time and this method is time cosuming (for example large pack of data).

Is one actor waits for second to stop executing this method of one shared object? I think yes because reentrancy setting (http://zone.ni.com/reference/en-XX/help/371361J-01/lvconcepts/reentrancy/)

But what, for example if i share one Hardware object of device (ADC converter) beetwen two HAL classes (Measurement1 and Measurement2). This Hardware object has two methods, for ex. Get Voltage At channel 1, and Get Voltage At Channel 2. Both of this methods cannot by call at the same time, because in physical device i can measure only at one channel in the same time.

Let the Get Voltage At channel X meausere take 10 seconds. Now i have huge chance, that Measurement 1 actor call Get Voltage At channel 1 method, and Measurement 2 wants to call Get Voltage At channel 2, when Get Voltage At channel 1 is in process. How to inform actor to wait till second actor release device resources?

Regards,

Kacper

0 Kudos
Message 1 of 10
(8,403 Views)

Let's start with the general axiom: Sharing data between actors is a bad idea.

I'm not saying it won't work. I'm not saying you couldn't build a successful application with that approach. I am saying that it is fraught with problems and reintroduces many of the problems that the AF was designed to prevent from occurring when working with parallel processes.

So... you want both of them to talk to the same Database object. You are correct ... if you just have them both call into Database:Log at the same time, one will wait on the other. This is exactly the kind of cross-talk that we want to avoid with objects.

So, here are your general options:

1) If the two measurements are reading from the database, have the VI that launches each measurement give the objects a copy of the portion of the database that they need to do their job. The launcher VI can either set the data into the objects when it launches them or by sending a message later.

2) If the two measurements are actively messing with the database (read and write), then break the single database object into pieces. Give each actor its own piece. When one measurement is done modifying its piece, it sends that piece back to its caller to be integrated back into the master copy of the database.

3) If you truly cannot disentangle the database access then you are kind of stuck making one process wait on the other, but it doesn't have to be the kind of dead waiting that a subVI call does. When a measurement needs data from the database, send a message to caller asking for the data. The caller can pull from the DB and send it back to the measurement. While that's going on, the measurement may be able to do other work or at the very least is available to hear a Stop message if it comes along. Similar process for writing to the DB -- send a message to the caller saying "please write this information into the DB for me."

0 Kudos
Message 2 of 10
(5,628 Views)

Thank you very much for your answer AristosQueue. So i must try to use only one physical device by only one actor. Actor's methods don't run in parallel? Am i right? I have NI 9476 DI unit and use it to control pneumatic valves. Valve opening takes 10 second.  I have a Controller actor and HAL sub-actor (named NI9476 Helper - to control NI9476). NI9476 Helper has public method Open Valve(valve_number). There are also message class Open Valve Msg.

Now what happens when i call Send Open Valve(1) from Controller to NI9476 Helper two times in a short period of time? (valve opening takes 10 seconds).

--

Kacper

0 Kudos
Message 3 of 10
(5,628 Views)

Ok, i tried this, and actor executes his tasks in serial, that's ok. I have one last question, i think.

I'll create Database actor, Data Acquisition actor, and Controller actor which will communicate with the two previous. I can create Controller method named for example Write to Database, call it from Data Acquisition actor, and Controller then will call suitable method in Database actor (all via Messages of course).

The question is - is it possible to call Database actor method from Data Acquisition actor "directly" but via Controller actor. That means without write Controller Messages for every one method in Database. Instead write one generic method in Controller, and relay calls to method names form Data Acquisition actor?

Best regards,

Kacper

0 Kudos
Message 4 of 10
(5,628 Views)

My general recommendation is to respect the actor tree and have the root actor relay messages as needed. My recommended way to do this is with these steps:

  1. your Data Acq would send message to Controller
  2. Controller would receive the message and send a new message to the Database.
  3. The Database would receive it, process it and send a response to the Controller.
  4. Controller would receive it and send a new message containing the response on to Data Acq

All of that is a lot of work, but it has valuable effects in terms of independence of actor design. The downside, obviously, is the code you have to write and sometimes it becomes a performance problem for the application overall. You can shortcut across the actor tree... I recommend that you try it the other way and only take the shortcut if you need it for performance. But if you do need to shortcut, here's the process:

  1. Controller sends a message to Data Acq... the message contains Database's enqueuer.
  2. Data Acq receives the message and stores the enquer in its private data. Data Acq can now send a message directly to Database whenever it wants. In that message, Data Acq includes its own message enqueuer (see the Self-Addressed Msg class in the Actor Framework's Advanced palette).
  3. Database receives the message, processes the request, and then uses the enclosed enqueuer to send the response directly back to Data Acq.

You have to be careful when you write Data Acq -- the enqueue to Database might return an error during shutdown because Database might stop first... you have to be prepared in all of your send code to handle that error and not propagate the error -- it isn't an error as far as your application overall is concerned, it just indicates that Database shutdown first. One way to achieve this when you want to shutdown your application, do this:

  1. When Controller receives the Stop message itself, in its Handle Stop Msg.vi, it (a) first checks a Boolean in its private data called "I am now stopping".  (b) If the variable is False, then Controller sets "I am now stopping" to True and then (b) Controller then sends Stop Msg to Data Acq then (c) Controller ignores the instruction to Stop by returning No Error. (d) If the variable is True, Controller sends the Stop Msg to Data Acq and then returns the error to actually stop its own execution (43 for a clean exit, 1608 for emergency exit)
  2. Controller then goes back to listening for messages. Eventually, it will receive a Last Ack message from Data Acq. In its Handle Last Ack.vi, Controller checks the "I am now stopping" flag.If this flag is true, it sends a Stop Msg to itself.
0 Kudos
Message 5 of 10
(5,628 Views)

Ok AristosQueue, thank you very much for your time. Now everything is clear.

Kind Regards,

Kacper

0 Kudos
Message 6 of 10
(5,628 Views)

AristosQueue a écrit:

  1. When Controller receives the Stop message itself, in its Handle Stop Msg.vi, it (a) first checks a Boolean in its private data called "I am now stopping".  (b) If the variable is False, then Controller sets "I am now stopping" to True and then (b) Controller then sends Stop Msg to Data Acq then (c) Controller ignores the instruction to Stop by returning No Error. (d) If the variable is True, Controller sends the Stop Msg to Data Acq and then returns the error to actually stop its own execution (43 for a clean exit, 1608 for emergency exit)
  2. Controller then goes back to listening for messages. Eventually, it will receive a Last Ack message from Data Acq. In its Handle Last Ack.vi, Controller checks the "I am now stopping" flag.If this flag is true, it sends a Stop Msg to itself.

I have a problem here, it's exactly what I want to do: my controller must wait for nested actors to stop before cleanup, and for this must handle their Last ack message. The problem is that when we reach controller's stop, its MHL has already stopped so Last Ack sent by nested actors back to controller aren't caught... How can I solve this?

0 Kudos
Message 7 of 10
(5,628 Views)

What AQ is saying is that you don't stop the Controller, so the MHL is still active. You have to set internal data to "Stopping" in the controller . You then override the Handle Last Ack Core and keep track of your Daq actors Last Ack. When you are happy with the stopped state of the nested actor(s) then you can stop the Controller.

I have done this where I have a list of nested actors that add their names to some data in the controller when they start. When the controller is stopping it removes the names from the list as it receives Last Ack messages. When the list is empty then it stops itself. If you only have a single nested actor you don't need the list.

Good luck,

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!

Message 8 of 10
(5,628 Views)

This presentation has a useful example to do exactly what Casey is describing (refer to slides 53-55): https://decibel.ni.com/content/docs/DOC-30870

-Patrick

Message 9 of 10
(5,628 Views)

Got it. At first controller stop you set the "shutting down" flag, and dismiss error 43, and when all nested actors has shutdown, do the real shutdown. Thank you!

0 Kudos
Message 10 of 10
(5,628 Views)