Jump to content

LapDog Message Library v1.2.0 Released


Daklu

Recommended Posts

LapDog Message Library v1.2.0 has been released and is available on SourceForge. It packages several classes designed to simplify general purpose messaging for object-oriented programmers. Highlights include:

1. Familiar methods! If you've used a native queue and you're comfortable with LVOOP, you'll have no trouble figuring this out.

2. More convenience! Have you ever overloaded your Default case with error handling, timeout handling, and more? No more! DequeueMessage (and PreviewMessage) checks for errors on the input, errors from the dequeue prim, and dequeue timeout conditions, and returns unique messages for each of those conditions. Say goodbye to the hassle of nested case structures just to handle normal checking procedures.

3. A PriorityQueue! Ever wish you could easily prioritize messages in your queue? The LapDog Message Library includes a class that does just that. Better yet, it allows you to configure the number of priority levels your queue will have.

4. A collection of message classes for native Labview data types to speed up your development. Includes classes to support the following native types: Error Cluster, String, I32, Boolean, Path, and LVObject. If you need something not included, it's a snap to create your own class that inherits from Message.lvclass.

5. Palettes, cool looking wires, and more!

The LapDog Message Library requires VIPM Community Edition (or better) to install. The package currently requires Labview 2010; however, we hope to release a version for Labview 2009 soon.

  • Like 1
Link to comment

LapDog Message Library v1.2.0 has been released and is available on SourceForge. It packages several classes designed to simplify general purpose messaging for object-oriented programmers. Highlights include:

1. Familiar methods! If you've used a native queue and you're comfortable with LVOOP, you'll have no trouble figuring this out.

2. More convenience! Have you ever overloaded your Default case with error handling, timeout handling, and more? No more! DequeueMessage (and PreviewMessage) checks for errors on the input, errors from the dequeue prim, and dequeue timeout conditions, and returns unique messages for each of those conditions. Say goodbye to the hassle of nested case structures just to handle normal checking procedures.

3. A PriorityQueue! Ever wish you could easily prioritize messages in your queue? The LapDog Message Library includes a class that does just that. Better yet, it allows you to configure the number of priority levels your queue will have.

4. A collection of message classes for native Labview data types to speed up your development. Includes classes to support the following native types: Error Cluster, String, I32, Boolean, Path, and LVObject. If you need something not included, it's a snap to create your own class that inherits from Message.lvclass.

5. Palettes, cool looking wires, and more!

The LapDog Message Library requires VIPM Community Edition (or better) to install. The package currently requires Labview 2010; however, we hope to release a version for Labview 2009 soon.

I have been using this and it is pretty cool stuff. I am mostly using it because I want to learn about object oriented programming and this is a nice example.

