Jump to content

PaulL

Members
  • Posts

    544
  • Joined

  • Last visited

  • Days Won

    17

Everything posted by PaulL

  1. OK, I will pose an OOP design patterns question and see if anyone wants to discuss this: I spent some time last night studying the MVC compound pattern in ch. 12 of Head First Design Patterns. My implementation of the pattern without knowing exactly what I was doing isn’t too far from what the book describes (although I missed some key ideas that I plan to implement next time) but the relatively simple example in the book leaves me wondering about a couple things. The most important is: In the book example the controller determines when the view’s components are grayed out or activated based on user events. The view does respond to changes in the model but only to update values, not to activate or deactivate view components. (In other words, the view is just a simple data listener.) Often in a real control application, however, like the one I am doing--and most of us using LabVIEW do, the view components should be grayed out or activated based at least in part on the state of the model, not just user events in the view. For instance, in an application that controls a motion axis the user might press an initialize button on the view but the move to position button should only be active once when the axis initialization completes (a change in the model’s state). In my solution I created a separate controller (a UI controller) that acts on state information from the controller. Probably the two controllers could be combined. (I am actually using three controllers, though, because I have two models--one for a moving axis and another for an acquisition device, but that is probably not necessary either.) Who knows how this “should be” done?
  2. OK, this is one I decided I should figure out eventually, and eventually is now here! I have a number of LVOOP classes in my current project that I want to reuse in other projects. Accordingly, I want to move these classes to the user library. How do I do this effectively (without breaking the calls to the member VIs from other project files and without saving one VI at a time)? I have done this a couple times with small classes, saving each VI in succession, but I think there must be an easier way I'm missing....
  3. QUOTE(Robbie Gehbauer @ Oct 15 2007, 12:57 PM) OK, I have tried using the method you describe the last couple months. I am still wishing for a streamlined path (i.e., new override method template) with LVOOP. In fact, it is the number one item on my personal wish list for the next release. The "create space" method requires resizing the window and it is essentially impossible to create space each time such that one doesn't have to move the controllers and indicators around to make them line up vertically with the default case structure I inevitably end up copying into the VI. This is an inconvenience, admittedly, but having a suitable template for override methods is probably the one thing I most miss among the many other nice LVOOP enhancements in 8.5. (I'm pretty excited about LVOOP and I use it all the time.) (The create space method just takes a while for each override method....)
  4. Ah! It turns out I had missed a subVI, which in turn called a VI in another class, which in turn... so at least most of the dependencies should have been there. (I'm not sure how the disinherited state classes fit in, but they must somewhere.) :-) I started from the last "known good" version and carefully worked in steps until I got to a version where I was always prompted to save each time I opened the project and immediately closed it. This time I promptly 'fixed' the code to make it executable, even if it didn't do what I wanted, and LabVIEW stopped prompting me to save. So in this case, at least, the save prompts weren't anything to be alarmed about--NI Support was correct there. NI Support filed a CAR on the File...Save As issue. As far as the starting topic of this thread, though, mballa's example seems to apply.
  5. QUOTE(mballa @ Dec 13 2007, 12:13 AM) Hmmm....yes, I think there is something like this going on, although it's quite confusing (starting with I'm not sure why the other methods are in memory at all).In the example you have it seems logical to me not to be able to remove the method from the class if both methods would remain in memory. In my case I don't know what is keeping the methods in memory, but there are such methods when I open the project. What I have at the moment is a much-pared down version of the full project. In my first post I show just the class in a project view. If I open the chopped-up project (I'm trying to isolate a larger problem) I have only this class remaining in the project, but the three methods include controls and indicators on a context class and invokes methods in that context class so that class is in memory. (I removed all the data and methods for that class, however, in the chopped up project--it used to have references to this top-level state and its now disinherited substates, though. I implemented the state design pattern. The first version was quite successful but I needed to add states because of functionality changes. More confusingly, the disinherited subclasses for the project class and their methods remain in memory. I'm not sure why that is, but maybe there is some link in the now-broken code that retains some memory of these?)QUOTE What always works is removing items from a class by editing the .lvclass file with a text or an XML editor. "Actually, I tried that. LabVIEW was too smart and put them back when I opened the project. I'm pretty sure LabVIEW was using the edited copy....QUOTE What's the CAR for the open issue? I can look at the source code there. I don't think there is a CAR. (Maybe this specific behavior isn't really broken--now I'm not sure. The larger NI support issue is 7178674. The larger issue involves two things--LabVIEW suddenly has started prompting me to save VIs it recompiles every time I open and close a project--which in my past experience has gone along with a corrupt project file, and a build for distribution crashes LabVIEW--or alternatively a save as on the project results in an out of memory error. I was adding, renaming, and editing subclasses and their relationships when the first problem occurred. I then found out the latter has been around a while--I just hadn't had a need to export the project before. It is possible, I suppose, that something like the following is going on, that relationships are being maintained in nonupdated nonfunctional code (????), but that is just a hunch. I haven't been able to determine what is going on.)PaulOK, I edited the chopped-up project still further.The three methods in the VentilationDoorState.lvclass have a couple subVIs that were formerly methods of the VentilationDoor.lvclass (the context class for the state pattern).1) If I delete the subVIs in the three VentilationDoorState.lvclass methods then only the VentilationDoor.lvclass (it's empty--no methods or data) remains in memory and all the former assoications go away and then I can remove the three VentilationDoorState.lvclass methods as expected.2) If, on the other hand, I leave a single subVI in the VentilationDoorState.lvclass methods (and this subVI used to be a method of the VentilationDoor.lvclass, so that it has a VentilationDoor.lvclass object in and out), even if this VI does nothing at all, then many things that used to be connected to VentilationDoor.lvclass stay in memory, even though there currently is no association whatsoever (or shouldn't be--I really can't see why any of these formerly related items should be in memory at all), so that LabVIEW keeps recompiling the methods and I can't export the project.I'm not entirely sure I'm not just confusing myself (maybe there is something going on of which I am just not aware), but this really doesn't seem to be expected behavior....
  6. QUOTE(Aristos Queue @ Dec 12 2007, 05:54 PM) Hmmm... and what's stranger is that I disinherited all the child classes and I still can't remove the methods.... Actually I stripped down the project to just this class. The child classes still show up under the items in memory (which they shouldn't unless I am totally missing something--well, in all fairness, one of them should because a subVI references it, but the others shouldn't). I tried to save the project but LabVIEW gave me an out of memory error. I do have an open support request with NI support on the larger issue to which this relates and NI Support has the entire project (much too large to post here).
  7. I somehow ended up with methods that I am unable to remove from a class. Generally VIs in a class have an option to remove from project either from the larger project view or from the individual class. These last three seem to be an exception. (If I open the front panels of the VIs the title bars show the expected class:method format....)QUOTE(Paul_at_Lowell @ Dec 12 2007, 05:01 PM) OK, I think I see. These are methods in what is functioning as an abstract class (they are overridden in child classes) so I can't remove them....
  8. I'm glad I found this topic because I just encountered the same thing. A couple notes: The child classes stayed in memory until I deleted the parent even though I changed their inheritance so they no longer inherited from the parent! Moreover, when I opened the .ctl file for one of the (disinherited) child classes in the Items In Memory folder I was not able to close the .ctl window.
  9. What does a black border on a class (LVOOP variety) indicator mean? (Usually the border is gray, as in the first example, but sometimes the border is black--see the second example. I vaguely remember reading somewhere what this means, but I can't remember where.)
  10. Tom, I just took a quick look at your post, but from your description this sounds like a great opportunity to apply the "strategy" design pattern. Then a particular "save data" algorithm would operate depending on which type of diagnostic data applies.... Ch. 1 of Head First Design Patterns has a good example.... (You may also apply the template method pattern--ch. 8--as well in the larger context of taking and storing data; and the adapter pattern could apply if you wanted to fit everything into one format, but I don't think that is what you have in mind....) Paul
  11. I am working on a software timer that I think does what you want. We want to run a timer in its own thread. (In my case I am using the state pattern. I start a timer and can transition to the next state. If a timeout event occurs the current state handles the timeout event appropriately.) In other words, I am constructing an asynchronous controller. Fortunately the "Elapsed Time" express VI runs in its own thread. I decided to start it in its own thread and update signals (in my case networked shared variables) based on inputs from the caller. I use VI server to change the values of the inputs and then execute my timer VI with "Wait Until Done" = False. I will pass in the timeTarget and the signal paths from my application when I call my startTimer.VI. [Notes: The result is good and clean, although I have to integrate it fully in my application still. The solution uses some intermediate concepts, but then the problem is nontrivial. I am using DataSocket because I want to set the shared variable targets programmatically. Also, the DataSocket Read and Write wrappers just include the DataSocket Read and Write VIs and use the same inputs. This is only necessary because of a bug in 8.5.] Are there any comments or suggestions?
  12. My thoughts. Part 1: Making tabs visible and invisible as you have done certainly works, and works well. You can also change the active tab programmatically. Another method is to use subpanels (or multiple panels) for your UI. There are lots of options depending on exactly what you want your UI to look like. Alternatively one can change the visibility of individual controls and indicators dynamically (may not need tabs). Part 2: It sounds to me like you want your UI to change appearance depend on certain events. You can use a state machine with an event structure (I suspect this is what you are doing) or take advantage of the OOP state pattern (305ff in Design Patterns by the Gang of Four--Gamma, Helm, Johnson, Vlissides, or see the chapter with the gumball machine in Head First Design Patterns). For the latter you could, as one method, include control references inside the UI object you pass to the states. (Caveat: Control references aren't the highest performing way to update your UI, so if you want to write lots and lots of data this way you may wish to consider another update method. If you are just changing the visibility of controls, though, control references offer a convenient API.) If you only want to update the tab control you may find you just want to stick with a simple state machine in the UI's block diagram and this may in fact be the best solution.
  13. QUOTE(Robbie Gehbauer @ Oct 15 2007, 10:33 AM) Yes, I had realized the ambiguities just as you describe with combining the Call Parent Method. I think it's simpler to add a Call Parent Method then make the other changes we have been describing. I guess "ideally" I'd want to be able to select a template from the shortcut menu, either from the menu itself or via a pop-up dialog. Personally, I like the blank case structure implementation, but then I like that template in general and I am really glad it is readily available in the other templates available from the shortcut menu. (Thank you!) I appreciate AQ's comments about error handling (and have pondered the issue myself but not too deeply), though, and realize this may not be the most useful general solution. I happen to like it now but others may not use the template much (readers please comment!) and there may be a better error handling solution. (I'm not averse to adopting a better way!) At the moment I find the blank error strucure the most useful template for my applications so I like to start there....
  14. QUOTE(gmart @ Oct 15 2007, 08:04 AM) Hmm.... There are times when each would be useful, to be sure. In my experience a blank case structure (the second option you describe) is more frequently applicable by far (probably by a factor of 20), so I'd much prefer that option. (I consider this a must have for LVOOP. An option to create a version with a copy of the parent's code would be a nice to have alternative in addition, but just a nice to have.)
  15. Generally, we can create an override method for extension or replacement of the parent method. (The parent and child methods must have dynamic terminals and identical--except for the class objects--connector panes.) In LabVIEW 8.5 (nicely improved from 8.2.1 in the options availabe!) when we right-click on a child class and select New... VI for Override... from the shortcut menu the VI template used is better suited for extension. (There is already a call to the parent method. One can add further behavior to the block diagram.) The question is, what if we want to create an override VI that we will replace the parent VI method's functionality? In LabVIEW 8.5 we can do this as follows: 1) Create an override VI as above, delete the call to the parent method, and add the functionality to the block diagram. (Unfortunately the template isn't set up as nicely for this as it was for the original VI. We have to add space and a case structure to get to the same place.) 2) Create a New... VI from Dynamic Dispatch Template in the child class. This is probably the easiest path currently. The drawback here is that the connector pane may very well not be the same as it was in the parent VI. (In particular, if we had any inputs or outputs that aren't part of the default template in our parent VI, then the connector panes won't match.) 3) Save a copy of the VI to replace from the parent class to the child class. Unfortunately we have to replace the parent class objects manually with the child class objects, update the labels (a separate step), and remove any existing code we don't want. These are the paths I know to accomplish this. Solution 2 seems to be the simplest solution for me, but it's not great. Hopefully the next LabVIEW release will have a New... VI for Override for Replacement option that will combine the best parts of options 1)--ready-made connector pane and 2)--code-ready template with empty case structure. Personally I would prefer this for the override template in any case. I'd like to have the case structure and add the call to parent method wherever it is appropriate.
  16. I've encountered some situations that are somewhat similar: 1) Symptom: Open a saved project. Close the project. LabVIEW prompts to save the project. The reason: "An attribute of the project was changed." Cause: Open a project with a virtual folder. Convert the folder to be autopopulating. Save and close the project. Reopen the project. The above symptom occurs. Impact: Inconvenience. Solution(s): 1) Ignore the prompt and trust that everything is really OK. :-( 2) Use virtual folders. (That's my approach for the moment.) Known issue (CAR 4CKEQ1F2). 2) In a recent project I decided I wanted to rename a class. Conscious that I should do this through the LabVIEW project interface and not just on disk, I used the "Rename" dialog. I went wrong, however, when I changed the directory name (in the file dialog) to match the new class. LabVIEW couldn't find the original library (I had messed up the path) to delete it so I had all sorts of project file issues and it took me a while to clean up all traces. (I should have created a copy of the class in a new directory first, then deleted the original class and its folder.) 3) Still more recently, I decided I didn't need a class anymore so I deleted the virtual folder in which the class resided, figuring this would remove the class from the project as well. The class ended up in the dependencies folder. (Perhaps this was complicated by the fact that the class inherited from a parent class?) Again I had a hard time getting rid of it from the project (even after I deleted all callers) and kept getting the "An attribute of the project has changed...." message after saving, reopening, and trying to close the project. I wasn't too far along in the project so I just started over.
  17. QUOTE(Paul_at_Lowell @ Sep 28 2007, 11:45 AM) 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
  18. 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
  19. Ah, I see!One can use an application reference to an invoke node to return a reference to a project (picture attached).QUOTE(Paul_at_Lowell @ Sep 27 2007, 03:30 PM) OK, this time with the picture. (I can use a .png file.)
  20. I want to use the "CreateOrAddLibraryToProject.vi" from the vi.lib\Utility\Variable directory. An input I need for this VI (and elsewhere) is a reference to the current library. How do I create such a reference?
  21. 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
  22. 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
  23. QUOTE(yen @ Sep 22 2007, 01:10 PM) 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.)
  24. Which service does the error dialog associate with the error? I received this message associated with the NI Domain Service when trying to uninstall 8.2.1 as part of the 8.5 install process. One can try to stop this service manually (didn't work for me, possibly due to some still existing connection with an OPC server). In the end what did work for me was to remove the NI software I had installed and reboot. The NI Domain Service no longer appeared in the services list. At that point I was able to install 8.5 successfully. Paul
  25. 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
×
×
  • Create New...

Important Information

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