Jump to content

PaulL

Members
  • Posts

    544
  • Joined

  • Last visited

  • Days Won

    17

Everything posted by PaulL

  1. If I follow your description, each cylinder has the same properties (but with different values) and the same behavior. If that is the case, then the application should have 5 instances of the same Cylinder class, not multiple Cylinder classes. The application will write unique values of the properties to each object instance. If, on the other hand, the cylinders have unique properties or methods, consider subclassing as an option. (Subclassing is one option and makes sense if we are talking about different types of cylinders. The Strategy Pattern is an alternative to subclassing when an algorithm is the thing that varies.)
  2. Jerome, Well, a couple things: 1) The application need only use the events API. It will not need the queue API (although events arrive in an event queue). 2) I do not suggest sending state modifications to the controller. I use a Model-View-Controller (MVC) architecture. The View (or another entity) sends data (which may be commands) to the Controller, which handles these data events, telling the Model what to do according to the situation; this may include instructions to change state. The model publishes its state to the view. A full MVC implementation from scratch may be a little much for now, although I think it is a great solution if you can invest the time. Otherwise you can use some of the hints. One of the hints (a huge hint, actually) is to use an interrupt event as one of the events to send to the controller. I actually use the State Pattern to implement a true state machine within MVC. If you want ro read more on the State Pattern you can look at https://decibel.ni.com/content/docs/DOC-23603. It might be a bit much to digest right now, but maybe some of the principles will be helpful. Good luck! Paul
  3. What you have described is good so far. The one thing I would add is that in practice your controller should execute in very small steps. What I mean is that a cylinder move could take a long time. You don't want the application to "stop" while the cylinder is moving. The cylinder move should be part of a loop. (The system may be in the same state during the entire cylinder move.) Anyway, you are going in the right general direction. OK, one more thing: if you use a Model-View-Controller architecture, the Cylinder is a model element commanded by the Controller. Model-View-Controller is the architecture I use. It does require a knowledge of object-oriented programming and design patterns to implement well from scratch, though. It is well worth the effort, I suggest, if you can invest that effort.
  4. Dermot, I think the approach you are following is not the way to go. If your application API deals with Vehicles, use an array of Vehicle. If the API expects Trucks and Cars, instead of an array of Vehicle, use a cluster containing a Truck and a Car. Paul
  5. My general recommendation is at the application design layer to put items into an array collection only when you intend to use an array interface to the items in that collection (i.e., iterate over the items in the array using an interface defined on the class of the array--the parent class, Vehicle, in your example). Now it may make sense in your example for the parent to have this definition. (In your example, then, each Vehicle would have a capacity. A Vehicle child object would define a nonzero value, while a Car object would define a zero value.) In this solution capacity would be an attribute of Vehicle, although dynamic dispatch methods on the child classes may define the values, calling a public accessor method on Vehicle. Note that it is perfectly logical to define static methods on Truck and Car for use before adding them to the collection.
  6. Jerome, There are many possible points for comment here, but I recommend that eventually you adopt a design that separates the user interface and its behavior from the application business logic. That is the high-level architectural answer. My suggestion above does not completely eliminate execution competition of the sort you describe, however. An event timeout is a bit troublesome in that the timeout occurs only if the specified timeout time has elapsed since the occurrence of any event the structure handles. (If there are enough events, the timeout may never occur.) I prefer not to use event structure timeouts for essential control loops, therefore. One approach I use is to have a parallel timed loop generate user events that my primary event-structure-in-while-loop handles. This is an improvement since "interrupt" events generate at regular intervals, and the event structure handles them as soon as possible. There can still be some jitter, however, from the arrival of competing events, unless one examines and sorts the event queue (which may be possible in LabVIEW 2013, but I haven't attempted this yet).
  7. Has anyone actually configured Git to work with LVCompare or LVMerge? (I've seen some threads on various boards saying it "should be something like" <x>, but it isn't quite <x> and I haven't found anywhere that someone has actually described it successfully.) Notes: I know how to configure TortoiseSVN to work with LVCompare and LVMerge. My new employer is moving to Git (some good, some bad in that, in my opinion, but that is another topic). I have been using Git for a few months now with reasonable success and decided I should try to set up graphical differencing. [OK, in practice I've rarely used graphical differencing for any practical purpose, and graphical merging probably never for any actual code.] Anyway, I think I've learned some things trying to figure out how to get this to work--but I don't have it working yet. I use the Atlassian SourceTree client at present (and sometimes the shell). For the purposes of this discussion it might be more helpful to share the relevant text in the .gitconfig file (and any associated scripts, if necessary). As others have mentioned, Git itself (SourceTree similarly) does not distinguish which diff tool to call according to the file extension. This seems like it could be a significant drawback. What I have tried: I added this (and variants of it) to my .gitconfig file: [difftool "LVCompare"] path = C:Program Files (x86)National InstrumentsSharedLabVIEW CompareLVCompare.exe keepBackup = false trustExitCode = false[difftool "LVCompare"] cmd = "C:Program Files (x86)National InstrumentsSharedLabVIEW CompareLVCompare.exe" "$LOCAL" "$REMOTE"[diff] tool = LVCompare SourceTree reports errors on start-up, so don't use this! Lol! Maybe I need to add a script, too. I'm not sure that I'm really all that close, honestly. [bigger question: Why is this difficult in the first place?]
  8. Martin, I'm very interested to hear what you learn in the process.
  9. Martin, I think your goals and approach (subpanels) are exactly right. I also agree that you should be able to start a view and it should reflect the present state of the system. All this we do in our system. The views are separate applications from the controllers, and neither requires the other to be running. When we open a higher-level view, for instance, the subpanels associated with lower-level systems automatically reflect the state information of the low-level systems. This allows the user to configure the display to use the views appropriate for the task at hand. (We do not add the strange constraint of requiring all views to run at all times.) What makes this possible for us is that we use network-published shared variable events, which have the special property that when you subscribe to them they fire an event with a value corresponding to the most recent value. (At first I thought that was not desirable, but it turns out to be quite handy because it specifically addresses the problem you are wanting to solve.) Of course, you will have to evaluate whether networked shared variables are appropriate for your application. (Unfortunately, the shared variable event registration requires the DSC module. That seems counterproductive to me.) I'm sure it is possible to build something else that would do the same thing (especially if one could use an off-the-shelf messaging system that has this functionality as an option), but that would seem to be a nontrivial exercise. Paul
  10. OK, but that means if you click on the new class (or class method) and look at the history, the version control provider will only show the history of the class (or method) after the rename. If you need the history for some reason (and there are reasons, for instance to research when you added a feature or fixed a bug, even if you don't want to revert to an old revision), that's not a good thing. It really depends on how often you need to look at the history. For me it's not a major issue, but it is an issue (I consider it to be clearly a bug), and, for the record, I think NI should fix the issue (properly deleting from disk in projects: http://forums.ni.com/t5/LabVIEW-Idea-Exchange/Restore-quot-Delete-from-Disk-quot-in-project-Files-view/idi-p/1270866). Full integration with version control providers absolutely requires it. In the meantime I will hope for the best with Git's auto-rename-detection or Subversion's repair move, although these are don't always work. Paul
  11. I'm not aware of any code I have that uses two event structures. I do occasionally have two loops, one of which includes an event structure, and another that generates "interrupt events" that the event structure handles. Most often (but not always) at least one of these is in a subVI. So, on the first take, it would be plausible from my point of view to impose a limit of one event structure per block diagram, but it seems there would need to be a very compelling reason to impose such a restriction.
  12. Small (on the order of 3 to 6). Yours are larger? I find it important (for many reasons!) to follow an iterative process, involving daily stand-up meetings. If the team members know and agree on the focus of each team member every day, it makes it much easier to avoid stepping on each other's toes.
  13. Well, we are at the moment using a local server as the central repository, not Bitbucket. I think I favor the Gitflow workflow over a Forking Workflow for our purposes, but for your purposes you may find the opposite (and you have actually tried it, while I haven't yet!). I completely agree that "breaking tasks and features up into small chunks and committing often will help resolve some of these pain points." I think that careful planning can remove (completely?) the need for merge operations (when merging a branch to the trunk, for example) to merge code within files. (Hopefully merging will just add/delete/replace files.)
  14. Following up: In post #11 I talked about renaming files and source control management. (How are people doing quotes on LAVA these days?) Anyway, I found out from our Git expert that Git is smart enough to recognize renaming automatically. I've checked in a couple renamed files and, sure enough, Git recognizes that the new file replaces the old, so that it is possible to track the full history. That helps!
  15. I've been using the Atlassian SourceTree client (free; Windows and Mac--no Linux support at this time) and the more I use it, the more I like it. (The use of the Pageant client is a bit clunky, but not really a problem.) Anyway, Atlassian's Git Tutorials are quite good, and include some helpful sections on workflows. I was quite impressed with the Gitflow Workflow section, and I hope to implement that approach (I haven't gotten that far just yet) in the near future. This workflow seems to be a logical, manageable, and consistent approach to handling development in branches and versioning (and seems to be applicable to LabVIEW, with the caveat that I recommend avoiding merging in LabVIEW with the present tools*). I highly recommend taking a look at that. I would include the links if I could, but since those are disabled at the moment, you will have to do a search. I'd like to know what you think of it. *I think the merge functionality itself is pretty good, but the API--how it links to the version control provider--is lacking, in that it doesn't cover some very common use cases (I've written an ad hoc wrapper to trick it), and in that it can take a very long time to load the appropriate files sometimes. My guess is that it wouldn't require all that much additional effort to make this a really useful tool (I think what is there is really pretty cool!), but with the current interface, it is rather useless in practice, and I can't remember ever using it to accomplish any meaningful merge task. I'm glad it exists, but I wish it would do more. Comments on this, LAVA folks? Does anyone merge LabVIEW code routinely?
  16. I have LabVIEW 2012 32-bit and 64-bit installed on my machine. I have a project I presumably opened in both versions at one point or another. When I open this in LabVIEW 64-bit not the project shows the vi.lib files referenced reside in the 32-bit directory, which obviously isn't what I want. I'm about to install LabVIEW 2013 and want to avoid this problem now. How does one address this? Virtual machines? Never mind! I was confused. LabVIEW references the correct vi.lib files for the version. My mistake.
  17. I always rename a class from a project, either using Save As... or Move on Disk.... In either case I also specify a new destination folder with the new name. Methods associated with the class I then move in the project files view. Yes, as mentioned above, it is necessary to delete the old, now-empty folders outside LabVIEW (from a file explorer application), unfortunately. (NI changed this for LabVIEW 2010: see "Restore 'Delete from Disk' in project Files view" topic in the Ideas forum for more details.) With TortoiseSVN (i.e., outside LabVIEW) it is possible to specify that one file is the rename of another, so that the new file will keep the old file's history. I recently joined a new organization that uses Git, and I haven't investigated how to do that with Git yet. I use the LabVIEW IDE's search and replace functionality to update the icon names on the object control and indicator names in the class methods. This is fairly straightforward, since it is pretty easy to select the VIs in the class for search scope. The process works well enough for me with too much trouble (although I don't always end up being able to link the new file to the old history), but clearly this requires more effort than it should. (I'm reasonably happy working with TortoiseSVN outside the LabVIEW IDE, but fully capable integration with source code control from the IDE would simplify these steps and reduce errors.) A few years ago someone posted a tool on LAVA designed to facilitate renaming of classes. I haven't tried it myself. This discussion points out areas where the project (failure to delete folders from disk) and version control integration (inability to rename files effectively--which requires the ability to delete folders) are incomplete in the LabVIEW IDE. It seems clear to me that a complete solution integrated with the IDE (clarification, a complete integration with external source code providers) would be quite beneficial. (OK, the fact that some people are seriously suggesting not to rename classes is a pretty big red flag here.) When it comes to renaming classes used in multiple projects, there are fundamental limitations, since an unopened project does not "know" what happened to a class also used in another project. If one can reasonably create a project with all callers, that works, but that isn't always practical. Putting classes in project libraries helps (the library, shared between projects, tracks changes internal to it, instead of multiple projects), where this is appropriate.
  18. If I understand correctly, then DeviceContainer.GetDeviceByID() functions as a factory method. (My preferred implementation of such a thing is via the Factory Method Pattern.) We also use an Enum (strict typedef, actually) as the input to our factory method, which input specifies the class to return. As you say this makes the method specific for the component. We have analyzed the trade-off and, to this point, we have decided to keep the benefits of the Enum on the one hand, and make a specific instance of the Factory Method Pattern for each component on the other hand. (This isn't a problem for us, although I do wonder if we might realize an advantage if we could figure out how to generalize this. I don't think there is a way, though, because that's how Enums work--and that's actually why we use them, after all.)
  19. Hmm... if you think this is important and you want NI to make it a priority to fix it (I know I do), please let NI know, if you haven't already done so.
  20. See the thread 15014-dirty-dots (can't add the link, as you know). (There is a list in post #3 there.) The system VIs issue is probably the most important.
  21. (Replying to post #10): Thanks for including the diagram. That helps, and admittedly it looks much like I thought it would (which is a good thing). I would like to pursue my earlier question regarding how the application uses the queries. In particular, for actions (or even queries, if these are needed) one can implement a null strategy that, in the event that something doesn't apply, does nothing and returns a suitable value. If the intent is to sort the items in the view so that the application only lists the relevant items on which one can perform an action, I think reflection could be useful. I'm getting ahead of things, though. Maybe you can explain how the application uses queries?
  22. I agree with shoneill that thinking in terms of composition is helpful. In particular, the strategy pattern might be quite useful in this case, although I'm having a hard time understanding the application's intended use of these calls. (One can use the strategy pattern to eliminate the unwanted case structures, certainly. I'm not sure why the application is invoking these methods--in particular the queries--on file in the first place, though. It may make perfect sense, but I would investigate that first if I were reviewing the design.) For the record, I would welcome the introduction of Java-style interfaces into LabVIEW, but I think multiple inheritance would cause more problems than it would solve. My experience to date is that when I think "multiple inheritance would be helpful here," I end up solving the problem by a redesign that is in the end much more robust and easier to understand than a multiple inheritance solution could ever be.
  23. Well, there may be other issues, but if the array is large, then using, for instance, Build Array to add each element each time will result in memory issues. Even if the final array size is unknown, it is better from the memory perspective to initialize a "large enough" array first and replace elements thereafter. If the application fills the array, it can create a new array.
  24. Hmm..., well, I guess I would make the accessor methods in question public and address the question by setting up the architecture (e.g., by composition--note that I use by value objects only), but that isn't guaranteed to be safe. (For your application, what is the likelihood of misuse happening?) Maybe package scope would be helpful in this case, if LabVIEW supported it, but I think it would depend on which classes use the ChannelClass objects. Well, one other option would be to use composition, such that the Configuration class is a member of the Channel class private data. Then a call to Channel.configure() [or init()] or whatever, could be public, but it would always return the desired answer, since it would depend on data within itself. I don't know if that will work for your application.
  25. Oh, OK! I misunderstood, then. For myself, I don't mind UML's representation of methods.
×
×
  • Create New...

Important Information

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