Jump to content

Daklu

Members
  • Posts

    1,824
  • Joined

  • Last visited

  • Days Won

    83

Everything posted by Daklu

  1. I doubt it. (I hope not, seeing as how I didn't create one for my exam.) I view XControls as an encapsulated unit of UI functionality, much in the same way a class is an encapsulated unit of programmic functionality. I have found them most useful when I want to reuse some bit of customized UI behavior in multiple places, which doesn't happen often. Usually I just customize the control's behavior on the VI's block diagram rather than creating an xctl. I have too many crashes and issues with xctls to want to use them more than necessary. My personal opinion is that there are better ways to spend your time than by creating an xctl. Our local sales rep arranged a con call with an exam grader when several people in the area were preparing for exams. He indicated the sample exam is an actual solution turned in by an applicant--it's not an "ideal" solution. My perception of the grader's comments were that he thought the solution focused a little too much on implementation and not enough on architecture. Nevertheless, he did indicate the applicant scored somewhere in the 90-95 range. However, I absolutely agree with JG. Anyone claiming to be an architect--regardless of certification--ought to be familiar with xcontrols.
  2. Why are hundreds and hundreds of unused VIs being loaded? Does your class have that many member vis? If so, I would guess the problem isn't with LVOOP or subpanels, but with your class design. It likely needs to be refactored and broken out into many individual classes rather than one gigantic class.
  3. The messaging infrastructure is definitely more visible, but it's not more important to the application's purpose. The messaging system is the glue that ties all the different functional pieces (loops) together, so it's going to be very visible. Yep, that doesn't surprise me. Object-oriented programming is very different than procedural programming. It takes time to make the shift from procedural programming to the thought processes, patterns, and debugging techniques of OO developers. There's really no way around that, but the more you do it the easier it becomes.
  4. There's no property that does that--you have to implement it. Set up a separate loop whose sole job is to write data to the serial port. When you have data that needs to be written, send it to that loop via a queue.
  5. I don't understand exactly what you're saying here. Looking at it from the user's point of view, how do you want the program to behave? Some questions off the top of my head... - Do you want it to run backwards automatically when the collect tank is full? - Do you want the user to press a button to make it run backwards? - Should the user be able to change flow direction before the collect tank is full? Yes, that is correct. Event structures are designed for capturing front panel event from the user. (User Events too, but that's a more advanced concept.) Generally *everything* should be outside of the event structure except for code to send a message to a parallel loop that does the actual work. That way the user interface doesn't become unresponsive while the program is running. Look at the "Producer/Consumer Design Pattern (Events)" template included with Labview. (Found in the menu under File --> New...) Since this is an exercise to help you learn Labview, yes, you should study up on state machines. You'll need to know it even if it doesn't help you solve this problem. Can't help with this one. Is it part of an add-on toolkit?
  6. FYI, I added a project with a working sample application to my post above.
  7. Feel free to post any questions you have. People are always willing to help.
  8. Was I too obvious? Actually, "favorite" isn't the right word. (Or maybe it is--but I don't like the connotation it carries...) I have nothing against OpenG and have used it in the past. It's good stuff. LapDog and OpenG aren't competitors. They serve different purposes with different goals. OpenG packages (I believe) are a bunch of very useful vis that arguably should have been included in Labview. LapDog packages are intended to be functional components you can easily drop into a project and extend to meet your custom requirements. A messaging system, collections, data structures, sequencer... things like that. I emphasized LapDog mainly because it needs more help for it to become truely useful for other developers. There's just too much work for a single person to put out quality stuff in his spare time. Design, implementation, documentation, examples, packaging, etc. takes a lot of time. And since that single person is me, I feel it a bit more acutely.
  9. Check with your local sales rep to find out if there are any user group meetings in your area. Volunteer to give a presentation about something you're experienced in. There are a couple open-source projects that always need help. Two that come to mind are OpenG and LapDog.
  10. Huh... that example has been completely rewritten since the last time I looked at it. I think this version (in LV 10) is much more useful than the previous version. Thanks for pointing it out.
  11. Whoops, sorry. I've replaced the incorrect zip file in the post above with the correct zip file. A couple notes about the sequencer: * Each step you create must be a child of SequenceStep.lvclass. A step can contain as much or as little functionality as you want. * SequenceSlave supports Start, Stop, Pause, and Unpause out of the box. (Pause will take effect after the current step finishes executing.) * This sequencer doesn't support nested sequences, though I am also working on another version that does. * It is the library user's (that's you) responsibility to build the user interface allowing end users to select which steps will be run. * Once you know which steps should be executed, create an array of steps and pass it to SequenceSlave.Start().
  12. Too obvious for my first ninja post? Assuming you're going to be developing and supporting the application, the "best" architecture is one you understand that satisfies the requirements. There are far too many unknown requirements for us to suggest one for you. I prefer loosely coupled code, so I tend to use a lot of messaging and parallel loops. It may or may not work for your situation. Can't answer that without knowing more about the tests. If the tests are similar, it's possible you could have one test class that takes different runtime parameters for each test. However, one class for each test is easier to understand if you're just starting in OOP. Whether or not you should use the factory pattern is an entirely different question. Personally I'll use a factory if I have to gather or process a lot of data prior to creating a new object. Otherwise I don't get much benefit out of it. Assuming Test Stand isn't an option for you, I've attached a simple sequencer component I've been working on and planning on releasing through LapDog. Sequencer.lvlib is the reuse component. SampleApp.lvlib is the start of an example showing how to use it, though it is incomplete and not functional yet. You do need to install the LapDog Message Library before opening the project. You can get it from SourceForge. [Edit Apr 8 2011 - Attached correct zip file.] [Edit Apr 10 2011 - Added project with working example. (LapDog.SimpleSequencer v0.8.zip)] Simple Sequencer with incomplete Sample App.zip LapDog.SimpleSequencer v0.8.zip
  13. I didn't... I read through the sample exam, sketched out some thoughts on paper, coded a little bit, but other than that I didn't really do anything special to prepare for the exam. Oh... our local sales rep hosted a one hour conference call with an exam grader. That was very helpful in helping us understand where to spend our time. Probably the hardest part for me was that the given requirements drift into implementation details instead of focusing on user or non-functional requirements. A big part of an architect's job (IMO) is translating the user and non-functional requirements into implementation details. I had a bit of a hard time grasping the bigger picture of what behavior the customer wanted and felt unnecessarily constrained by the list of requirements given.
  14. Danny, you should add this to the Wiki. I've often thought there should be an optimization section with tips and tricks (mainly because I can never remember them all)--this would be perfect. (2 for Shaun! One for knowing the details and one for telling us how to learn them ourselves. )
  15. Could be lots of reasons... maybe during dev you want to sandbox a variation without messing up your original code. Maybe you discover you need another channel that is almost exactly identical to an existing channel, like a connection to a second database. Whenever I assume I won't want to do something in the foreseeable future, I almost always discover the 'foreseeable future' is much shorter than I expected. Well... "better" is a subjective term... and I'm not exactly clear about which parts you are considering architectual and which parts are modifiable. But I think there are simpler solutions. (Break out the syrup grandma, I'm cooking up waffles!) **ack** kugr... overload... <faint> Yep, that's the command pattern. I'll second what kugr said. It's not a *bad* thing, but it does come at a cost. In addition to what he said, I'll add: Time - Creating new classes is still relatively time consuming in Labview. Typically I'll spend 2-5 minutes on a new class with accessors and icons. No big deal for a few, much more of a drag when there's 2 dozen of them. That was a dev task I started dreading. Readability - Having the class hierarchy control execution flow hinders readability. All dynamic dispatching does that to some extent, but if you carry it too far it becomes very difficult for a new dev to build a mental picture of what is happening--especially if the dev isn't familiar with OOP. In my opinion the command pattern doesn't make a very good messaging system, though I know there are many who disagree with me. I think it couples the sender and receiver together too much. Where it shines is when component A is responsible for defining the processes to be done on the data, component B is responsible for deciding when to call the process and for executing the process in its own thread, and you don't mind A being coupled to B. Plugins are a good example. The plugins are component A and the app is component B. The roles are much less clear to me in your design. The messages for a given channel are component A, but the responsibilities for component B are split between Main.vi (executing the process) and all the other channels (deciding when to call the process.) That leads to circular source code dependencies between the messages of all your channels which will be a nightmare to untangle. IMO, good design is primarily about managing dependencies.
  16. First, since you want to make this a generic messaging framework, I assume you're planning on adding it to your reuse library. Second, I know you mean "which bit of code should define the log text," but let's take a step back and read it a bit differently. Which developer--the library designer or the library user--should define what messages and text are logged? To me the answer is clearly the library user. (Maybe the user wants to log message data along with the message name... maybe the message contains a queue and the user wants to log the number of items on the queue... the possibilities are endless.) What part of the system is customized by the user on a per-application basis? If I'm understanding your system correctly, it's the messages. Therefore, the decision about what text to log should be left up to each message--option 1 from your list. What if I want the log file in xml format instead of text format? What if I need the log file stored on a server instead of the local computer? What if I just want the log messages to appear in a window instead of on disk? In other words, how can I, as the user, change the logger behavior without editing the distributed reuse code? 4. Create an abstract MessageLogger class that can be injected into the Message Interface Object by the library user. If no MessageLogger is injected, no messages are logged and you don't suffer any performance penalties. You can supply a simple text file logger with the distribution. If users need different behavior, they can create their own MessageLogger child class that does exactly what they want it to do and inject it into the Message Interface Object.
  17. Aye... there were about 8 of us taking certification exams in the Seattle area that day. I know at least 3 of us post on Lava... John's the lucky bastid that got his results first. (I'm sure they're sitting on my exam just to make me squirm. Either that or they're trying to figure out what in the world I did...)
  18. Erm... you do realize this is an OOP forum, right? And doing a Save As on the Channel_StopMsg class and any other messages you want to copy... followed by changing all the new message classes so they inherits from the new channel class. (Hint: Wrap each channel in a lvlib and put it in it's own directory and you can simply copy the folder on disk to create a duplicate channel.) This is a question of strict type safety versus rapid development. Strict type safety is safer in that it eliminates potential bugs, but it also really slows down your development. The trick is to find the right balance. I use strings to define the message name, but I try to keep all instances of that string contained within a single project library so it's easier to do a search and replace. Ideally, code outside the library isn't even aware of the message's real name. Nope, I don't. It does what you outlined in your requirements so call it good and move on. (The tickling in the back of my head is wondering what drove the requirement to have a messaging system that uses dynamic dispatch to auto route messages.) The problem isn't with the producer-consumer pattern; the problem is in not properly encapsulating the subsystems or managing the dependencies between the subsystems. One thing worth pointing out in your example is that neither of your channel loops have any data associated with them. In fact the command pattern, which this is based on, doesn't provide a good way for a message to obtain data from the channel loop when the message is executed.** Each message has to be given all the necessary data for it to execute prior to being put on the queue. That means... 1. Your producer loop has to maintain all the information about the entire application rather than allowing subcomponents to be responsible for their own information. Better to delegate the responsibility for certain bits of knowledge to the subcomponent (channel) itself. 2. Since using this pattern prevents a loop from "owning" its own data, all your channels are simply identical remote execution threads. Why do you care which loop a message gets sent to? Why not just send it to the first loop available to process it? In this particular case, defining routing based on class hierarchy hinders your ability to be flexible in balancing message load. (**You could add an abstract Channel_Data class and put and input and output on the MessageQ:ExecuteMessage method, derive a concrete data class for each channel you implement, and provide all the accessors so the messages can access the channel data when it is executed... but you're really getting into obscure code for very little gain.) Parent. Queues are based on the wire type, not the object type.
  19. I believe I read somewhere they use a red-black tree.
  20. If it fits your needs then who are we to tell you it's wrong? But since you asked for our thoughts, here are a few: * It's an interesting way to take advantage of inheritance. My inner geek is celebrating. My inner pragmatist is sounding the alarm. * The unusual class hierarchy and odd dynamic dispatch terminal location makes it harder to understand what is happening. * Since you're using type checking to find the correct queue, my gut sense is that it is more complicated than it needs to be. I haven't really dug into it though. Agreed. That was my first thought too, but it's not correct in the general sense. Channel_A_SendToB.lvclass is message sent by the UI loop to the Channel A loop telling it to send a message to Channel B. It's just a way to test the ability to send messages between channels A and B. I don't think it's something you would do in a regular application.
  21. Congrats Steve! (Would you have rather waited until Monday for the news?)
  22. I've seen that before, but it's still funny. Although it's meant as a dig against programmers, if you read it closely it highlights one of the main differences between the software world and hardware world--expectations. The potential issues raised by the software developer are patently absurd when applied to a toaster. Nobody expects a toaster to do anything except make toast. It's obviously not well suited to cooking an omelette. That task requires creating an entirely new set of tools from the ground up. (Stove, frying pan, spatula, etc.) Yet software developers are asked--and expected--to make these kinds of changes all the time. It's kind of like delivering the first Dodge Viper to the CEO and having him say, "everything is perfect, except.... diesel fuel is cheaper than premium unleaded, so we'd like the car to use that instead. The diesel fuel dispensers at the gas stations are 1/2" larger than the unleaded dispensers, so you'll have to make the hole under the gas cap a little bigger. That's an easy change, right? I'll be back tomorrow to give it a test drive."
  23. I agree and don't use them in my code. It was just the easiest way to illustrate the key-value concept of identifying the correct queue in the array. Your solution might work, but I'm missing a few things. Couple questions: 1. How are you storing the queues in the queue collection class (the first class?) Storing each one as a separate control isn't very scalable, especially if you have 1000's of queues. The best solution will allow an arbitrary number of queues. 2. How does the message class (the dynamic dispatch vi) know which queue to get from the queue collection class (the first class) at runtime? It needs to have some way to tell the queue collection which queue to put the message on. 3. How does the loop dequeue the message? There is, but it might not help you in this case. The 'Preserve Run-Time Class' prim downcasts based on object type, whereas the 'To More Specific Class' prim downcasts based on wire type. I don't know what will happen at run time if you feed the PRT output into the Obtain Queue prim. I suspect you'll get a run time queue of the child type. In any event, you're resorting to some (as you suggested) obscure code for (imo) very little gain. Yes, if the parent has accessor methods. I often create protected static accessor methods specifically for allowing children access to parent data.
  24. In the queue palette there is an "Enqueue Element at Opposite End" primitive. This enqueues items at the front of the queue instead of the back. Using that instead of the regular enqueue prim effectively gives you a LIFO. [Edit - Wow... we just can't wait to help someone out... ]
  25. It does have the command pattern part of the solution, but it's still only a point-to-point solution. John (if I'm understanding correctly) is asking about a single wire message bus supporting multiple queues with automatic routing based on message subclass type... and the command pattern. I've attached an example that shows how to implement the general behavior you're asking about. (It does depend on the LapDog Message Library.) Some of the implementation is different. Specifically, you mentioned dynamic dispatching on the message type to choose which queue to enqueue or dequeue the message to. I can't think of a clean way to do that. This implementation uses named queues to associate a message type with a queue. Each loop's message class overrides Get DestinationQueue and returns the name of the queue assigned to that loop. This is the main vi, and it illustrates what I don't like about this particular pattern. If you imagine each of the four loops as separate components, you'll notice they each contain class cubes or vis "owned" by other loops. This creates source code dependencies between the components... tightening the coupling and making the app harder to maintain later. The messaging system (IMO) should help keep components independent from each other, not tie them together more. The QueueCollection.AddQueue method queries the input message for it's queue name and adds that queue to the collection. Obviously this should check to make sure the queue doesn't already exist. EnqueueMessage (and DequeueMessage) iterate through the array of queues and return the one with the name that matches the message's queue name. AutoMessageRouting.zip
×
×
  • Create New...

Important Information

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