Jump to content

Examples: Messaging with objects, Command and State Patterns, configuration with XML


Recommended Posts

OK, I finally finished a version of a document I have been promising to write. We put it on our site: OOMessagingCommandPatternStatePattern.

Very cool. Haven't read it yet, but you get kudos just for taking the time to write a 24 page document for the rest of us. :thumbup1:

--------------

Okay, I've read through it a couple times and really like it. I'm going to forward it on to our other developers and suggest they read it. To my eyes it looks sound. Of course, maybe that's just because I agree with all the important points you brought up. ;) Unfortunately, as you pointed out that solution does appear to be fairly heavily reliant on the DSC module. Without the free events the DSC module provides everything becomes more complicated. (I think... I have no experience with shared variables or the DSC module.) Nevertheless, the principles you describe can be applied without shared variables. Good job!

I ended up almost in the same place as you did with my recent state machine soapbox. The principles are the same (using references/shared variables to access the SM inputs and unique classes for each state) but my implementation is a bit different. I think there might be one key difference between our implementations, but I want to take some time to write up my idea correctly. I'll try to post it soon.

I despise action engines but accept FGs as a valid way to share data. I don't quite follow your critique of AE's though. On page 20 you said, "We can implement this 'action engine' simultaneously in other applications, and each of these can change the state." AE's are still restricted in scope to the specific application aren't they? If you're running from source code it will execute in that project's context. If you've compiled an executable the OS executes it in it's own app space. Am I missing something?

Link to comment

Very interesting, thanks Paul for putting together so much good information. You have given me some new ideas!

I have developed a system that is similar except I use NSV with variant payloads to message my cRIO processes.

I also rely on Citadel and DSC SV events. I agree that it would be nice if they became a native part of LV. I thought someone

(maybe you) told me that DSC runs a service that polls the NSV's for change and then somehow generates the user events.

Link to comment

This is how I implemented a state machine without shared variable events.

post-7603-095289000 1279553290_thumb.png

Like you, I have each state defined as a separate class. Since I don't have shared variable events, I need a different mechanism for getting the input (control) signals to the state machine. StateMachineInputs is a by-ref class that encapsulates those signals. In this case it is just 3 booleans for the Start, Abort, and Exit signals.

I also have 3 separately defined sections for each state: Entry Actions, Input Actions, and Exit Actions. Entry Actions are things that happen once every time the state is entered. Input Actions are those things that happen continuously while the state machine is in a given state. This method monitors the state machine input signals for the right set of conditions that prompt a state transition. Exit Actions are things that happen once every time the state is exited.

Splitting the state's actions into three categories helps me decompose the problem into an implementable solution. Wrapping the Input Actions in their own loop avoids the QSM's inherent problem of continuously reentering the same state, which makes entry and exit actions somewhat cumbersome to implement. I could get the same behavior using a single Execute.vi and coding the entry and exit actions inside that method. I like to explicitly define entry and exit action because it helps me make sure my states are cohesive. At this point I'd consider the difference a matter of personal preference.

Link to comment

I'm going to butt in here for a few questions which are occupying me at the moment. I'm not terribly qualified to actually engage in this discussion but if someone could answer a question or two for me I'd be very grateful.

I have made some attempts into encapsulating different kinds of functionality into some of my code in the past with varying levels of success. While I'm aware of coupling and cohesion issues as well as basic data abstraction I'm not very up-to-speed with the different design patterns out there. I always run into the situation where I can do nearly everything from within a class (including display and user input by using sub-panels) but at some stage there comes a point where you need to get at the actual data. Sharing of information between different steps of a LVOOP state machine would be one example.....

How does one go about solving this problem (Or am I wrong in even identifying this aspect of things as the problem I am having).

When looking at the examples Paul has in his PDF linked above I am left wondering (in the case of the "Save" button push for example) where does the object gain access to the data it needs to save..... The object itself does not contain the data right? So where does the data-to-object boundary take place and how does one go about implementing it without eroding the encapsulation of the classes too much.

The only way I can think of is hard-wiring a datatype to the entire family of obejcts (QUEUE or whatever) and retaining access to all the data internally but this seems like it's violating the encapsulation from the inside (if that makes any sense).

Sorry if my ramblings don't make much sense, but this is simply an area where can see the benefits of LVOOP, but have problems actually envisaging the to-and-fro of data between the "Program" and "Objects".

Shane.

Shane.

Link to comment