Can you explain the benefits (other than #2 above) of using objects for messages instead of a string/enum variant cluster? Do you ever override the message class and create methods other than create and get?

Link to comment

Can you explain the benefits (other than #2 above) of using objects for messages instead of a string/enum variant cluster?

In a single word, "encapsulation." That's not a very helpful answer though...

There were a couple things with the string/variant messages that I found difficult. First, changing the name of a message can be difficult or impossible. Since the message name is the only information immediately available I changed it often during development to be appropriately descriptive. Second, in a string/variant message the message name and the message data are inherently different elements. The message sending code can match any message name with any data type. This opens the door to runtime errors that may or may not be found during testing.

By creating your own custom messages that derive from Message.lvclass, you have much more control over the both the message name and the message data. The message object is responsible for ensuring the message name and data match up correctly instead of the message sender. I typically create a 'Create MyMessage.vi' for each of my custom messages that accepts the necessary data types and does not have an input for message name. This greatly reduces the risk of message name/message data mismatches. Since any code that sends the message uses the creator it also means I have more confidence if I change a message name to improve code clarity--the message name is (usually) only used in two place, the creator method and the receiving loop.

It's important to understand that messages that expose a Message Name input on the creator or inherit from RenameableMessage have inherently 'weaker' type safety. (All messages in the NativeTypes library inherit from RenameableMessage. Message and ErrorMessage do not inherit from RenameableMessage.) You're giving the responsibility of making sure the name and data match up correctly back to message sending code. In that respect they're not much different from string/variant messages (though the creator conpane inputs still give you data type information so you don't have to look it up.)

You can create similar protection for string/variant messages by building creator vis for the various message types, but I've never seen anybody actually *do* it. For that matter, I don't think I've ever seen anybody even suggest doing it. There's not enough payoff for the extra effort of making a creator vi that just bundles a cluster. LVOOP *requires* a method to bundle the name and data into a message. For me, building robust code is much more natural (and easier) with the Message Library than with a string/variant messaging system.

Do you ever override the message class and create methods other than create and get?

Not yet, and to be honest I haven't really thought about it. All my message classes have been of the "protected cluster" kind. It's an interesting idea though. For example, with the command pattern you could have Command.lvclass inherit from Message... In general I favor composition over inheritance, but I'll have to think about it for a while.

Wait, how did I miss this? I was under the scope of LapDog was something else entirely more of a general framework.

I really like it, well done!

Thanks. To be honest, currently LapDog isn't much of anything except an idea... a hope... a dream. <queue orchestral music>

LapDog is intended to be a set of components to assist LVOOP programmers. Originally I envisioned small-ish base class libraries for things like Collections and Data Structures. I've since realized a flexible and robust messaging system is absolutely critical for managing larger projects. I'll still work on the other libraries, but I think this is more generally useful than collections or data structures so it took priority. Also, I use the Message Library far more than I use data structures or collections, so it's easier for me see flaws and make improvements.

  • Like 1
Link to comment

You can create similar protection for string/variant messages by building creator vis for the various message types, but I've never seen anybody actually *do* it. For that matter, I don't think I've ever seen anybody even suggest doing it.

I have, in LV 7.0, although in that case I didn't include the message in there, which I should have. That said, there weren't many messages there, so the effort was relatively small.

AX_Scrpt%20Create%20Attachment%20Variant.png

http://forums.ni.com/t5/LabVIEW/Community-Nugget-06-25-2007/m-p/541714#M255221

Link to comment

By creating your own custom messages that derive from Message.lvclass, you have much more control over the both the message name and the message data. The message object is responsible for ensuring the message name and data match up correctly instead of the message sender. I typically create a 'Create MyMessage.vi' for each of my custom messages that accepts the necessary data types and does not have an input for message name.

LVOOP is new ground for me but I am starting to understand it much better thanks to these forums. So the only thing you need the message name for is so that you can wire it into a case selector right?

This has me thinking of an idea for the idea exchange. What if you could wire the object right into the case selector? It would behave like a typedef with the ability to add a case for every value (child) and break if there is no default and you don't have a case for every value.

Would anyone out there kudo an idea like that?

Edited by SteveChandler
Link to comment

LVOOP is new ground for me but I am starting to understand it much better thanks to these forums. So the only thing you need the message name for is so that you can wire it into a case selector right?

This has me thinking of an idea for the idea exchange. What if you could wire the object right into the case selector? It would behave like a typedef with the ability to add a case for every value (child) and break if there is no default and you don't have a case for every value.

Would anyone out there kudo an idea like that?

I'd definitely kudo that.. Add to the idea that the internal connection of the case-selector returns the specific-child class wire, so we don't have to do the cast anymore and we're complete. :thumbup1:

Link to comment

By creating your own custom messages that derive from Message.lvclass, you have much more control over the both the message name and the message data. The message object is responsible for ensuring the message name and data match up correctly instead of the message sender. I typically create a 'Create MyMessage.vi' for each of my custom messages that accepts the necessary data types and does not have an input for message name. This greatly reduces the risk of message name/message data mismatches. Since any code that sends the message uses the creator it also means I have more confidence if I change a message name to improve code clarity--the message name is (usually) only used in two place, the creator method and the receiving loop.

I've used several messaging models in my applications, but this comment and the next few posts confuse me if I'm reading them correctly. If I use an object as the message, I don't see a need to even have a message name (or enumeration, or any supplementary type information). The object itself is the type of message: having a message name from which I need to branch code off of seems to defeat the whole point of having an object message. When my message is an object, the executing code can be decided via dynamic dispatch.

I don't have access to your code at the moment, so maybe I'm misinterpreting the previous few posts in the discussion?

Link to comment

OK, looking at your code now, I think I see where you're going.

I get the idea of a RenameableMessage. Though it flies in the face of any usage I apply messaging to, I get why your message hierarchy would be built the way it is when I look at this class.

In the case of your plain old Message class, I suppose where you're going is for type re-use. You want to be able to separate the actual message from the data type. At least it would keep things a little more light weight in that it would keep the number of classes down if you have multiple messages which have the same fundamental data components. In it's heart it's no different than cluster based systems where the message consists of some form of identifier, a data payload, and maybe other information

post-11742-0-94884500-1298037811_thumb.p

Link to comment

Great job on the release thumbup1.gif

You can create similar protection for string/variant messages by building creator vis for the various message types, but I've never seen anybody actually *do* it. For that matter, I don't think I've ever seen anybody even suggest doing it.

FWIW I like using events most of the time, but I have been creating API's for the messages since I can remember - I agree that the process/object should not expose the message

In terms of the structure I started using what Justin presented after seeing it a@ NI Week, it was close to what I was doing but more defined/refined.

When I don't need multiple instances, a few examples API's looks as follows:

post-10325-0-86115000-1298040110_thumb.p

Link to comment

Argh, my previous post half vanished...

Guess I know what happened when I failed to edit my post: the server probably only got half of the content and truncated it. I've lost my train of thought, but as for the abandoned screenshot, I was saying one bit of code stands out:

post-11742-0-84734100-1298041985_thumb.p

You might run into name collisions if you deal with multiple libraries. I'd recommend examining the flattened class data and extracting the PStrings that form the fully qualified name.

Link to comment

First, thanks to all for looking it over and giving me feedback. If anyone does use it, I'd be interested in what you like/don't like about it.

In the case of your plain old Message class, I suppose where you're going is for type re-use.

Yep. If I'm going to put it in my reuse library I'd like it to be reusable. ;)

