Popular Post Daklu Posted February 11, 2011 Popular Post Report Share Posted February 11, 2011 I know some people like to have all their loops visible on the block diagram of the top level vi. I used to do that too, but I found that as the application grew the block diagram got too busy, there was too much to keep track of, and it became very easy to make mistakes. (Not to mention stepping through code is a major PITA.) After a bit I switched over to wrapping my loops in sub vis. That helped, but I ran into issues with that left me dissatisfied as well. I thought I'd share one of the patterns I now use that not only helps me think of my code in components, but also help ensure my components stay decoupled during implementation. It's not particularly unique or innovative, but it might be helpful to someone. I call it a "slave loop object," indicating this object is providing some sort of functionality and sending messages to an "owning" vi. The core slave loop pattern consists of four vis. Three of them, CreateSlaveLoop, ExecutionLoop, and Destroy, execute in parallel to owner's processing loop. The fourth slave loop vi is the messaging vi, ExitLoop. Here's a simple example of what it looks like when used. As expected, the creator method sets up the object's initial conditions and obtains the required resources. At a minimum the object needs an input queue to receive messages from the owning vi and an output queue to send messages to the owning vi. The execution loop can contain anything you want to run in parallel to the owner's main process loop. Usually I'll use some kind of message handler, like this, though I also have used the slave loop pattern to combine several other components and expose a simplified api to the higher level components. (The "facade pattern.") The destroy method is boring and predictable, but here it is. I return the Output Queue to the caller, mainly as a way to indicate to programmers using the slave object that the slave is not releasing the queue. I don't think I've ever used that output for anything but it's there if I need it. Finally, here's the ExitLoop message method. What's this do? Nothing but send a message from the owner's loop to the slave loop executing in the ExecutionLoop method. All new methods I add to the class I'm implementing are messaging methods similar to this. Very simple. The natural question is why bother wrapping all that functionality in a class? Isn't this a lot of extra effort that takes more time and makes the application more complex? Complexity in this context is a matter of perception and depends to a large extent on what you're familiar with. For some this will be more complex until they become familiar with OOP ideas. For others (like me) this is easier to understand because I can tackle parts of the application without needing to comprehend the whole thing. I don't think there's an absolute answer to the complexity question. As for it being a lot of extra effort, no, it isn't. The core of a slave loop class can be hammered out in a few minutes. Less if you have a template class you can copy and paste into your project. That's certainly more time than the 30 seconds it takes to set up a parallel loop on your main BD, but I save a lot of time in other ways. -Since the slave loop's input queue is private, I'm forced to write messaging methods to send it data. This lets me enforce type safety on the message's data because I--the slave loop developer--have control over what data types are exposed on the messaging method's connector pane. If my slave loop has a FormatText message, I drop a string control on the messaging method, wire it up, and I'm done. Since the only way to send that message is by calling the FormatText method, and the FormatText method only accepts string inputs, I don't have to worry about handling cases where the caller might accidentally send an integer. Any mismatched data types are compiler errors instead of runtime errors, so there's a bunch of testing and reviewing I don't have to do. -When using multiple slaves, I don't have to worry at all about accidentally sending a message to the wrong loop. The slave object can only be wired to it's own methods. Again, a mistake here is a compiler error instead of a runtime error, saving me a bunch of time down the road. -The names of the messages are not exposed to the calling vi. Whether you use strings or enums for your message names, you can run into problems if you ever try to change them. Since those names are never exposed or even known outside of the class, it is much easier to make that change and be confident you haven't accidentally broken functionality elsewhere. -Each message to the slave loop is a class method so it is very easy to discover what messages it accepts. I just go to that folder in the project window and look them over. I'll add any pertinant details to the vi's documentation so context help tells me everything I need to know. I don't ever have to dig in the execution loop itself to refresh my memory about what messages to send or what data types to use. All the gory details are encapsulated away and I have a nice clean api to work with. ------------------ As they say, the proof is in the pudding, so here are some block diagram images of execution loops I've created for a project I'm currently working on. The first slave loop is called by my top level UI and is an example of a facade. Notice it has three slave loops of its own. Each of those slave loops were created and tested earlier in the project. Those slave loops were designed to work together, but if I expose them directly to the UI then the UI has to handle all sorts of messages it doesn't really care about. That can be confusing so I created this slave loop object to hide that complexity from the UI. The second execution loop is from the 'DutCont' slave in the above diagram. In this instance I had a slave loop, 'XFireServer,' that has most of the low level functionality I needed, but I needed a watchdog timer to automatically send periodic messages. XFireServer's api is pretty low level, reading and writing to usb pipes, so I also wanted to create a higher level api with methods specifically for the device this tool is testing. This class encapsulates all that functionality and presents it as an easy-to-digest single class with only 7 public methods. As always, comments and critiques are welcome. [Edit Feb 23, 2011 - Added SlaveLoop template class.] Here's a template class with the basic SlaveLoop elements. It's built in LV2010 and does require the LapDog MessageLibrary package, included as part of the zip file. LD SlaveLoopTemplate.zip 3 Quote Link to comment
Onno Posted February 11, 2011 Report Share Posted February 11, 2011 Thanks so much for the insightful, inspiring write-up! Plenty of food for thought, I'd say I rather like the idea of wrapping messages with method VIs, to ensure data validity. It does seem a bit cumbersome, but I guess that it may well be a lifesaver in bigger projects. I'll have to consider it... One small question: what's the data type of your queues? Quote Link to comment
Black Pearl Posted February 11, 2011 Report Share Posted February 11, 2011 Amazing speed on the forums today. This answer was posted before I posted the question. Felix Quote Link to comment
Aristos Queue Posted February 11, 2011 Report Share Posted February 11, 2011 Excellent writeup. Daklu: Am I correct in guessing that "Output Queue" is a class that specifically only exposes a Dequeue method and "Input Queue" is a class that specifically exposes only an Enqueue method? You don't explicitly call that out in your text, but it seems like that's what you're doing. Quote Link to comment
ShaunR Posted February 11, 2011 Report Share Posted February 11, 2011 Just curious....... In terms of code metrics. How much is framework and how much actually does something in the physical world (e.g read/writes from an device, updates a display, plays Mozart on a xylophone etc) Any program needs it's infrastructure (of course). I'm just wondering what the trade-off is (you don't want to write 100 VIs just to turn an LED on/off for example). Quote Link to comment
Daklu Posted February 11, 2011 Author Report Share Posted February 11, 2011 This answer was posted before I posted the question. Really? You were just asking yourself, "I wonder what crazy idea Dave has cooked up recently?" [Comment regarding the similarity to the Actor framework he recently published seems to have been edited out.] Yep, this is similar to the Actor framework. It's the result of a conglomeration of ideas I've picked up from various places, and the many discussions I've had with you has been a primary source of those ideas. I don't think the slave loop concept is revolutionary, but for me it is an evolutionary step forward in my code. I view the slave loop object as an intermediate step between a naked loop and an actor object. Since there's no dynamic instantiation the slave loop is easier to develop and debug than an actor object. It's also easier to understand than my prior actor object implementations. One thing I really like about this pattern is that if the time comes when I need to convert it to an actor, I can do that by adding a single DynamicLaunch method to the class and replacing the ExecutionLoop method with the DynamicLaunch method. None of the messaging needs to change. One small question: what's the data type of your queues? The queue data type is Message.lvclass; however, the "queue" wire you see is not a queue refnum. It's the class wire for MessageQueue.lvclass. (More details below.) Daklu: Am I correct in guessing that "Output Queue" is a class that specifically only exposes a Dequeue method and "Input Queue" is a class that specifically exposes only an Enqueue method? You don't explicitly call that out in your text, but it seems like that's what you're doing. Nope. I have a generalized object-based messaging library that is a foundational component of all my code. (Link to a copy that is very similiar to what is shown--right down to the icons.) [Edit - Ahh, I think I understand the potential confusion. InputQ and OutputQ aren't the names of the classes--they are labels signifying how each of the objects are used within the class. Both objects are instances of the MessageQueue class.] MessageLibrary contains a handful of core classes: MessageQueue.lvclass - This is essentially a wrapper for the queue prims with a couple exceptions. First, you can't define the data type. The primitive queue data type is always Message.lvclass. Second, the DequeueMessage and PreviewMessage methods contain logic to output special messages if there's an error on the input terminal ("QueueErrorInMessage",) if the queue prim generates an error ("QueueErrorMessage",) and if the primitive times out ("QueueTimeoutMessage".) Both DequeueMessage and PreviewMessage output the message name (wired into the case structure) and the message object for retrieving the message data. Message.lvclass - This is a base class from which all messages inherit. It is for sending messages that don't contain any data. It contains a single property ("Name" - string) and two methods: CreateMessage and Get MessageName. Here's how they look on the BD. Get MessageName is a static dispatch method so child classes cannot override it. It's an accessor with some extra logic. If the user defined a message name when the object was created, Get MessageName returns that string. If a name was not defined, Get MessageName returns the name of the class as the message name. Here's the BD. ErrorMessage.lvclass - This is a Message subclass that contains an error cluster and two methods: Create ErrorMessage (with a Name input) and Get ErrorCluster. It's part of the framework because it is the object type returned by the DequeueMessage and PreviewMessage methods when they deliver QueueErrorMessages and QueueErrorInMessages. I also use it extensively for transmitting error information around my apps. Originally I subclassed Message.lvclass for every message I wanted to send and did not provide the ability to give the messages names (aside from Message.lvclass, which always needed a message name.) The strict typing and class-based message name meant I didn't have to worry about matching the message name string in the sending vi with the message handling case strings in the receiving loop. I just had to make sure the message handling case strings in the receiving loop matched the class names in the project window. It was much easier for me to verify the code was correct. In practice I found that to be quite cumbersome during development and confusing, especially as messages propogate up and down through application layers. I've recently added some additional capabilities and add-on libraries that give up some type safety in exchange for simplifying the dev process. Unfortuately I haven't rolled them into the released package yet. How much is framework and how much actually does something in the physical world. What metric should I use? Number of vis? Bytes of hard drive space? Block diagram area? As for "doing something in the real world," I assume you mean "actually helps meet the functional requirements?" There's a lot of processing taking place that doesn't have a physical aspect but is still required to meet the functional requirements. Quote Link to comment
Black Pearl Posted February 11, 2011 Report Share Posted February 11, 2011 Really? You were just asking yourself, "I wonder what crazy idea Dave has cooked up recently?" I was contemplating about producer/consumer design pattern and LVOOP. Well, it's a real world app I'm coding in LV7.1, so I don't really have OOP. I use type def'ed clusters and accessor VIs, organization 'by-class' in folders and Tree.vi, marking Accessors as 'private' by the Icon. Pretty primitive, no inheritance and dynamic dispatching. But as this is all self-made rules to mimic some of the advantages of OOP, I need to do a lot of thinking about such design decisions. Now the code is simple DAQ/presentation. A super-cluster/über-class (think of it as a composition of objects for all kind of settings made for the measurement) is coming into that VI. Then it's 'split' in a 'method' of that super-cluster/über-class. The super-cluster/class continues to the DAQ/producer loop with all the settings for the actual DAQ measurement and the signal processing (might be split into more loops later). A kind of factory method is creating a new object that contains the display-settings and the pre-initialized data. This goes down into the consumer loop. At this stage, I also create the P/C queue and it goes into both clusters/classes. To compare this to your implementation: * I use a factory method to create the slave-classes. This is where I pass the queue (in this case I also create it) and other information (*), the queue is invisible on the top level. * As this is a pure data P/C and not a message passing, I stop the consumer by destroying the queue. * Because the consumer is writing to an indicator, the 'loop' is top-level, but only contains a single VI+the indicator. (*) I'm unsure how this will evolve. Maybe all the settings needed in the consumer are just a pseudo-class on their own. But queue and those settings would be a nested-classifier only shared between the über-class and the consumer class. Now, just some thinking I had about this design: The main concern is merging the data from producer and consumer. The measurement report will need the settings from the über-class and and the measurement data. So several options I have: * Merge both loops clusters, kind of brute-force. * Modify the 'Send data' method to both call an (private) accessor of the über-class and hold the measurement data in both wires. Then I just 'kill' the consumer/display object. * Just put everything into the über-class. Then I call a parallelize method that creates the queue, branch the wire in both loops. Combine with above method. When the producer/consumer part is finished, actually both classes should be equal. Then I can reconsider everything when moving onto the next task. So, I was really planning to post the question here. But part of this projects challenges is, that I always get distracted by something more pressing. So it's hunting me since more than 5 years. And of course, each time I rewrite all from scratch because I know a much better way of doing it. Felix Quote Link to comment
Daklu Posted February 11, 2011 Author Report Share Posted February 11, 2011 * I use a factory method to create the slave-classes. Does each of your slave classes have it's own factory method? If so, then I think we do the same thing but just give them different names. In my mind, if a method instantiates the class it's a member of, I call it a creator. It is (again in my mind--no idea if this is universally true) a "factory" if it instantiates and returns an instance of a different class. Here's a factory method I have implemented as part of a factory class. This particular factory method correlates data from a bunch of different sources and creates a GutStep object based on that data. The main concern is merging the data from producer and consumer. The measurement report will need the settings from the über-class and and the measurement data. So several options I have: "Producer" and "consumer" don't have to be fixed attributes of a specific loop. They can be roles. Furthermore, the roles can change at various stages of your application. Nearly all my loops both produce messages for other loops and consume messages from other loops. What's preventing you from adding a 'receive' message queue to your producer loop and having your consumer loop send it the required information? Any chance you could throw up a block diagram? Quote Link to comment
Black Pearl Posted February 12, 2011 Report Share Posted February 12, 2011 Does each of your slave classes have it's own factory method? If so, then I think we do the same thing but just give them different names. In my mind, if a method instantiates instances of the class it's a member of, I call it a creator. It is (again in my mind--no idea if this is universally true) a "factory" if it instantiates and returns an instance of a different class. Here's a factory method I have implemented as part of a factory class. This particular factory method correlates data from a bunch of different sources and creates a GutStep object based on that data. Yes, that's how I do it. Creator is a better term than factory in this case. The creator-object knows (directly or indirectly) all information that is needed to initalize the object-to-be-created. Using a create-method, there is no need to expose this information publicly. "Producer" and "consumer" don't have to be fixed attributes of a specific loop. They can be roles. Furthermore, the roles can change at various stages of your application. Nearly all my loops both produce messages for other loops and consume messages from other loops. What's preventing you from adding a 'receive' message queue to your producer loop and having your consumer loop send it the required information? Interesting point. I guess, to use 'roles', I'd need inheritance. But the requirements are really simple for that section of code. Aquire->Analyze/Process->Present. So it could even be done with 3 VIs wired as a chain inside a single loop (for now, aquire and process are still together). There is no need to call back to the aquisition loop. But the code needs to scale well and be modular, as I have to implement more features (from single channel-measurement to multi-channel measurement, this will lead to n Process-loops; changes in the Aquisition code should not affect processing). Any chance you could throw up a block diagram? I can make a screenshot the next time I develope on the target machine. But it's just prototype-code that gives us a running setup where I and others (non-software people) can see the progress of our work/test whatever we do. Other modules are not clean-coded yet, but just a fast hack on top of the low-level drivers. Felix Quote Link to comment
Michael Aivaliotis Posted February 12, 2011 Report Share Posted February 12, 2011 Daklu, any chance for you to attach your code instead of just images? Quote Link to comment
Daklu Posted February 13, 2011 Author Report Share Posted February 13, 2011 Daklu, any chance for you to attach your code instead of just images? I'm planning on putting up SlaveLoopTemplate.lvlib (you know... the uninteresting part) soon, but unfortunately I can't post the code from the real-world apps. I did convert the Timer Loop shown above into a generic slave loop. I'll see if I can dig it up and post it next week if you'd like. Interesting point. I guess, to use 'roles', I'd need inheritance. Nope. You're overthinking it. The 'producer' is simply the loop that sends the information. The 'consumer' is the loop that receives the information. All it means is that (almost) every loop has both a receive queue and one or more send queues. But the requirements are really simple for that section of code. Aquire->Analyze/Process->Present. But the code needs to scale well and be modular, as I have to implement more features (from single channel-measurement to multi-channel measurement, this will lead to n Process-loops; changes in the Aquisition code should not affect processing). Interesting problem. Why n process loops? Is each channel analyzed differently? Quote Link to comment
Black Pearl Posted February 13, 2011 Report Share Posted February 13, 2011 Interesting problem. Why n process loops? Is each channel analyzed differently? The DUT is excited by n-channels. I only measure a single response signal. Each of the excitation channels is modulated by a unique frequency. Each of those n processing loops will filter out the response at one of these frequencies. Each of these channels will be also presented seperatly. Ahh, things will even be more complicated, as I measure other channels as well, do the same kind of processing. With all this data, now I'll do some physical calculations. Now I get some more data sets to present, both n-channel and 1-channel. But I have a clear directional data flow, as opposed to a messageing system where the objects talk in both directions. So certain stages of the processing could be done better by traditional procedural chain wireing of vi's with some for-loops. But then again this is tricky when it comes to the filters, as they need to keep the history of the signal. So I'll need to keep their state seperately for the different channels. Well, nothing I really want think too much about sunday morning. Felix Quote Link to comment
ShaunR Posted February 13, 2011 Report Share Posted February 13, 2011 The DUT is excited by n-channels. I only measure a single response signal. Each of the excitation channels is modulated by a unique frequency. Each of those n processing loops will filter out the response at one of these frequencies. Each of these channels will be also presented seperatly. Ahh, things will even be more complicated, as I measure other channels as well, do the same kind of processing. With all this data, now I'll do some physical calculations. Now I get some more data sets to present, both n-channel and 1-channel. But I have a clear directional data flow, as opposed to a messageing system where the objects talk in both directions. So certain stages of the processing could be done better by traditional procedural chain wireing of vi's with some for-loops. But then again this is tricky when it comes to the filters, as they need to keep the history of the signal. So I'll need to keep their state seperately for the different channels. Well, nothing I really want think too much about sunday morning. Felix Thats fairly painless with a database. Log all the raw data to the DB. Make your filter re-entrant (notch/bandpass filter?).and just operate it on Selects from the database as many times as you like - in parallel with different settings. Any channel, any number of data points, any frequency - 1 re-entrant filter (simple case of course). Compare channels, cross correlate,,,you name it. You also don't need to keep state information or bung up your memory with a huge history. Just a thought. Quote Link to comment
Black Pearl Posted February 13, 2011 Report Share Posted February 13, 2011 Thats fairly painless with a database. Log all the raw data to the DB. Make your filter re-entrant (notch/bandpass filter?).and just operate it on Selects from the database as many times as you like - in parallel with different settings. Any channel, any number of data points, any frequency - 1 re-entrant filter (simple case of course). Compare channels, cross correlate,,,you name it. You also don't need to keep state information or bung up your memory with a huge history. Just a thought. Thanks for the input, but I'm unsure if this will work. There is no written specs for this app, and I even plan to use in in a whole family of setups. That's why I dedicate a layer to the 'componentizing'. One worst case estimate would be running the meaurement over the weekend at 500kS/s. With processing I get an estimate 1S/s (from literature, my processing is better, so I'll certainly hit other limitations; well they can be overcome as well and then ....). But a hybrid solution could work, do the signal pre-processing with producer-consumer pattern and push the reduced data into the DB. Then do the physical calculations as a post-processing. The more I think about this, the more I like it to use a DB... I'll add it to the feature list. Felix Quote Link to comment
ShaunR Posted February 13, 2011 Report Share Posted February 13, 2011 Thanks for the input, but I'm unsure if this will work. There is no written specs for this app, and I even plan to use in in a whole family of setups. That's why I dedicate a layer to the 'componentizing'. One worst case estimate would be running the meaurement over the weekend at 500kS/s. With processing I get an estimate 1S/s (from literature, my processing is better, so I'll certainly hit other limitations; well they can be overcome as well and then ....). But a hybrid solution could work, do the signal pre-processing with producer-consumer pattern and push the reduced data into the DB. Then do the physical calculations as a post-processing. The more I think about this, the more I like it to use a DB... I'll add it to the feature list. Felix I don't want to hijack Daklus thread.(too important). I have to do acquisition stuff all the time. I used to do the classic acquire-process-display or producer-consumer. Now I just run acquisitions daemons direct to the DB and completely decouple the acquisition from the processing and reporting. Much more flexible and means you can swap reporting modules in and out (even while live acquisition is taking place) without having to manage the transitions or worrying about filling queues, memory or synchronising. However, direct streaming is only feasible up to about. 500 samples per second, reliably, without (as you rightly say) some intermediary processing and buffering. But that is part of the daemon so you don't lose any of the benefits. I think your proposal would work extremely well since you can decide how much processing you want to put on either side of the divide (it could be staged and split). Quote Link to comment
Black Pearl Posted February 14, 2011 Report Share Posted February 14, 2011 Any chance you could throw up a block diagram? This shows the top level implementation. The main object is the 'MAP'. The slave is the 'MAP D.' Felix Quote Link to comment
Black Pearl Posted February 22, 2011 Report Share Posted February 22, 2011 Just ran into some error handling issues and reviewed this document. How do you handle errors? Specifically, when in the master loop an error occures, I would guess the ExitLoop-Message won't be send. My own problem was that an error in my producer loop did make the Accessor VI for the queue return a 'not a refnum'-const so the Destroy Queue didn't stop the consumer. Felix Quote Link to comment
Daklu Posted February 23, 2011 Author Report Share Posted February 23, 2011 How do you handle errors? Specifically, when in the master loop an error occures, I would guess the ExitLoop-Message won't be send. Here's how I approach error handling... (hopefully I can explain in coherently.) I imagine each loop as a separate entity in my app. Each of these loops has a specific responsibility: mediator, data producer, data processor, etc. Then, within the context of that loop's responsibilities, I figure out how I want that loop to respond to errors. I try to make each loop self-recover as much as possible. Sometimes that means the loop will "fix" the error on it's own. (Got a bad filename for saving data? Create a temporary file name instead.) Lots of times the loop simply reports the error and goes into a known, predefined state, after which the app user can retry the process that caused the error. I know that's a pretty vague description. Here are some more specifics. They all tie together into a system that works well for me, but it's past the stupid hour and I can't figure out how to lay it out all nice and neat... --- a. All my loops support bi-directional communication. A single queue for receiving messages (input queue) and at least one queue for sending messages to other loops (output queue(s)). --- b. The preferred way to stop loops is by sending it an "ExitLoop" message. --- c. A loop automatically exits if it's input queue is dead. (Can't receive any more messages. Fatal error... Time to stop.) --- d. The loop automatically exits if it's output queue(s) is dead. (Can't send any more messages. Fatal error... Time to stop.) --- e. The loop always sends a "LoopExited" message on its output queue(s) when it exits to let whoever is listening know. If the output queue is already dead the error is swallowed. --- f. I don't connect a loop's error wire directly to the loop's stop terminal. I want the loop to stop when *I* tell it to stop, not because of some arbitrary unhandled error. (Cases c and d are safety nets.) LapDog's messaging library (especially the DequeueMessage method) is an integral part of my error handling. In fact, having the error wrap around the shift register and go back into the DequeueMessage method's error in terminal is the main way errors get handled. DequeueMessage reads the error on the error in terminal, packages the error cluster in an ErrorMessage object, and instead of dequeuing the next message it ejects a message named "QueueErrorInMessage." In that message handling case I'll check the error and decide what action to take. When an owning loop gets a message that a slave loop has exited, it decides whether it can continue operating correctly without the slave. If so, it will make adjustments and continue. If not, it shuts down its other loops and exits too. My own problem was that an error in my producer loop did make the Accessor VI for the queue return a 'not a refnum'-const so the Destroy Queue didn't stop the consumer. Hmm... do you mean to say the prior error on the queue accessor's error in terminal triggered the error case, which in turn returned 'not a refnum' and left the queue active? Quote Link to comment
Black Pearl Posted February 23, 2011 Report Share Posted February 23, 2011 Ok, I'll need to look into LapDog to see how you do it. Hmm... do you mean to say the prior error on the queue accessor's error in terminal triggered the error case, which in turn returned 'not a refnum' and left the queue active? Exactly. Felix Quote Link to comment
Popular Post Daklu Posted February 23, 2011 Author Popular Post Report Share Posted February 23, 2011 My own problem was that an error in my producer loop did make the Accessor VI for the queue return a 'not a refnum'-const so the Destroy Queue didn't stop the consumer. Hmm... do you mean to say the prior error on the queue accessor's error in terminal triggered the error case, which in turn returned 'not a refnum' and left the queue active? Exactly. There are a couple things I do that make your specific problem a non-issue for me. (Factory method image copied from my post above.) First, I've ditched the rule of wrapping all sub-vi code in an error case structure. (Blasphemy! Burn me at the stake!) About 9 months ago AQ and I had a discussion about this. He termed it, "hyper-reactionary error handling" and talked about the impact it has on the cpu's resources. He said, Since the common case is "no error", all that extraneous error checking *has* to hurt performance... I mean, in many VIs, several registers must be permanently allocated to the error cluster because it is constantly in your "most recently used" cache. These days I almost never wrap an entire code block in an error case. Many of my sub vis don't have an error case at all. The questions I ask myself when deciding about an error case are: 1. Do I need the sub vi outputs to be different if there is an error on the input terminal? If so, then I'll use a switch prim or case structure somewhere on the block diagram, though it may not wrap the entire code block. 2. Will the sub vi exit significantly faster by skipping the code on the block diagram when an error is on the input terminal? Yes? Then I'll wrap at least that section of code in an error case. The code I'm looking at here are things like For Loops containing primitive operations that don't have error terminals. String operations, array operations, cpu intesive math, etc. If a sub vi or prim has an error input terminal I don't worry about it's processing time. It will respond to the error input terminal appropriately and I assume (for simplicity) that its execution time = 0 if there is an error in. In the diagram above I'm doing a lot of data compilation to gather the information necessary to create a GutStep object. However, the only primitive operations on this bd are Search Array and Index Array. Is the time saved in those rare instances where there is an error in worth the cost of error checking the other 99.9% of the time when there isn't? Nope, not in my opinion. In fact, this entire block diagram and all the sub vis execute exactly the same code regardless of the error in condition. (I know the sub vis ignore the error in terminal because of the green triangle in the lower left corner.) But low level performance isn't the main reason I've ditched it. I found it adds a lot of complexity and causes more problems than it solves, like the issue you've run into. You've wrapped your accessor method code in an error case because that's what we're "supposed" to do. Why does the accessor method care about the error in terminal? Is unbundling a cluster so expensive that it should be skipped? Nope. Is it the accessor method's responsibility to say, "I saw an error so I'm not returning a real queue refnum because some of you downstream vis might not handle it correctly?" Nope. Each sub vi is responsible for it's own actions when there is an error on its input terminal. In pratical terms that means my accessor methods never have an error case. They set/get what's been requested, each and every time. My object "creator" methods almost never have an error case, also passing the error wire through. Most of my "processing" method don't have an error case either. In the diagram I have 3 different "Lookup" methods. Each of those encapsulates some processing based on the inputs and returns an appropriate output. None of them change their behavior based on the error in terminal's state. However, each of the three Lookup methods are capable of raising an error on their own (I know this because the lower right corner doesn't have a colored triangle,) which would ulitmately result in an invalid GutStep object being sent out. Is it the factory method's responsibility to try and prevent downstream code from accidentally using an invalid GutStep object? Nope. It's the factory method's responsibility to create GutStep object to the best of it's ability. If an error occurs in this method or upstream of this method that might cause the data used to create the GutStep object to be something other than expected, this method lets the calling vi know via the error wire. It's the responsiblity of the calling code to check the error wire and respond according to it's own specific requirements. That's a little bit more about my general approach to error handling. It's more geared around responsibilities--who should be making the decision? (Responsibility-based thinking permeates all my dev work.) I've found that, in general, much of the time I don't really care if there's an error on the wire or not. Many of the primitive functions and structures already know how to handle errors. What do I gain by blindly adding more layers on top of it? Complexity in the form of more possible execution paths, each of which needs to be tested or inspected to verify it is working correctly. Thanks, but I'll pass. ------------ So, now that I've talked your ear off, it sounds like you can solve your immediate problem by removing the error case from your accessor method. All the above is justification for doing that. Of course, that change may cause problems in the rest of your code if you use the <Not a Refnum> output for branching execution flow. 3 Quote Link to comment
Daklu Posted February 24, 2011 Author Report Share Posted February 24, 2011 [General FYI to readers... I've added a SlaveLoop template class to the original post.] Quote Link to comment
K-node Posted February 24, 2011 Report Share Posted February 24, 2011 First, I've ditched the rule of wrapping all sub-vi code in an error case structure. (Blasphemy! Burn me at the stake!) About 9 months ago AQ and I had a discussion about this... Hey Daklu, This thread has been great. I am starting to use AQ's Actor Framework with success on my systems. My derived classes add some features that probably break the proof of stability, but it is a risk that I accept. One of the latest things I did was to not propagate an error up into 'ActorCore.vi'. Once that happens, the Actor is dead. That is not why I am posting however. Your last two posts on Error handling, I think, deserve a thread of its own. Those coming into the top of this thread for the first time may never get to this gem of insight. I can start it if you want. -kugr Quote Link to comment
Daklu Posted February 24, 2011 Author Report Share Posted February 24, 2011 Kugr! Dropping by for your bi-yearly check in? If you want to start an error handling thread and copy over the content you think is worthwhile that's fine with me. Quote Link to comment
K-node Posted February 24, 2011 Report Share Posted February 24, 2011 (edited) Kugr! Dropping by for your bi-yearly check in? Ha Ha! Yup, I am pretty much a voyeur on this this site. I do enjoy your contributions though. I am not full time on LabVIEW programming but I do want to get a common framework that I can use on systems here. AQ's ActorFramework fit that bill for me with some enhancements on the derived Actor classes. For instance, my Actors have a name and know their receive queue - sometimes an actor will send itself a message. Actors also retain the queues of Observers. It seems the intent of the ActorFramework (especially for stability) was that an Actor that created another Actor was the only one that could communicate with that new Actor. AQ can correct me here but that was my take. There was nothing inherent in the framework that restricted this, it was just my understanding. I did not like that restriction and I generally allow a full network of messaging, though I do have the concept of retaining what Actors another created. The biggest change to AQ's framework was to make the Message:Do.vi re-entrant. Within an actor, the queue will keep processing to one message at a time. I was OK to have all actors processing messages in parallel though this does make debugging crazy at times. And I just added queue names to make my debugging a bit easier - now I can see the name of the actor who sent the message. Oh, yes, all of my messages allow the sender to add it's reply queue. Generally the Do.vi only gets the creator's queue, but sometimes I want to reply directly back. This is an added property in my derived message. I have added an ActorFacade class that can be derived to hide the nuances of all of the messages. A message is essentially wrapped in a method of an ActorFacade. The ActorFacade is not an actor though and cannot receive messages. It is usually contained within an Observer. The ActorFacade actually becomes very useful in updating UI controls via CtlRefnums. So it is a bit more than just a Facade with respect to a design pattern, but I am not sure what to call it. I also have ManagerActor (creates new actors via a message), a UIActor (an easy way to put a user interface on an actor), and a PollingActor (for those times when I need to watch for something to change that does not generate events). From there the derivations continue to my specific requirements. I deal with moving fluids so there are generally valves, pressures and temperatures to deal with. They boil down to analog and digital IO. Not sure this was very helpful but it is what I am up to. I will set the thread up shortly for Error Handling. -kugr Edited February 24, 2011 by kugr Quote Link to comment
SteveChandler Posted October 18, 2011 Report Share Posted October 18, 2011 I have a question. What is the best way to handle multiple instances of a slave? Specifically I want to write a slave loop that does communications with an RS232 device but I have at least two of these devices. What I am not clear on is how best to handle slave responses in my mediator loop. The slaves will send messages on the output queue but how do I differentiate between the slaves so I know who (which instance) sent what? One thing I was thinking is to maybe give the slave loop a name property. The name would be set when the slave is created and become part of the response message name. Cases in my mediator would be something like: SerialDevice:DeviceA:Response SerialDevice:DeviceA:Error SerialDevice:DeviceB:Response SerialDevice:DeviceB:Error Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.