Shane,

I'm not sure I will quite answer your question, but I will try.

In the case of the configuration files application, when the user clicks "Save" the View application passes data in an object to the controller. You are correct that the object does not (at that point) contain the specific data for the control in the object. In this case I opted to implement this by including a reference to the control in the object, and then I use the reference to read and write the data. (I needed the type as well the data values in order to implement the XML reads and writes.) In this case decoupling isn't absolute, but still nearly so and I think this is a reasonable trade.

In the State Pattern examples, however, all the actual data is in the <Component>Data Model object. (The object is actually a complex composite.)

post-6989-015363500 1279640626_thumb.png

It is straightforward to read or write <Component>Data in the Controller when I need it. post-6989-025338900 1279641314_thumb.pngpost-6989-086981200 1279641197_thumb.png

I define external interfaces to share data with the View or external applications. In my implementation the data communication is via shared variables.

[Aside on a fine point I noticed: In the next application I decided I could merge the funcationality in startTriggeredLoop and endTriggeredLoop into one method executing at the end of a loop, which made it easier to inherit and override cleanly.]

Is this kind of what you are asking?

Paul

Link to comment

Without the free events the DSC module provides everything becomes more complicated.

....

AE's are still restricted in scope to the specific application aren't they? If you're running from source code it will execute in that project's context. If you've compiled an executable the OS executes it in it's own app space. Am I missing something?

I just saw your edited post this morning.

On the RT side we generally poll the current values of the shared variables upon receipt of an interrupt from the FPGA, so we aren't using shared variable events there. It's still quite straightforward. Events offer greater flexibility, however, and using them we can do reads only when we have new messages.

No, you aren't missing something--I was. I ran my tests in the development environment, where the scope of the FGVs included all VIs (applications) in the project. If I build the VIs (myApplication, otherApp, otherApp2) into separate executables then the FGVs don't share the same application space, as you point out. That is a a correction to what I wrote but the point is still the same--someone reading the code must know what all FGVs in scope are doing in order to comprehend what will happen in the application when invoking an action. For instance, if I build largeApplication containing calls to myApplication, otherApp, otherApp2, these all share the same scope.

Paul

Link to comment

Shane,

I'm not sure I will quite answer your question, but I will try.

....

Is this kind of what you are asking?

Paul

Yeah, that answers it for the example I listed. thumbup1.gif

I'm always looking for ways to really encapsulate and abstract as much as possible and when I've got 95% of it done, hard-wiring to achieve something similar to what you were describing it seems like I'm compromising all which went before. Maybe I'm hung up a bit too much on abstracting everything. If everything has been abstracted, where do we go from there? wacko.gif

I always tend to try to create re-usable code which is reusable without code changes whereas I'd probably most often be better off assuming that minor changes will be neccessary for minor updatesfrusty.gif. There are exceptions where a truly generic class can be created, but these are rare I think.

Thanks

Shane

  • Like 1
Link to comment

If I can add a bit to what Paul said...

at some stage there comes a point where you need to get at the actual data... How does one go about solving this problem?

Any two components that collaborate to achieve some goal are going to be dependent on (coupled to) each other somehow. Some types of coupling are strong; others are weak. (See here for a list of coupling types.) At the very least they have to agree on the shared data schema. What's it look like? How is it formatted? What does it mean?

How you solve the problem depends on your goal for that particular component. If a component is application-specific, typedefs are an okay solution. (I generally avoid them though.) Since all the code that uses the typedef will be in memory any time the typedef changes you're pretty safe. If the component is intended for general reuse, you're probably better off passing data using native types--arrays of strings, numbers, etc.

Sometimes I don't want the two components (CompA and CompB) to directly depend on each other at all. In those cases I have a few options:

Mediator - I create CompC that acts as a go-between for CompA and CompB, controlling the flow of information. It doesn't matter if CompA and CompB use the same data schema; CompC simply translates one schema to the other and passes along the data. CompC block diagrams use CompA and CompB methods, so it is dependent on them. A and B can easily be reused in other apps, but C must take A and B along with it for reuse.

Dependency Injection - In this case CompC provides a common interface and I "inject" it into CompA and CompB at runtime. CompC could be a TestData class, providing Set Data, Get Data, and SaveData methods. This makes CompA and CompB dependent on CompC as CompA and CompB block diagrams use CompC methods. C can easily be reused in other applications; A and B must take C with them if they are reused.

