Jump to content

drjdpowell

Members
  • Posts

    1,964
  • Joined

  • Last visited

  • Days Won

    171

Everything posted by drjdpowell

  1. Oh dear, 5 to 1 against so far. Surely there are some more unicorns? Additional question (motivated by the ni.com discussion): do you think having both forms leads to confusion, and would you be in favor of LabVIEW being changed to allow only one form? For the purposes of this question, assume the form to be chosen is the other one from the one you like. -- James
  2. I was just reading some opinions on NI.com about whether it is better to show FP terminals on the block diagram as full-sized 32x32 icons or as the smaller 16x32 terminals. I wondered what people on LAVA think. Do you use one type or the other and how strongly do you feel about it? Personally I (moderately strongly) prefer icons. -- James
  3. Are you sure you're actually using the "task" configured in MAX, rather than creating a new task programatically? Is "BDHSIO" the name of your MAX task, or is it the name of a channel? If it is a channel, you are creating a new task with the default sampling rate.
  4. Hi todd, I can't comment on everything you and Daklu have been discussing, but let me make some comments on your design so far. I would suggest that, whatever design for a parallel process you decide on, use the same one for both "Instruments" and "Tests". They are both "actors"/"active objects"/"slaves"/whatever. Currently, you have a totally different method for running tests (dynamic launch after "Set Control Value") and running instruments (class method on-diagram), which means you basically have to debug two different, quite advanced and complex, designs. Settle on one design that works for both and perfect it. For example, I've been developing a design I call "Parallel Process". I have a parent class that contains the "Launch" (Create) VI that dynamically launches a VI, sets it up with a command messenger (message queue) and wraps that queue inside the parent object. All my... let's standardize and call them "actors"... inherit from this class (I have a few templates that I copy from). As each actor is standardized, each is created by the same parent "Launch.vi", and once that VI is debugged I never have to worry about the details of dynamic launching. And the child actor classes are very simple; generally with no additional private data items, no additional methods or overrides, and only one VI (the one that is dynamically launched) copied from a template. The complicated stuff is all in the parent class. Note that once you have the basics of a VI dynamically launched and running in parallel, sitting behind a command queue, you have all the ability to customize it. Your "Create Test.vi" could (after launching) send your Test a message containing a cluster of "Instrument" proxy objects (ie. objects that contain the command queues of your instruments). Your "Create InstrumentA.vi could send messages containing the information needed to configure InstrumentA. Regardless of what design you go with, try to get complicated stuff like dynamic launching and messaging standardized into clean reusable components (such as in a reuse library or a parent class) so that you can deal with the complexity once and then not have to worry about again. A second comment I have is about running multiple "Tests" in parallel, which I get the impression you're trying to do. If that is the case, it may be a good idea to add the ability for a Test to "lock" an instrument so that it can't be simultaneously used by other Tests, to prevent potentially dangerous conflicts. This is something for which a parent "Instrument" class (child of your "Actor" class) might be useful. The locking could use a semaphore or similar feature contained in the parent class. -- James I stole this idea it for my own messaging system within about 10 minutes after downloading LapDog. I would also note that Daklu treats timeouts in a similar way: outputting a "Timeout" message rather than having an extra timeout-boolean output. I really great idea. -- James
  5. I don't do a lot of thinking about "design patterns" when I'm coding either. Instead LVOOP extends my ability to think things like "these operations are closely related, I ought to make a simple class and inherit from it". That statement is not more complex than inter-loop communication. As I indicated before, I think one is better off just starting to use LVOOP as "type-def clusters+" and start exploring a (very small amount of) inheritance, LONG before ever reading about a "design pattern". Even now, the only "named" design patterns I've used are the "Decorator Pattern" and the "Command Pattern". Technically, I've probably used a few others, but independently of actually reading about them as "Patterns" (as, probably, have you). -- James
  6. I don't use your method, but mainly because I do use two closely-related methods that "bracket" yours. One is to extract important information from commands and keep it is a named cluster in a shift register. On timeout, the appropriate action is expected based on this information. This is more flexible than yours as the retained information can be from all the past commands, rather than just the last one. The other is to use a Notifier: wait of notification, and on timeout get the last notification. This is the same as using yours with a single-element lossy queue. I use this design when there is only one "command" (ie. the parameters controlling what to do on timeout). So, your method is more capable that the later (can use alternate queue characteristics and multiple "commands") but less capable than the former (only save the last message). The problem is I can't think of a use case where I would want more than my notifier design, yet only care about remembering the last command on timeout. -- James
  7. Hi todd, I'm having a hard time following what your doing. It sounds complicated, but things always sound complicated when one is can't tell what's going on. I'm not sure what exactly you are "classing" and calling your "instrument. So let me sketch out how I see things. The design I normally use is this: There is a communication reference (often a queue) that allows communication with a loop (on diagram or in a subVI). This loop contains information about, and methods to act on, a real-world instrument. Now, there is more than one place to use a LVOOP class in the above design, and more than one thing that can be referred to as "the instrument class". The place I think your using is the latter part: the information about, and methods to act on, the real-world instrument. That's a perfectly good place to use a class, the most obvious place, and useful for various reasons, but I would never want to use that class as "the instrument" in the higher-level code. The entire structure is "the instrument", and the communication reference out front is a "reference to the instrument". If you look at my designs, the LVOOP classes with instrument names are actually just wrappers of a communication reference (or references). And these are effectively by-reference objects; they can be easily passed around and used in multiple places (no problem with branched wires). They serve as a proxy for, and encapsulate, the entire structure of communication, loop, data/methods, and real-world hardware. To interact with the "instrument", I send a message to this proxy object (or call a class method that sends a predefined message), which passes the message to the loop, which performs the appropriate action on the internal data or communicates with the real-world instrument. If I have second, by-value class for the instrument data and methods, it is accessed only internal to the loop, and never "passed across BD lines". If your trying to pass this by-value object between different loops, that sounds like a rather complicated and troublesome thing to do. -- James
  8. One thing to think about is the possibility of organizing instruments into "subsystems", so the higher level program controls subsystems and each subsystem deals with a limited number of instruments that work together. Then no part of the program is trying to juggle too many parts, and you can test the subsystems independently. It depends if your application can be organized into subsystems ("VacuumSystem", "PowerSystem"). I think that is called the "Facade Pattern" in OOP. Personally, I usually find it worthwhile to give each instrument a VI running in parallel that handles all communication with it. Then any feature like connection monitoring (as Daklu mentioned) can be made a part of this VI; this can include things like alarm conditions, or statistics gathering, or even active things like PID control (the first instrument I ever did this way was a PID temperature controller). Think of this as a higher-level driver for the instrument, which the rest of the program manipulates. You can use a class to represent or proxy for each subsystem or instrument; this class would mainly contain the communication method to the parallel-running VI. Daklu's "Slave loop" is an example. You can either write API methods for this class (as Daklu does) or send "messages" to this object (which I've been experimenting with recently).
  9. I agree, there are more significant differences than communication method. A "server" stands ready to respond to requests from a "client"; a "subject" notifies its "observers" of what it's doing (but is not acting on requests and is entirely unaffected by the observation). A "slave" serves, and is entirely dependent on, one master. "Producer" and "consumer" are dependent on each other and have no separate identity. There all essentially pieces of code exchanging information, but the particular metaphors chosen carry useful, if imprecise, meaning. -- James
  10. Ah yes, the "Decorator Pattern". I like using it but it is a royal pain if your class has a lot of methods, and that limits its use. Direct inheritance is easier. Instead of accessors, you could alternately make a "Create" method that accepts a MessageQueue object as input. Then you can provide a PrefixedMessageQueue constant as input and it will initialize the internal queue. Then PrefixedMessageQueue never needs to directly access the data items of MessageQueue (I think; I'm still waiting for a replacement of my development computer that broke weeks ago, so I can't experiment). You can, of course, make the "Create" method protected and wrap it in a "Create PrefixedMessageQueue" method that doesn't accept a class input, thus maintaining your current public API. -- James
  11. I wouldn't use the "master/slave" pattern provided, but I do use an alternate structure where a slave loop performs an action continuously, with control parameters provided in a Notifier. The difference being, the loop relies on the "Get Notifier Status" function rather than "Wait on Notification". Admittedly, I could probably do the same thing with a one-element lossy queue. -- James
  12. My understanding of "Master/Slave" is from electronic hardware, where often groups of identical or similar units are "slaved" together, with one being designated the "master" and all "slaves" being controlled through it. For example, 10 power supplies might be slaved together such that adjusting the voltage of the master adjusts the voltages of all of them. Generalizing the pattern, I'd say the key feature is that the "master" fully controls the "slaves", control of the slaves is only through control of the master, and a slave only has one master. Both NI examples seem like they could fall into that classification of "master/slave". If one were to modify the Producer/Consumer example to have multiple Producers (which one can do because of the communication buffering) then it would no longer be master/slave. Another aspect is how you look at the relationship; is the Producer loop exerting control of the Consumer (sending "commands") or is it providing a service ("data")? Master/Slave is about control. -- James
  13. A third option would be to put the functionality into your MessageQueue class. Make a "PrefixedMessageQueue" child class with a "Prefix" data item, an extra "Specify Message Prefix" method, and an override of "Enqueue Message" that adds the prefix to the message name before calling the parent method. Use this child for the master's incoming queue and, just before passing it to each individual slave, call the "Specify Message Prefix" method with prefixes such as "SerialDevice:DeviceB:" or whatever. The advantage of this method is that you only have to create the child class once, rather than modifying each slave to have a name property, or making a separate adapter class for each slave class. -- James
  14. Why not? He's got his hard hat on. I disagree, Shaun. Those confusing lists on wikipedia don't seem to contain any design patterns that solve problems caused by OOP. Take the "Creational patterns" list, for example. "Lazy Initialization", "Pool", and "Prototype" are things that aren't OOP specific (they can be done in non-OOP LabVIEW) and aren't solving OOP problems. The Factory/Builder ones seem more OOP specific, but they aren't solving OOP problems, they're doing things not easily done without OOP (one could argue against their usefulness, but they are optional, and one doesn't need to learn them to use OOP to improve ones LabVIEW). "Singleton", as you say, is more easily done in non-OOP LabVIEW, but I would suggest that that leads to it being used far too often. How many "Action Engines" actually have a real reason to be Singletons? The advantage of the "Singleton pattern" for the LabVIEW programmer is consciously thinking about where NOT to use it. I do admit that one can take OOP and become an Architecture Astronaut, and I've certainly seen code that is well beyond my ability to understand, but there is plenty of down-to-earth advantages below the stratosphere. Just simple inheritance, by itself, is a great advantage. -- James
  15. Only, presumably, if the team agree that OOP is a reasonably good set of rules. I doubt ShaunR will be much impressed with this argument.
  16. Are you sure? Have you ever used the "design pattern" of having a type-def cluster and a set of subVIs that act on that cluster? With the cluster going in the top left terminal and out the top right? Because that is the basic design pattern that is LVOOP. Looking at a long list of impenetrable "design patterns" makes OOP look like its impossibly hard to start with, but actually it's very easy to start using as a cleaner, better way to do this important pattern you already do in LabVIEW. I recall in a conversation somewhere with someone saying that a big advantage of LVOOP is the ability to customize the appearance of the wires. Some people thought they must be joking, but I understood and agreed completely. LVOOP formalizes and supports the "type-def cluster" design pattern. A less-common LabVIEW pattern is to allow the same set of subVIs to act on related, but different, "things". If I recall correctly, ShaunR uses this pattern in his Dispatcher/Transport code to allow use of either TCP, UDP or Bluetooth as a communication method in a common set of VIs. I don't remember the method he uses (there is more than one way), but this is another design pattern that LVOOP makes cleaner and also more powerful (through "inheritance"). Inheritance is harder to get comfortable with, admittedly, but that's because this design pattern is harder to grasp regardless of how it's done. LVOOP makes it easier to do and more widely applicable (and thus you can make use it more often). Starting with those two patterns (or even just the top one) which you are likely already using is an easy way to get started on LVOOP. -- James
  17. I believe an "Actor" can't have any direct sharing of it's internal state information. Using a global to pass a message for the Actor to act on (in a well-defined, controlled, atomic way), is different than being able to directly fiddle with state parameters (as I did with my PID controller). It's a kind of encapsulation, in the same way that an OOP object controls access to its private data.
  18. I think an "Active Object" is anything that is an "object" and has an active internal process. The first active object I ever created was a PID temperature controller. Methods such as "Change Setpoint" or "Set PID parameters" could be called on the PID controller "object" (this was pre LVOOP), while the PID control cycle continued automatically in the background. An "Actor" is a process that interacts with other processes solely by passing messages (it has no shared memory with any other process). My PID controller was not really an Actor.
  19. I would imagine that, if one were strictly using command-pattern messages with dynamic dispatch (instead of case structures), it would be relatively easy to convert from an on-diagram loop to an Actor (since the messages are decoupled from the structure handling them). Of course, if one doesn't strictly use the command pattern from the start, converting to an Actor would be much harder. I could really use such a a thing and am looking forward to it. I think it's all a loosely defined continuum. Your Slaves certainly satisfy key aspects, like only being coupled by message passing. Wikipedia says: "In computer science, the Actor model is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent digital computation: in response to a message that it receives, an actor can make local decisions, create more actors, send more messages, and determine how to respond to the next message received." Your Slave loop can do all that except create more actors in response to a message (but adding dynamic launching would allow this too). -- James
  20. I haven't actually used either and don't even have a working computer with LabVIEW at the moment, but from memory of looking at them: I would say the the "Slave Loop" design is a type of actor framework. The biggest thing about the "Actor Framework" is that one can create child actors that inherit from parent actors and add functionality. This is because it uses an "Actor" object to contain the internal state data and dynamically dispatches important methods like "Actor Core" off of it. -- James PS> you could also have a look at mje's "Message Pump" framework for a different design that does similar things.
  21. From reading the paper, I believe provided methods can be overridden; in fact they sometimes have to be, when there is a "conflict" between identically names methods provided by multiple traits (section 3.5, page 11). -- James
  22. Just skimmed the paper, but it looks quite promising. The .lvtrait library would presumably be able to be owned simultaneously by multiple .lvclass libraries; I wonder if NI might have some difficulties with that. -- James
  23. Can I encapsulate the local queues as well and call my Proxy class "Postman"? I actually use my version of Postman ("Send") as the parent of classes that serve as Proxies for my Active Objects ("Parallel Process"). So I can extend/customize these proxies if needed. For example, my "MessageLogger" class (really a proxy of communication with a parallel-running VI that records messages) overrides "SendMessage" to collect additional information from the sending process: The sending VI name and the message time seen above is extra functionality added by override. So, I think I can add desired custom functionality in a non-clumsy way for that "other 10%" through inheriting off of Postman. And I like the Proxies being "up front"; my preferred abstraction is: CompA --> CompB(proxy) Which may explain why I'm uninterested in queue operations like "Flush" or "EnqueueInFront"; they don't mean anything in this abstraction. -- James
  24. Well, it just seems to me that this route leads to less reusability/flexibility, or alternately, a lot more work. A component written to communicate in one way can't be reused in an application where it must communicate another way without either modifying it or writing an entire Proxy for it. A custom Proxy may be more flexible that a generic solution, but 90% of the time the generic solution is fine. No. But not blocking and throwing an error is fine. I must admit that my network experience is only one project using Shared Variables, but the likely use I would have for networking is between Real-Time controllers and one or more PCs in the same room on a dedicated subnet. Network failure is rare, and in any case, there is little value in half my distributed application running if the network connection has failed. They call that "message persistence" in the stuff on Message Queues like RabbitMQ. The reason I would like to wrap one of the open-source messaging designs is to get advanced functionality like this for free. I'm not sure I would have much use for it in typical applications of mine, though, so I would be happy with a TcpPostman with buffers at each end that errors out in the unlikely event that the buffers fill up. If only my computer hadn't up and died on me last week! Gotta wait a couple of weeks for a new one. In the meantime I can keep bugging you. -- James
  25. Ah, but isn't "Send Message" the central functionality of a message sending system? Aren't these "extra levels of control", just like the "priority queue", not central to the business of messaging? "The ideal queueing system... spits things out in the order it received them..."; User Events do that. Having a common parent class means one can write code with "plugin" communication methods. "Actor A" can register with "Actor B" for message updates without Actor B needing to depend on the exact communication method that A provided. Why does Actor B need to do anything other that "Send Message"? Note that this does not mean throwing out the option to use the Queue advanced features; you can write Actor A to deal with a Queue explicitly (and use Queue methods like Queue.Preview or Queue.Flush) while Actor B just uses Postman.SendMsg. Or you could write both Actors to use Queues specifically. Having a parent class doesn't eliminate the ability to use the child classes fully, it just adds the ability to use them generically, using the common subset of functionality. And to repeat the point, the one common function, "Send Message", is the only crucial one for a messaging system. In my own design of a messaging system, my parent class is actually called "Send", and it is used all over the place, particularly in implementing "Observer Pattern" designs. If not user events, what abut TCP? How would you extend LapDog to a network communication method, and wouldn't it be good to be able to take a previously-written design and just substitute a TCP_Postman for the Queue? Do you really need methods like TCP_Postman.Flush or TCP_Postman.Preview? Well, I'm interested not in queues, but in message-passing methods, which is not exactly the same. For example, queues have the ability to have blocking enqueue (if a limited-size queue is full); but a blocking "SendMsg" is NOT acceptable in asynchronous messaging (and you can't have an effective "Observer Pattern" with a potentially blocking Observer.Notify). So I don't want full Queue flexibility. I want a selection of message-passing methods with some degree of plug-and-play interchangeability (on the sending side, at least). I'm not sure this is true for a notifier, which is less a method of receiving messages that must be acted on than a bulletin board of posted information that I can choose to look at if I need to (or ignore if I don't). A queue-based message handler could consult a notifier to get information needed to handle a message arriving on the queue. I envision querying my MessageNotifier for the last message of a specific name/label/type, but only when I need the information. Can I not provide another tool in the toolkit because it might not be perfect for every job? Can't provide a hammer 'cause someone might prefer a slightly larger hammer? I certainly think I can come up with a reasonably good set of design choices in making a MessageNotifier that would be reasonably useful (assuming one really wants a notifier). [bTW, I'm thinking "sorted array", "binary search", "in the enqueue method", and "exact order not important" (a notifier is a bulletin board, not an inbox).] And I've gone back to using QSMs, but with better clarity of their pitfalls due to your reasoned arguments against them. -- James
×
×
  • Create New...

Important Information

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