In it's heart it's no different than cluster based systems where the message consists of some form of identifier, a data payload, and maybe other information.

That's correct. The key difference between this (and string/variant) data-centric messaging system and the kind of messaging system you're talking about (using the command pattern to send data and the process) is where the message handling code resides--which component is responsible for defining how the message is processed. In the command pattern, the message sender determines how the message is going to be processed by choosing which message to send. That's okay for messages coming into a component; the component will always know how to handle its own messages. It doesn't work so well if the component wants to send messages out to an arbitrary receiver.

For example, let's say you have a reusable component called WidgetPainter that defines a WidgetPainter.Paint message class to start the process and a WidgetPainter.Error class for returning errors to the caller. Each of these classes derives from a base CommandMessage class that defines an abstract ProcessMessage method. For WidgetPainter to be reusable it has to be independent of any application specific code; however, who should define how a WidgetPainter error is handled? The application, right? So we create a subclass of WidgetPainter.Error and name it App.WidgetError. How does WidgetPainter get an instance of the App.WidgetError object to send back to the application when the error occurs? We want to reuse WidgetPainter, so we can't drop the App.WidgetError class constant on WidgetPainter's block diagram. The app has to somehow 'register' the App.WidgetError object with WidgetPainter. That's a lot of 'stuff' to do just to send an error message.

(I know I could have just said "callback" and you would have understood. I spelled it out in case other readers aren't familiar with the concept.)

Using a command-based messaging system with callbacks is arguably a "more correct" OOP design. At the same time, it does have significant drawbacks. As you pointed out, each and every specific message has to be a unique class. There's no way around that. Each specific outgoing message also has to have an abstract parent class defined by the component. So now, in addition to the n message classes you need to create anyway, you have to add somewhere between 1 to n more message classes. Creating a handful of message classes is pretty painless. Creating say... 50... message classes gets old very fast. During active development when things change frequently in my environment, I have found the "one class for each message" approach to be an obstruction. It interrupts my flow too much and slows me down significantly.

A command-based messaging system has another subtle drawback that requires a lot of complexity to get around. The message data contained in the callback object is set at the time that object is created. There's no easy way for the receiving component to update the messages data before executing the processing code, or for the component to retrieve the results from the message process.

There are a couple reasons why I chose this messaging pattern (and prefer it for general purpose messaging) over command-based messaging:

1. A command-based messaging system didn't occur to me. (When I first developed this library a couple years ago I wasn't very familiar with the command pattern.)

