Jump to content

deploying child classes with common control/UI


Recommended Posts

I am designing an application upfront and am considering what is the BEST way to go….

I haven't really worked too much on the solution yet but I have encountered similar cases before and I think it is a question with pretty general interest, so here goes....

This application (which uses the DSC Module, by the way) will control ventilation doors (not important). Each door (at the moment) has exactly the same functionality, so…

I designed a parent class (VentilationDoor) with child classes for each particular door. (Right now this class pretty much stores data about each door.)

I designed a DoorController class and a UI.

Now my current thinking is to put the child objects in an array and feed the array into a for loop that calls the DoorController and UI high-level methods (using VI server—or alternately deploying these as OPC servers—to get these to run in parallel for the different doors), resulting in an instantiation of DoorController and the UI for each object. Other solutions are possible--it may be a matter of picking the best design pattern. How would you do it? :)

(I have attached a PDF of my current design class diagram but that is probably peripheral.)

Paul

Link to comment

By the way, I don't really need the child classes at this point, since the functionality is the same for each door. (This may change.) For the moment I just have to create multiple instantiations of the VentilationDoor class, where each instantiation has a path to its own shared variable library, etc.

Link to comment

QUOTE(Paul_at_Lowell @ Sep 21 2007, 10:41 AM)

By the way, I don't really need the child classes at this point, since the functionality is the same for each door. (This may change.) For the moment I just have to create multiple instantiations of the VentilationDoor class, where each instantiation has a path to its own shared variable library, etc.

That was going to be my recommendation :)

Link to comment

OK, let me explain a little bit one aspect of the issue.

If I have, say, five doors, each is an object (an instantiation of the VentilationDoor class). Now let's say the doors are all moving and I click a stop button on the UI.... The stop button changes the value of a shared variable.... The controller is waiting for such an event....

I have at least two possible implementations....

1) I have one set of shared variables for a single controller (e.g., commandedPosition, stopMotion) and a separate shared variable that indicates the axis (axes) for which these commands apply. (In this solution I would pass five VentilationFan objects to a single controller.)

2) I have one set of shared variables for five instances of a controller. (In this solution I would run an instance of the controller for each VentilationFan object.)

Which is better? (Or is there another way?)

Method 2 has some appeal to me because the controllers can run independently. Unfortunately, determining which shared variable triggered requires some inelegant coding since the shared variables must be unique (either by name or by library name) for the similar message for each door....

Paul

Link to comment

QUOTE(Paul_at_Lowell @ Sep 22 2007, 01:42 AM)

Which is better? (Or is there another way?)

If I understand you correctly, there is a better way - ditch the shared variable, create a Stop Door method (VI) and simply call that VI on all instances when the user presses the stop button. That's the point of OO.

P.S. It doesn't have to be a Stop Door VI, it might be Change Door State or something similar.

Link to comment

QUOTE(yen @ Sep 22 2007, 01:10 PM)

If I understand you correctly, there is a better way - ditch the shared variable, create a Stop Door method (VI) and simply call that VI on all instances when the user presses the stop button. That's the point of OO.

P.S. It doesn't have to be a Stop Door VI, it might be Change Door State or something similar.

I am using the shared variable for network communication and (in general) for logging and alarming. It is unfortunate from this perspective that the shared variable libraries are external to classes, but that is the case. Note that I can create and deploy a shared variable library associated with each class and even subscribe to events for those specific shared variables programmatically. I can strip the path programmatically for an event to remove the library name and just return the variable name (not difficult, but inelegant).

Also, the stop button(s) may apply only for a specific door. (That will usually be the case, in fact.)

Link to comment

I'm still working on this. :)

First, let me say something about why we wish to use shared variables....

1) We are developing a distributed system. The user must be able to press a button on a UI to affect a controller in another location. Networked shared variables are ideally suited for this purpose.

2) We want to follow the MVC design pattern--that is, we want to separate the domain model code, the view (UI) code, and the controller code. To subscribe to a UI button event directly in the controller's event structure, the UI and controller must be part of the same VI, which transgresses the MVC principle. This is not a moot point. By implementing an Observer pattern with NI-PSP and shared variables, we separate the view and controller so that...

a) we can modify the view or the controller separately without affecting the other.

b) we can create different view instances (different UI versions or programmatic controls that write to the same SVs) and use these with the same controller. (This functionality is necessary in our application.)

These are tremendous advantages!

To control a single device (e.g., one door) the desired implementation of the MVC principle is fairly straightforward (see attached .pdf file for a simple but incomplete picture):

1) The user presses a button on a UI (e.g., to command a new position)

2) This results in a value change of an associated shared variable (simplest implementation: bind the UI control to the shared variable)

3) The shared variable value change triggers an event in the controller (not completed in the .pdf).

4) The controller logic determines what to do and calls the appropriate domain method.

(Similarly, we write values from the domain or controller to shared variables to update the UI--for example, to show the current position.)

As I said, this is fairly straightforward when we are controlling a single device (e.g., one door). Now what if we want to control multiple doors (multiple objects--instances of the class--for multiple identical doors)? Shared variables are precisely shared between objects! So when we change the value of a command position control on a UI, should we:

1) bind each command position control to a unique shared variable (e.g., 'commandPositionDoor2')--which requires creating a copy of the shared variable libraries for each door (not exceptionally difficult but copying the libraries is manual, this increases the number of shared variables, and this solution can get quite complex if we have more than a few doors), or

2) should we link all command position controls to a single shared variable (simply 'commandPosition') but also pass a door name or door object as part of the shared variable (i.e., use a cluster)? (This is an intriguing idea but I haven't figured out how to make this work yet. It is especially complex because the individual door controllers could very well be in different states at any given time.)

3) or is there another, better way to deploy multiple instances of the UIs and controllers that will guarantee independence for each without a lot of code or variable copying?

This is not the first time I have encountered this issue. I think that its resolution will offer a quite useful paradigm for distributed control. I am guessing someone somewhere has solved this (the problem is not unique to LabVIEW), but I haven't yet found such an answer!

What do you think?

Paul

Link to comment

Hi,

It seems to me that the design paradigm for distributed control involves message-passing, not uber-global shared-variables. The shared variable is, first and foremost, a global variable. It's useful when slapping something together quickly, but it can easily become a problem for scalability. And scalability is the problem you're going after!

Multiplying the shared variables for each door you want to control seems to be pretty much a no-go.

With a single-mailbox system (one shared variable), how will you guarantee that each message is received? Will you leave each message in the mailbox for a minimum amount of time? Will you wait for a confirmation that the message has been read? What if one door-controller locks and never gets its message?

If each entity in the system has its own "mailbox" that works without extra programmatic configuration (i.e. without having to add shared variables all the time,) you get the advantage of multiple shared variables with the advantage of the single-mailbox system. That's what you'd get with message-passing. It will probably be more work upfront, but once it works, it'll be done.

Link to comment

QUOTE(Paul_at_Lowell @ Sep 22 2007, 03:41 AM)

By the way, I don't really need the child classes at this point, since the functionality is the same for each door. (This may change.) For the moment I just have to create multiple instantiations of the VentilationDoor class, where each instantiation has a path to its own shared variable library, etc.

I often use an Abstract Base Class and almost always implement a Simulated_X Child, I.e. SimulatedDoor.

This class could have a UI to show the door status. You could have one UI per object or one UI for all Simulated Door objects.

//Mikael

Link to comment
QUOTE(Guillaume Lessard @ Sep 25 2007, 01:29 PM)
Quite right!I do think, though, that the networked shared variable built on NI-PSP does offer a way to do messaging, and NI-PSP does guarantee message delivery (though this means I have to send the message to the proper endpoint, which is your quite valid point).My most recent thought is to use DSC's "processes" and deploy shared variables in the processes (one process for each door). This allows me to isolate unique messages, and I can create the processes programmatically. There are rather a few messages but ultimately that is necessary whether I break the messages into one or more layers or not. The process approach seems to me to be at least promising(?!?!?)....QUOTE(MikaelH @ Sep 25 2007, 03:57 PM)

I often use an Abstract Base Class and almost always implement a Simulated_X Child, I.e. SimulatedDoor.This class could have a UI to show the door status. You could have one UI per object or one UI for all Simulated Door objects.//Mikael

I have to admit I don't entirely understand your point. I did decide to go more abstract (I added a generic controller class, for instance). The UIs as I designed them won't be within the door classes, but the door classes will be part of the UI class data, which I think achieves essentially the same thing, but maybe it's fairer to say I just don't understand your message. :) Would you mind explaining a bit more?Paul

Link to comment

Here's something that ALMOST works....

I can deploy a library, retrieve its shared variables, create a new process, and add identical shared variables to the new process. (See attached picture.)

Issues:

1) The example duplicates the shared variable names and types (sort of--see 2) but not the other properties. This however, should not be a big deal because I should be able to read the existing shared variable properties and write those to the new shared variables.

2) More troubling is the coercion dot on the DataType input for the "Create Shared Variable.vi". This enumerated type input has only the values u32bitfield, bool, double, and string, even though shared variables can be of many more types! I'm not sure yet what the implications of this limitation are....

Paul

Link to comment

QUOTE(Paul_at_Lowell @ Sep 28 2007, 11:45 AM)

Here's something that ALMOST works....

Well, that didn't work all that well, because anything beyond a basic type doesn't translate well. (Shared variable events trigger but indicators bound to variables of any but the basic types didn't update correctly, I suspect because of the extra attribute information.)

The good news is, I now have something that works. Credit goes to NI support (thanks!) for suggesting the following implementation:

Programmatically copy an existing library and its contents to a new library using the "Copy" method from the Advanced File Functions palette. (See attached image.) Deploy the new library. This copies any type of shared variable. Hence we can use the DataSocket API to change the URLs programmatically, even for custom data types. Even better, creating the new library does not alter the project, so bookkeeping is simplified. I anticipate this will address my concerns and I am looking forward to utilizing this implementation in my actual project.

Paul

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.