Jump to content

drjdpowell

Members
  • Posts

    1,994
  • Joined

  • Last visited

  • Days Won

    183

Everything posted by drjdpowell

  1. I guess one of my problems is that my own messaging design has too many polymorphic VIs to add to. In the quest to make the wiring simple I have polymorphic VIs for Write(create), Extract(get), Send, Reply, Query, and Notify Observers. So using the inbuilt polymorphism of the variant-to-data primitive is attractive. For example, here's a quick rewrite of the lower receiving loop in my previous example, where the "Sett>>..." messages set the internal data values of a cluster in a shift register (a form I use a lot): To extend this to accepting Set>>Temperature messages, I just need to duplicate the frame and select the right element of the shift-register data cluster. I don't need to change the message type. Or alternately I could use the OpenG Variant tools and write one case to handle all "Set>>..." messages: The "Set>>" subVI: Now I can add new "settable" items to the shift-reg cluster (and drop corresponding controls in the UI) without any new wiring to do. -- James Opps: just noticed I left the "Get Var" subVI with the original "Get LVObj" icon; hope that isn't confusing.
  2. I see the great value in using custom message objects instead of custom cluster typedefs (particularly if you use the "command pattern" and dynamically dispatch off the message), but I'm talking about using variants for standard LabVIEW datatypes. From my (more limited) programming experience, I more often use variants to write "generic" code that handles multiple simple datatypes, not large typedef clusters. Now, a large number of simple-type classes (combined with large polymorphic VIs as Steve mentions) will perhaps do everything Variants do, BUT, isn't this reinventing the wheel? Variants have a lot of nice features and are pre-existing, supported by NI, and have lots of utility code written in them already (love the OpenG stuff). Using Variants lets one interface more cleanly with LabVIEW features that already use variants to represent simple data types. For example, here's a simple code for a test UI, where all front-panel controls are connected to a message queue such that all "Value Change" events on any control are sent out as "Set>>{control name}" messages in a VarMessage: Here, I'm directly connecting to LabVIEW's ability to handle multiple control types in a single event frame, via variants. If I want to add a control, I just drop it on the front panel and give it the right name and it's done. Writing code to receive variant messages and update the appropriate control (by lookup of the control name) is only slightly more complicated. To do this UI without variants, I would have to stop and create new message types for the pressure and temperature unit DBLs (it's going to be a big polymorphic VI, Steve, once you get to all the different possible units!), then configure an event frame for each control. But again, isn't this reinventing the wheel? Your goal is to send data, and variants are designed to send data. I like things to be as "easy as hooking up a...wire". I imagine one could design a "SimpleTypeVarMessage" that would produce an error if one connected a cluster to it; that might address your dependency issues. Custom messages I see the advantages of, but that seems a lot of work. Also, isn't the use of custom messages a "dependency" in an of itself? I haven't worked with them enough to know, but it seems like custom messages to talk to a module is a dependancy. But variants ARE a pre-LVOOP solution to the problem of a wire carrying multiple datatypes. Wrapping that solution in a message class is a lot easier than reinventing it. -- James
  3. Question: Why don't you have a "VariantMessage" Type? You have an I32Message, and one could extend that with U32Message, I16Message, DBLMessage, etc. etc. etc., but that's a lot of message types! Is there a reason not to use a single VariantMessage Type to send all simple data messages, rather than a long list of native types? I ask because I've been experimenting with my own (similar to LapDog) message design, and there seems to me to be a lot to be said for using VariantMessages. -- James
  4. I'm currently trying to upload binary data from LabVIEW into a Filemaker 11 Container field via ODBC and I'm getting nowhere. I wondered if you were able to do this? I can upload non-binary fields fine.
  5. The other two methods aren't precise enough to see the <0.5ms variation in times. The ms timer can only time to the millisecond, and the datetime is even less accurate.
  6. Actually, I just came across an example of exactly this use, in an "LVOOP Event Handler" by Francois Normandin: http://lavag.org/top...dpost__p__79074 (you can see "Preserve Run-Time Class" being used in the image) He uses it slightly differently, to allow the publisher to specify who can subscribe to an event, while I intend to use it to allow the subscriber to specify what events they are interested, but it's basically the same. -- James
  7. If anyone is following this, here's some images from one of my first projects using (and continuing to develop) this Active Object design. Its an active object that runs an Ocean Optics USB2000 spectrometer. There are actually 6 "Parallel Process" style objects involved: "USB2000" itself, a subprocess object ("USB2000sub") that synchronously communicates over USB to the spectrometer, two clones of the simple testing UI (two to make sure changes on one reflect in the other), and two different types of message loggers (one for errors and one to monitor all communication by "USB2000"). "USB2000" and "USB2000sub" together publish events and state information via it's Observer Register, to which the UIs and loggers subscribe. There is a mix of Queue and User-Event communication here, with "USB2000sub" and the error logger being based on the queue-messenger template I showed in the initial post, while the other active objects use User Events. "USB2000" itself is based on an adapted JKI-QSM template. Here's a shot of the "State Logger", which displays the last received message of each label, organized by what VI sent it (here it's showing "USB2000sub"): There is now two different types of published messages, "Event" and "State". The difference is that the Register keeps a copy of the last inputed "State"-type message, allowing it to immediately inform newly registering observers of the current state information.
  8. I ended up leaving this feature on the to-do-list awaiting an upgrade, and just hard-wired in the ability to register for all children of "ErrorMessage", that being the only use case I need at the moment. I'm also considering adding the ability for the publishing process to organize groups of messages into "topics", with subscribing processes registering for topics of interest. This would obviate the need for registering message classes.
  9. It is? What I have is an "Observer Registry" (mentioned in my "Parallel Process" topic) that deals with "Message" objects published by one process, to which other processes can register for. I would like the other processes to be able to specify what specific types of messages they would like to be notified of. For example, Process A may be interested in all "CommandMessages", a child class of Message, and any children of CommandMessage. This could be done by Process A passing a default CommandMessage object to the Observer Register, and then having the Register check every Message published to see if it is a child of CommandMessage. The Register would then be dealing with two children of Message (on Message-type wires) and needs to see if one is the child of the other. -- James
  10. An update: As I've had the chance to improve the API for my "ObserverSet" Class, here is the more complex example from above (the one where the Metronome object is used to instruct Process A to periodically send it's time string to Process B) redone with improvements that hopefully make it clearer. Included are some custom probes to see what the Observers and Messages look like. Observers (aka ObserverSets) serve as containers of any number of Messengers (or Active Objects), where sending a message to the Observer sends it to all the contained Messengers/AOs. In addition, an Observer can be set up to automatically alter ("translate") the messages sent by it: in the example they are used to relabel a message, add prefixes, and substitute one message for another. ObserverSets are internally recursive to allow multiple levels of translation. Observers have the additional features of never throwing errors into the process using them to send (the Messenger throwing the error is just dropped internally), and never allowing the process using them to access the contained Messengers/AOs other than to send to them. -- James
  11. It indicates that control has a default value saved that is different than the usual default value for that class (i.e. someone probably choose "Make Current Values Default" after running the VI). The changed default value might be causing your issue. -- James
  12. This post has been promoted to an article
  13. I need to get around to upgrading; I can't seem to do this in 8.6 either
  14. Found what I need: the "Preserve Run-Time Class" function. Unfortunately this isn't in the LabVIEW 8.6 that I'm mainly using. -- James
  15. It's because I don't know either child at runtime; I only have parent-type wires. The "To More Specific Class" function only uses the type of the wire, not the actual object on the wire at runtime. See below: I could do what I want if I could programmatically get a list of an objects ancestors; does anyone know how to do that? -- James
  16. Doesn't work, because those functions use one of the inputs just for the wire type (which has to be the Parent), ignoring the specific child on the wire. -- James
  17. I'm probably missing something obvious but I can't seem to do the following: Imagine I have a class tree with parent, children, grandchildren, etc. How do I take two objects in on parent wires and tell if one object is a child of the other? So for example, if Parent P has Children A and B, with grandchildren A1 and A2 of A, and B1 and B2 of B, I want a subVI (with two "P" inputs) that if comparing A and A1 returns "true", but if passed a B and A1 returns false. -- James
  18. Have you considered adding some kind of optional local scoping to your framework? Sort of a "local router subnet". If you had the option of specifying a local scope when registering to send or receive a specific message (such as string like "UI subnet") you could easily set up an arbitrary number of Waveform Generators under different scopes, communicating independently with different modules, without needing to extend the code of any module. Local scoping could also increase the flexibility of your framework, allowing more hierarchal "module-submodule" relationships in addition to stand-alone modules. -- James
  19. You should be able to us a 5V input SSR. Something like this Cyrdom CN024D05.
  20. The "To More ____ Class" functions don't actually change the class of the object (so, as you created a Child object, you got the child's "multiply" function by dynamic dispatch). They actually just change the type of the wire. A Child object flowing on a Parent wire is still a Child object. I've found this very confusing myself. -- James
  21. Question: what if I wanted two or more of the same module. Say, I wanted to make your "Waveform Module" reentrant and use it in two different ways in my application, with the "UI Module" interacting with Waveform Module A, and, let's say, "Simulated Temperature Readings" (STR) Module interacting with Waveform Module B. Is there a way to direct the messages to only the correct module (UI to/from A and STR to/from B)? If not, I would not be able to operate A and B independently, as they both act on start/stop messages from either of the other two modules. -- James
  22. Having just watched Mike's video it seems he is mostly using asynchronous messaging (via a User Event in his example Active Object). He is using a DVR to allow some of his API methods to modify the active object's private data, and those are synchronous (but fast). My (perhaps mistaken) impression of the differences of Mike's Active Object to mine are: 1) He uses a specific reply channel for all responses from the AO (a queue in his “Send Message To Process” Method Template), while I use multiple reply channels, provided with the incoming messages themselves or "registered" as an Observer. This means (correct me if I'm wrong) that VIs based on the “Send Message To Process” template would not work properly if multiple were called simultaneously in parallel, as there is no mechanism to route the separate replies to the correct VI. 2) He uses bare User events and queues, while I wrap all communication as "Messengers". This will hopefully allow me to substitute network communication when I get the chance to develop it, without needing to modify my active objects internally. I've been following the philosophy that the active objects should know the bare minimum about the specify communication channels they are using. My example AO given in the first post, for example, only knows that it is using a "Receive-type" incoming messenger (which could easily be TCP-based instead of queue-based), and knows nothing at all about it's outgoing communication (which could be queues, TCP, shared variables, notifiers, etc., and can be several processes instead of just one). 3) His active objects are interfaced with by an associated API, while I build messages to send to mine via the generic API of the parent "Send" class. This is a minor difference as he could just use his “Send Message To Process” Template, unmodified, to send messages, and I could easily create wrapper API VIs. I'm not sure which is a better way to do things. -- James
  23. I made the same design choice in my own version of object messaging. Though I'm also not sure if there isn't a problem with this way of doing things. One possible advantage of having a VI take the parent class is that you have the option of handling more than one message type. For example, the VI to get a String might work for both a String message type and a Variant message type, where the variant contains a string. -- James
  24. The alternative communication channel is done: an easy-to-use "Observer Registry" as illustrated in my active object design. Also, although my active object template is a simple queued message handler, one can use more complex designs like a producer-consumer, where the producer loop monitors the input queue and a "Sequencer" consumer does the synchronous calls to the spectrometer and sample handler. Then if the producer were to receive an "Abort" message, say, from the UI it could send "abort" to the spectrometer and sample handler, which would then error-out the synchronous call from the Sequencer. Admittedly, the need to keep the synchronous stuff away from the input queue receiver does illustrate your and mje's point about being careful with synchronous calls. -- James (off on vacation and internetless for the next week)
  25. Following on the "Self-addressed Stamped Envelopes" topic, I'd like to present a design of an Active Object using my messaging library. This template has the ability to both "reply" to messages and to allow "observers" to register for event updates. I haven't used this template yet (just wrote the observer-registration part yesterday) and I would appreciate any feedback before I use it in a real-world project (which I'll be doing after a weeks vacation). The example object, "PP Template" does the following: Responds to "Current Time?" with a text string of the current time. Sets the format string for the time by the command "SetTimeFormat" Starts generating "TimeUpdate" messages periodically on the period given in the command "AutoGenerateTime" Allows external processes to register as observers of the following events: "SetTimeFormat" received "Current Time" issued "TimeUpdate" issued "Macro: Exit" received (this is the shutdown command for the active object) All error messages Here is how a higher-level VI might create ("launch") and communicate with "PP Template": In this example we launch "PP Template", register a Message Logger (another active object) as observer of all published events, send a nonsense message "Blah Blah" just to demonstrate error handling, set the time format, and query "Current Time?" first synchronously(*), then asynchronously with the message reply going to the "Asynchronous Receiver" loop. Then we register "Asynchronous Receiver" as an observer of TimeUpdate events (and set the autogenerate time of these events). On pushing the "stop" button, "PP Template" is sent a "Macro: Exit" command to shut down. Here's what the Message Logger receives when we run this VI: And here is the VI dynamically launched VI at the heart of the active object. It's basically a message handler with eight cases; three of the cases handle the three commands, then there is a timeout case, an error handling case, a shutdown case, an "ignore null messages" case (not shown), and a default case to catch unhandled commands (and spelling errors). The way receiver timeout and errors feed into the message case structure is stolen from Daklu's Lapdog messaging library. Some details of communication with the active object: 1) The object is launched by dynamically calling the PP Template.vi, setting it's "Startup Message" control via a VI Server call, and then doing some handshaking messages between "Launch.vi" and the "Startup" and "Acknowledge Startup" VI's. This creates the incoming command messenger for the process (in this case it is a queue based messenger, but there is an alternate "Startup" VI for a User-Event-based messenger). Calling a "Send" or "Query" method on the active object calls the same method on the command messenger. There are two startup modes: when started as an "autoshutdown slave", the messenger is created inside the Launch.vi, which means that if the main VI goes idle, the queue reference will become invalid, which triggers a "MSRReceiverError" that shuts down the launched VI ("Message Logger" in the above example shuts down in this way). The other way creates the Messenger inside the "Startup" VI, making the active object independant of the Launching process. 2) Observer registration is handled by an "ObsReg" by-ref object, with two methods: "Registration", which filters out and acts on registration commands, maintaining a list of all observers for each event message (and sets for all events and all error events), and "Notify Observers", which accepts messages and sends them to all observers registered for them. Thus, it is extremely easy to publish new event types; just package a message for the event and pass it to "Notify Observers". 3) Reply-based communication involves a "return address" (a messenger or other active object reference) to be attached to the message, and the active object using a "Reply" VI to respond (in synchronous communication, the "Query" VI creates a single-use response queue to receive the reply). For error handling, the Reply VI will package any error in it's error terminal as an error message and send this to the return address (this is in addition to an observers being notified of the error). -- James (*) Please note the potential problems of synchronous communication brought up by mje.
×
×
  • Create New...

Important Information

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