2. It's easier to create decoupled components using this system.

3. The implementation pattern is familiar to almost all LV developers, lowering the barrier to entry.

4. My philosophy when designing an api is, "make the simple stuff easy and the hard stuff possible."

On point 4, one common and simple use for messaging is to command a parallel loop to stop. With data-centric messaging you can send a dataless message named 'stop' and case out the execution code. Simple. How do you implement that using command-based messaging? On the other hand, if you want to use LapDog's messaging library to send command-pattern messages, it's pretty straightfoward. Derive CommandMessage.lvclass from Message.lvclass and give it an abstract ProcessMessage method. (I'd probably just use LVObjectMessage to carry the command object, but I don't think it matters.)

In my opinion, the command pattern is a very good solution to specific problems the meet specific constraints. I don't think it makes a very good general purpose messaging system. However, as with all design decisions, it boils down to a subjective evaluation of the cost/benefit ratio.

You might run into name collisions if you deal with multiple libraries. I'd recommend examining the flattened class data and extracting the PStrings that form the fully qualified name.

Yeah, I discovered that about 6 months ago. It's part of the reason I created renameable classes, though that is only a partial solution. Fully qualified names don't completely solve the problem either. It's possible (I think) for a different class with the same fully qualified name to be in memory instead of the intended class. My brain starts to fuzz out when I start thinking about those kinds of scenarios. Regardless, the only foolproof solution I know of is for the developer to be aware of situations where he will receive different messages with the same name. If he can't change the message names, he'd have to set up a secondary loop to receive messages from one of the senders and repackage that data in messages with new names before sending it to the main loop. (This is a simple example of the adapter pattern.)

I did consider returning the fully qualified name (though I didn't think of examining the flattened string.) I rejected it for a couple reasons:

1. It requires a lot more typing in the case titles, which means more opportunities for mistakes.

2. It requires more updates when refactoring code and moving things around. Again, more opportunities for mistakes.

3. It can add significant overhead to the DequeueMessage method to address what is ultimately (IMO) a corner case scenario.

4. In keeping with my api philosophy, there's an easy way for developers to get fully qualified names if they want them: Subclass MessageQueue.lvclass and override DequeueMessage.

FWIW I like using events most of the time

I know we've discussed it before, but I typically only use events for sending messages to my fp event handling loop. I have found the message object to be useful in those scenarios. I'll create a single user event using the Message object as the type. In the event structure I'll wire it directly into the Get MessageName method and case out the message names.

Link to comment
  • 13 years later...

Here's a new take

On 2/17/2011 at 11:01 AM, SteveChandler said:

LVOOP is new ground for me but I am starting to understand it much better thanks to these forums. So the only thing you need the message name for is so that you can wire it into a case selector right?

This has me thinking of an idea for the idea exchange. What if you could wire the object right into the case selector? It would behave like a typedef with the ability to add a case for every value (child) and break if there is no default and you don't have a case for every value.

Would anyone out there kudo an idea like that?

Here's slightly different take on that idea

>> LabVIEW Idea: Enumerated Variant <<

It works with, but is not limited to, classes.  It's inspired by languages like Rust (enums) and Zig (tagged unions)

Native Controls

Native Case Structure

Link to comment

Wow this thread is a blast from the past. I have actually used LapDog in a project many years ago!

And Steve called it: 

Quote

What if you could wire the object right into the case selector? It would behave like a typedef with the ability to add a case for every value (child) and break if there is no default and you don't have a case for every value

I remember reading this thread and totally glossing over this idea, which I now 100% support.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

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