Which one you use depends on several factors. If A and B have already been created and tested then using a Mediator is the better solution. You don't want to make a habit of rewriting completed code. If you're creating them all from scratch then you can decide what you want your dependency graph to look like and design around it. FWIW, I often implement a TestData class and use it with both a Mediator and/or Dependency Injection, depending on the module that needs the data.

Sharing of information between different steps of a LVOOP state machine would be one example.....

In my case the BaseState class holds the shared information and exposes protected static dispatch accessors allowing the child classes to read and write the data. It may or may not be a good solution... I haven't worked with that state machine model enough to decide.

So where does the data-to-object boundary take place and how does one go about implementing it without eroding the encapsulation of the classes too much?

Your questions sound like the same questions I was asking not too long ago. I'll make the huge assumption that your designs and way of thinking are similar to where I was at that point. My apologies if my assumption is wrong.

When literature refers to cohesion and coupling, they usually refer to it in the context of a specific class. It is certainly important there, but I found that focusing on each class as a separate entity in my applications often resulted in bad code. I ended up trying to make each class do too much.*

I believe my code became much clearer and much more flexible when I started concentrating on decoupling modules instead of classes. I typically use project libraries to organize a group of classes into a module that provides some useful functionality. Since none of those classes are going to be reused independently, it's okay for them to be relatively tightly coupled to each other. To limit the incoming dependencies and preserve module decoupling I'll often make all the tightly coupled classes private and provide one or more public classes as the module's interface.

*As an example, just yesterday I was tracking down .Net memory leaks in a class I had written to control an RF sniffer. The instrument hardware captures data in a queue and 8 ms "frames" of data (each one a .Net reference) are read one at a time. I need to keep all the capture frames in memory so they can be post processed, but I didn't want .Net references running all over my application.

The RfSniffer class' evolution resulted in the class maintaining a history of captured frames, to preserve encapsulation. This class not only provided hardware control (Open, StartCapture, ReadFrame, etc.) but also had to provide data analysis methods. (FindTriggerFrame, GetFrameTimestamp, etc.) The api for the class was a little confusing, but not terribly so. I thought it was a reasonably good design. (This was just last week...) Had I not discovered the .Net reference leak I would have left it as it was.

As it turns out, the RfSniffer class wasn't properly releasing all the captured frames in memory when a new capture was started. Furthermore, because of the overall application design there wasn't a good way to introduce a ReleaseFrameReference methods without violating my layering and creating a ripple effect up through the app. The solution I decided on was to refactor the RfSniffer class into two classes**: RfSniffer for instrument control and RfSnifferData to store and perform operations on captured frames.

It's obvious to me now the new design is cleaner, easier to use, and more flexible. I hadn't really realized it at the time, but I was struggling with how to implement the requirements into the RfSniffer class. It's easy to chide myself for not realizing the problem and figuring out the solution earlier, especially because I often create TestData classes and I'm familiar with the strategy. In retrospect I was so focused on trying to get the product released (it was supposed to be out the door on Tuesday) I made a series of "quick and easy" changes rather than step back and look at the bigger picture. If my design had been correct from the start I could have avoided the reference leak and released on time.

[**I don't always encapsulate modules in libraries. For trivial cases such as this I'll define sub-modules using virtual folders and the project window hierarchy.]

I'm not terribly qualified to actually engage in this discussion but if someone could answer a question or two for me I'd be very grateful.

Meh... I don't know that any of us are terribly qualified for this. I consider all the LVOOP discussions 'group learning.' The more people contribute--regardless of qualifications--the better we all learn.

  • Like 2
Link to comment

If I can add a bit to what Paul said...

Any two components that collaborate to achieve some goal are going to be dependent on (coupled to) each other somehow. Some types of coupling are strong; others are weak. (See here for a list of coupling types.) At the very least they have to agree on the shared data schema. What's it look like? How is it formatted? What does it mean?

...

Thanks for that post.

This is something that just sunk in with me this week-end. I managed to stumble into a messaging pattern but was troubled by the fact that the Messsaging Class only had a generic Class taht it passed around but I needed to slam other stuff into it depending on where it was being used in my grand scheme. I really did not want to cast the generic in the two classes that where using the messaging service because it seemd wrong somehow. I ended up convincing myself that cast the generic class recieved from the Messaging stuff into the class I waanted to share between the two interacting classes was OK.

I tried it out with a couple of variations it worked beter than I expected since the messaging is completely decoupled now so I can re-use that class and scheme again.

