Jump to content

OO Task override without Blocking


Recommended Posts

Hello everybody,

I am trying to create a common task class in labview where I would like to substitude the task vi with a task vi of different moduls. So that it would be possible to start, stop, set cycle time ... for each derived class. Additionaly I only like to use reference, since the task should run whereas I use the functions to access its private data.

For example I would run two modules classes which are derived from the task: a measurment device and a generator device. Each class task communicates with its device and saves the data information in its private members. With read functions I would like to access the data from wherever by knowing the reference without waiting for the task to end.

The picture below shows the task frame. The task itself is the vi receiving the dereferenced class. Since dynamic dispatching is needed I ran into problems handing the reference directly to the task. Understandably this blocks all my functions during the active time of the task.

TaskFrame.png

My question would be if there is a method whereas I could hand the reference to the task and still be able to use dynamic dispatching; to only dereference when a member is written.

I hope I could express my problem in understandable way, otherwise I try my best to answer question.

Don't forget I am new to Labview and english ;)

Best regards

Martin

 

Link to comment

The data value reference (DVR) is a pointer to the data (in your case an object). Entering the in place element (IPE) structure using the data value read/write puts a lock on the reference to ensure no one else tries to modify the data. This blocks any other code from using the DVR. So the only way for other code to access the data is to write back to the DVR when leaving the IPE. The only way to use a DVR in the way you are trying is to only access it at the moment you need to, which is not what you are doing. There is a significant time-cost of locking/unlocking the DVR that can make this a very undesirable way to use it.

There are other means to access singleton data in multiple locations. There are action engines, single element queues, global variables... All have their pluses and minuses. However, is keeping the data by-value needed for your application? The compiler is pretty good about optimizing code to reduce data copies (there are good articles on how to improve memory performance on NI's website).

Link to comment

From your description, it doesn't sound like you want to do what you are doing. None of your code in the image has any labels so I can't really understand what bits are important. However, you said you have code chunks which talk to their device and generate data. I would argue that you just want to create N parallel loops and use a more dataflow-y communication mechanism to move data around (queues, events, notifiers, or channel wires). Any process which needs to get the measurement data can subscribe to the queue/notifier/event and receive its own copy of the data.

If you decide you do want to use DVRs, I'd suggest one workaround being that you introduce DVRs within the class rather than around the class. That is, the private data you're setting could be a big DVR cluster and then your task can have finer grain control over when it accesses that data. If it turns out that task just needs to read a copy of the information, it needs to obtain the lock just long enough to copy the data out, and then it can release the lock.

Finally, the property node syntax for data accessors does allow you to operate directly on a DVR, but the access is only as fine-grained as the data access method.

 

Edited by smithd
Link to comment

First thank you very much for all you effort. I attached a small sample project to clarify things.

16 hours ago, Tim_S said:

The data value reference (DVR) is a pointer to the data (in your case an object). Entering the in place element (IPE) structure using the data value read/write puts a lock on the reference to ensure no one else tries to modify the data. This blocks any other code from using the DVR. So the only way for other code to access the data is to write back to the DVR when leaving the IPE. The only way to use a DVR in the way you are trying is to only access it at the moment you need to, which is not what you are doing. There is a significant time-cost of locking/unlocking the DVR that can make this a very undesirable way to use it.

 

3 hours ago, smithd said:

I'd suggest one workaround being that you introduce DVRs within the class rather than around the class

Yes and I am wondering how I can hand the reference into the derived tasks. It was not possible for me to dispatch the object reference dynamically.

I do have a few other ideas reaching from creating a copy of the object to using a reference to reference construct, but both have other drawbacks and might not work.

First I try to find out if the construct is somehow decent, before I spend more time to investigate further solutions. Maybe you could provide me with your expert opinions :)

LVOOP_TaskSample.zip - usage of the Get and Set functions of ClassA and ClassC are not implemented (probably doesn't matter)

Btw. in my first project I have used the messaging system (Clustered Queues with function/type and variant) of NI. Its hard to explain but I didn't like much - thats why I started with OOP.

 

Edited by ToyLab
Link to comment
2 hours ago, ToyLab said:

Yes and I am wondering how I can hand the reference into the derived tasks. It was not possible for me to dispatch the object reference dynamically.

Btw. in my first project I have used the messaging system (Clustered Queues with function/type and variant) of NI. Its hard to explain but I didn't like much - thats why I started with OOP.

If you went for the DVR-inside-class route, you'd need a 'create' and 'destroy' function. Create would initialize the references (and potentially call the parent 'create' method) while destroy would release the DVRs. You would pass the 'create'-d object into the task loop, where it could dispatch normally.

Enum or string + variant is a reasonable way to implement a message system, but it doesn't work as well for just transferring data around. A better fit is using user-defined events, which let you subscribe to several different data types in a single structure. As a nice bonus, the producer of the data doesn't have to be aware of the existence of any consumer -- there could be 10, there could be 0, it doesn't know or care. As a downside, if you're doing difficult processing and nobody is listening to the result, thats kind of a waste of processing power.

 

Edited by smithd
Link to comment
Quote

 It was not possible for me to dispatch the object reference dynamically.

Try an Object in a Cluster for your DVR, rather than an Object directly.  There are special issues around Objects in DVRs that are possibly causing your problems.

Edited by drjdpowell
Link to comment

Thanks again for your suggestions.

To overlay the task.vi it seems that I need at least one dynamical dispatched input, so that if the base class task.vi is called, it knows witch Task.vi from the child class should be executed. I could use an extra input just to determin the correct child class and then find a method to take the correct DVR from the cluster and work with that. To avoid blocking, I would pull the task out of the in-place-element structure and feed the object to the task.vi in the dynamical dispatched input. However I would not use it but take the reference of the object holding all DVRs....

This makes it not realy intutive... but let me play around with it a little mybe I missunderstand your suggestions at the moment :)

Edit: A secound problem might be the selection of the correct reference from the object holding all references ...

Edited by ToyLab
Link to comment

While I was playing around with the references object, I could get it running with a small work around. I attached it in case you are interested. I am now thinking of using the reference cluster as global functional variable. Than all modules run for them self (without interaction) and the top level can easily access the parameters of the modules.

LVOOP_TaskSample.zip

Link to comment
  • 5 months later...

I may have gone a bit overboard with an example but this demonstrates several things: 

  1. Call Asynchronous cannot use a polymorphic member reference. To get around this the TaskMgr class has a static member that takes in a Task Instance and can then call the polymorphic task while it runs asynchronously.
    1. The Start Task.vi demonstrates setting up the static vi reference properly to use the call async node.
  2. The base Task class (and TaskMgr) follow my by-reference design to allow parallel operations. The key note here is that the reference is contained within the class data, as opposed to a reference of the class itself, which allows polymorphism to work correctly.
  3. Two example tasks are provided:
    1. BgTask is implemented as a by-reference design and demonstrates reading its "progress" in a parallel loop.
      1. Note that it only dereferences the class data when it needs to update a value, instead of keeping the reference locked for the entire execution of the task.
    2. SlowTask is by-value, though the Task base class it inherits from is still by-reference for control signalling and status tracking.

TaskMgr.zip

TaskMgr.zip

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.