Jump to content

PaulL

Members
  • Posts

    544
  • Joined

  • Last visited

  • Days Won

    17

Everything posted by PaulL

  1. 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.
  2. 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....
  3. 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).
  4. 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....
  5. 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.
  6. 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.)
  7. 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
  8. 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?
  9. 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.
  10. 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....
  11. 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.)
  12. 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.
  13. 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.
  14. 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
  15. 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
  16. 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.)
  17. 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?
  18. 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
  19. 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
  20. 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.)
  21. 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
  22. 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
  23. 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.
  24. 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
  25. For the record, NI support found the following: 1) The problem only occurs if the method that calls the DataSocket function is wired with dynamic terminals. Rewiring these terminals as nondynamic (if possible) gets around the problem. If 1) is not possible (e.g., dynamic terminals are necessary) then 2) use a wrapper VI around the DataSocket library VI. (This is a variation on 1 since the wrapper won't need dynamic terminals.) I implemented such wrappers with the same connector panes as far as possible as the DataSocket library VIs (I had to use variants as necessary, with a type conversion on the output of the read wrapper) and so far this has worked for me....
×
×
  • Create New...

Important Information

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