The actual message passed are special and unique to the interacting classes so having them both coupled to the class they use to comunicate is OK as well.

So agian tahnks for the post. It is always nice to find you accidently stumbled in an accepable answer.

Ben

Link to comment

Your questions sound like the same questions I was asking not too long ago. I'll make the huge assumption that your designs and way of thinking are similar to where I was at that point. My apologies if my assumption is wrong.

..........

Your answer reflects pretty well where I am in my thinking at the moment. I had the feeling I was slowing myself down by taking the "cohesion", "coupling" and "encapsulation" ideas WAY too seriously and trying to do everything without coupling any two classes..... This obviously gets very hard very quick. Interdependency is OK as long as it serves a common purpose I suppose (coherent). So coupling is OK as long as the coupling takes place coherently?

Shane

Edited by shoneill
Link to comment

Your answer reflects pretty well where I am in my thinking at the moment. I had the feeling I was slowing myself down by taking the "cohesion", "coupling" and "encapsulation" ideas WAY too seriously and trying to do everything without coupling any two classes..... This obviously gets very hard very quick. Interdependency is OK as long as it serves a common purpose I suppose (coherent). So coupling is OK as long as the coupling takes place coherently?

Shane

I should point out that in the configuration files example the View writes the control reference to the Model object (the Command) dynamically at run-time. That is actually an important part of the design. That way I can use any collection of controls I want, without editing the Model code in any way whatsoever. So the Model is still essentially independent.

The alternative is to write the control data to the Model instead of the reference. Then the Model can send updated data (from a file, for instance) to the View, which can handle updating the control in turn. I decided that:

1) since I was using XML it was a little simpler to use the reference

2) since for this application the View will always be the same (just with different controls, which the solution handles gracefully)

it was OK for the View to pass references to the controls to the Model.

Note that in other situations, e.g., where we have a component Controller that may interface with View A, View B, or another Component (which we do all the time) we don't pass references, only "pure data," since the references would not be meaningful in all application contexts.

Paul

  • Like 1
Link to comment

This is how I implemented a state machine without shared variable events.

post-7603-095289000 1279553290_thumb.png

Like you, I have each state defined as a separate class. Since I don't have shared variable events, I need a different mechanism for getting the input (control) signals to the state machine. StateMachineInputs is a by-ref class that encapsulates those signals. In this case it is just 3 booleans for the Start, Abort, and Exit signals.

I also have 3 separately defined sections for each state: Entry Actions, Input Actions, and Exit Actions. Entry Actions are things that happen once every time the state is entered. Input Actions are those things that happen continuously while the state machine is in a given state. This method monitors the state machine input signals for the right set of conditions that prompt a state transition. Exit Actions are things that happen once every time the state is exited.

Splitting the state's actions into three categories helps me decompose the problem into an implementable solution. Wrapping the Input Actions in their own loop avoids the QSM's inherent problem of continuously reentering the same state, which makes entry and exit actions somewhat cumbersome to implement. I could get the same behavior using a single Execute.vi and coding the entry and exit actions inside that method. I like to explicitly define entry and exit action because it helps me make sure my states are cohesive. At this point I'd consider the difference a matter of personal preference.

I've been thinking about this solution. I will make a couple observations:

If the Entry and Exit actions are common to all cases then it makes sense to keep them out of the statemachine, as you have done in your example. We have solutions either way, depending on whether this behavior is state-specific.

For some applications messaging is an advantage because we can separate the View from the Controller not only into separate loops but into separate applications. This means we can create alternative Views that will work with the Controller, and the Controller can work with another component as well. The Controller just knows that it receives messages defined on its interface, and it knows what to do with those data messages. It does not care about the source of the messages. This is very powerful when you need it. (The ability to bind controls to shared variables makes this pretty simple to implement with shared variables, but that's just one solution.)

We use this capability especially since we often want to test a component stand-alone and then integrate it into the system. In the former case we need a local UI, but in the latter case the component usually receives data from another component in the system. Moreover, we use the local UI for local control for diagnostics and an "engineering mode." Of course, the needs of the application will indicate whether this is appropriate or not.

This is how I implemented a state machine without shared variable events.

OK, I'm also curious what your State Transition method does exactly....

  • Like 1
Link to comment

So coupling is OK as long as the coupling takes place coherently?

I can't give an authoritative 'yes,' but that's my take on it.

Each class provides a specific function. For example, I use collection classes fairly frequently. Its sole purpose is to manage a group of arbitrary objects at runtime. It has a few very basic methods: Add Item, Remove Item, Get Item, etc. I'll use a collection class as part of a set of classes (often grouped in an lvlib) to provide some higher level of functionality. I have an EventManagerLibrary I can drop into an observable software component that provides a relatively easy way for that component to manage and raise events from multiple observers. The other classes in the EventManagerLibrary use the collection class' methods and as a result are very tightly coupled to it. That's okay, because the EventManagerLibrary is used as a whole, not broken up into separate parts. (I don't want software outside the library depending on the collection class though, so it's a private member of the library.)

For better or worse, I spend a lot of my design time figuring out and maintaining dependencies and between modules. Somewhere in my reading I saw that described as "managing the seams" and I think that's an good way to phrase it. Once I figure out what my dependency tree should look like from a high-level application view, then I can figure out how to implement it using (when appropriate) various design patterns.

Paul gave some good examples of managing the seam between the Model and the View. In his XML app the Model directly updates the View's fp control. This couples them together to some extent. (Though I believe it's a relatively weak coupling since it's using a native LV type to cross the 'seam.') Lately I've been keeping the Model and View completely decoupled by having them both send and receive messages from the Controller, which acts as a mediator. Is one method right and the other wrong? Nope, it all depends on what you're trying to accomplish. Paul understands the consequences associated with each approach and made a decision based on the requirements. Where it gets dangerous is when decisions are made without understanding the consequences.

If the Entry and Exit actions are common to all cases then it makes sense to keep them out of the statemachine, as you have done in your example.

Actually, the Entry and Exit actions are overridable BaseState methods and are part of the state machine. Each state has the ability to execute arbitrary actions when entering the state, while in the state, and when leaving the state. Moore state machines only allow entry actions. Mealy state machines don't allow any of those, instead opting to associate actions with a specific transition. Decomposing a problem into states using Entry, Input, and Exit actions for each state reduces the number of states required and makes the system as a whole easier to understand and modify. (That's my hypothesis anyway.)

[Note: QSM's are most closely related to Moore state machines, except instead of executing an entry action and then waiting in that state for the next transition like a "real" state machine, in a QSM a state is continuously reentered until the next transition occurs.]

We have solutions either way, depending on whether this behavior is state-specific.

Functionally I think our two approaches are identical. It is possible in your model for the Execute method to have equivalent Entry and Exit operations surrounding a loop that polls the shared variable. I created unique Entry and Exit methods in the BaseState to help guide the way the developer thinks about states in their application. Without understanding the idea of Entry, Input, and Exit actions a developer is more likely going to lapse into bad habits learned from the QSM.

OK, I'm also curious what your State Transition method does exactly....

The BaseState class contains a private LVObject labelled NextState. When a state has enough information to determine what the next state should be, it puts an instance of that state in the NextState variable. This most correctly occurs in the InputActions method, but technically could happen in any of the three methods. StateTransition is a static dispatch BaseState method that simply gets the next state object from the private data, copies the existing BaseState data into the next state object, and puts the next state object on the shift register wire.

I'll try to put together a simple example that I can post online. However, it is the weekend and the sun is out (rare enough in the Pacific Northwest) so no promises on when I'll get it up...

  • Like 1
Link to comment

I'll try to put together a simple example that I can post online. However, it is the weekend and the sun is out (rare enough in the Pacific Northwest) so no promises on when I'll get it up...

Actually, I understand what you are doing from your description.

Enjoy the sunshine!

Link to comment
  • 1 month later...

OK, I finally finished a version of a document I have been promising to write. We put it on our site: OOMessagingCommandPatternStatePattern.

In it we present examples of the following in LabVIEW:

messaging with LabVIEW objects

Command Pattern (with XML configuration files application example)

State Pattern

Hopefully the examples will be helpful to some readers, and promote further discussion on scalable application architectures.

Any chance you can post the example source code? I'm trying to wrap my head around these concepts and an example would help.

Thanks for the great info.

Link to comment

Any chance you can post the example source code? I'm trying to wrap my head around these concepts and an example would help.

Thanks for the great info.

I can't post the source code from the paper. On the other hand, I have been thinking about just coding up the relevant examples from Head First Design Patterns in LabVIEW and making those available. That, I think, might be helpful.

Link to comment
  • 9 months later...

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.