Jump to content
News about the LabVIEW Wiki! Read more... ×
drjdpowell

[LVTN] Messenger Library

Recommended Posts

Messenger Library


An extensive library for passing messages between parallel processes. Generalizes the communication method, allowing the message sender to use the method provided by the receiver. Supported communication methods include wrappings of simple queues, user events, and notifiers, as well a more complex channels such as a TCP server and client. In addition, one can configure simple forwarding addresses (“Observers"), which can send messages to multiple destinations, optionally with modifications such as adding a prefix to the message label, relabelling, or substituting a different message.

Communication patterns supported include request-reply (asynchronous or synchronous), where the reply is sent to a "reply address" attached to the request, and register-notify, where one process sends a registration message to another in order to subscribe to a series of updates.  Also supports scatter-gather, the gathering of replies from multiple senders into an array of messages.

An option framework for dynamically-launched VI "actors" is also provided, including example templates, which can be accessed via the Tools menu (from an open Project, select Tools>>Messenger Library>>Create Actor from Template..).  An "Actor Manager" debug tool is also installed under the Tools menu.  Please note that this package has nothing directly to do with the NI Actor Framework (other than both packages are influenced by the Actor Model).

***Introductory Videos are on a YouTube channel.***

Original conversation on this work is here.

Now hosted on the LabVIEW Tools Network (but note that the latest version will often be on LAVA)

***NOTE: latest versions require VIPM 2017 or later to install.***


 

  • Like 1

Share this post


Link to post
Share on other sites

For anyone using this package, I’ve uploaded the latest version (should really have updated this more than once a year).  I’d like comment on the following new features:

post-18176-0-13114600-1384794166_thumb.p

This is one of three asynchronous dialog boxes.

 

post-18176-0-49400300-1384794241_thumb.p

This can be used in a “delayed message to oneself” form of doing a continuously updating process (as opposed to using the “Metronome” helper actor or the (ill advised) timeout case).

 

post-18176-0-86099700-1384794346.png

Intended for when the reply is uncertain.  With this VI, a process can be confident that it will get a message of some kind back, and thus does not wait forever.

 

post-18176-0-17498800-1384794445.png

Observes an address and triggers a response if that address goes invalid.

 

 

  • Like 2

Share this post


Link to post
Share on other sites

Version 1.2 has the following changes/additions:

 

1) A sample project that is a rework of an NI standard sample project, to illustrate the differences with using Messenging (original conversation):

post-18176-0-51433400-1394716295_thumb.p

 

 

2) New polymorphic VIs for writing messages and synchronous “queries” (send message and wait for reply).

post-18176-0-56278200-1394716378.png

 

The new write VI is smaller and lacks error terminals.  The new “Query&Read” VI allows immediate reading of the reply message.  

 

I deprecated the original “Query” polymorphic VI because of non-consistant error handling.  When error messaged are “read”, they return the error cluster through the "error out” terminal; some of “Query”’s instances read the message, and some just returned the unread message.  The most common use of “Query” I have found is to issue a command and wait to be sure it has completed without error, so it is desirable to consistently return the error cluster.

 

3) An internal change; the “ObserverRegister” publication system has been switched from an asynchronous helper actor to a synchronous DVR-based solution.  This is to prevent messages sent by “notify” from arriving AFTER later messages sent by “reply” (because notifications goes via the helper actor while replies go direct to final recipient).  For example, in the code below from the new sample project, the “Logging” and “Status” messages used to arrive after the “Reply” message.

post-18176-0-68990700-1394716879.png

Share this post


Link to post
Share on other sites

Hello, if anyone is using this package, what version of LabVIEW are you using?  And in particular, are you using the feature where messages containing data in a Variant can optionally take the data name as the message label (as seen below)?  I ask, because the (off-palette) NI “GetTypeInfo.vi" for determining data name is 10 times faster than OpenG’s “Get Data Name.vi" in LabVIEW 2013, but 10 times slower in 2011.  As all my active projects are now in 2013, I am considering switching to using the NI VI (though I am staying with 2011 for package development).

 

post-18176-0-46942300-1395927265.png

Share this post


Link to post
Share on other sites
Hello, if anyone is using this package, what version of LabVIEW are you using?  And in particular, are you using the feature where messages containing data in a Variant can optionally take the data name as the message label (as seen below)?  I ask, because the (off-palette) NI “GetTypeInfo.vi" for determining data name is 10 times faster than OpenG’s “Get Data Name.vi" in LabVIEW 2013, but 10 times slower in 2011.  As all my active projects are now in 2013, I am considering switching to using the NI VI (though I am staying with 2011 for package development).

Sorry I'm not answering the question you ask (because I don't use your messenger but do like it).  But I wanted to mention that I love this type of feature.  The majority of the time I have a state machine of some kind with a BFC (big cluster) with data I pull out and put into stuff and having it get the name from the data and making the name optional is a great feature.  I wonder if it would be worth reading the Application.VersionYear and perform one set of code for 2011, and another for other versions?  I realize this would add to the overhead so it might not be worth it, but if someone is concerned I'm guessing all of that overhead would be skipped if a Label name was provided.

Share this post


Link to post
Share on other sites
I wonder if it would be worth reading the Application.VersionYear and perform one set of code for 2011, and another for other versions?  I realize this would add to the overhead so it might not be worth it, but if someone is concerned I'm guessing all of that overhead would be skipped if a Label name was provided.

 

Perhaps better off a conditional disable structure that way no run time check is performed? If the behavior you settle on is undesired, the user can define a symbol in their own project to revert the library behavior to the original implementation.

Share this post


Link to post
Share on other sites
Perhaps better off a conditional disable structure that way no run time check is performed? If the behavior you settle on is undesired, the user can define a symbol in their own project to revert the library behavior to the original implementation.

If you don't mind I took it a step further.

http://forums.ni.com/t5/LabVIEW-Idea-Exchange/Conditional-Disable-LabVIEW-Run-Time-Versions/idi-p/2795540

 

But going with the tools we have now, I think that defaulting to the NI method is probably best.  There are multiple functions that are both done by NI and OpenG and when possible I choose the NI because I expect with each version LabVIEW gets better NI's methods maybe improved.  Besides I think NI has better support then OpenG.

Share this post


Link to post
Share on other sites

The majority of the time I have a state machine of some kind with a BFC (big cluster) with data I pull out and put into stuff and having it get the name from the data and making the name optional is a great feature. 

Yes, the main application of this feature is for publishing state information.  I have a cluster already with meaningful names and can reuse those names as the message labels.  Nice and clear.  And I only have to name something in one place, rather than two.

post-18176-0-61987700-1395951856.png

 

You can also use in other places where one is dealing in variants already.  For example, the code below connects all controls on a Pane to a remote program via message.  Any Value Change event on any control causes a message to be sent using the control name as label (one could use the control’s “Label.Text” property instead in this case, but at considerable property-node overhead).

post-18176-0-83466300-1395952609_thumb.p

Drop a new control, label it the message label you need to send; job done, with no new coding required.

 

I don’t feel it is worth it to introduce multiple versions, as I’m not likely to use this in 2011.  A conditional disable structure was my first thought, until I found you can't currently disable based on LabVIEW version (kudoed your idea).

Perhaps better off a conditional disable structure that way no run time check is performed? If the behavior you settle on is undesired, the user can define a symbol in their own project to revert the library behavior to the original implementation.

That’s a good idea, though NI really should add a “LabVIEW version” condition (I don’t understand why there isn’t one already).

But going with the tools we have now, I think that defaulting to the NI method is probably best.  There are multiple functions that are both done by NI and OpenG and when possible I choose the NI because I expect with each version LabVIEW gets better NI's methods maybe improved.  Besides I think NI has better support then OpenG.

I wouldn’t have agreed with you when the NI functions were 10 times slower than OpenG; but your right, they are getting improved.

Share this post


Link to post
Share on other sites

Some overheads on common messaging patterns I use from a talk I gave recently:

 

Synchronous Request/Reply

 

post-18176-0-39684200-1411510121.png

 

Asynchronous Request/Reply

post-18176-0-47353300-1411510131.png

 

Subscribe for future Notifications

post-18176-0-50656500-1411510140.png

 

Asynchronous Dialog

post-18176-0-76171800-1411510148.png

 

Asynchronous Multiple-Actor Request/Reply  (aka Scatter-Gather)

post-18176-0-41727100-1411510159.png

Edited by drjdpowell

Share this post


Link to post
Share on other sites

Version 1.3 contains the following significant changes/additions:

 

1) new pallet location under “Data Communicationâ€. 

 

post-18176-0-93020100-1413402977.png

 

 

2) a new “DEV Actor Template†which is my attempt to combine two past templates I have used into a singular one with the advantages of both.  Find the templates under “<LabVIEW>\examples\drjdpowell\Messenging\Actor Templatesâ€.  This design owes a debt to the excellent JKI “state machine†template.

 

post-18176-0-63943800-1413402955_thumb.p

 

 

3) I have had a project where I have dealt with an array of actors (and analysis chain), and this has let me exercise and extend the “FutureToken†part of the library.  A Future Token is a single-use address that can be used to represent and organize messages that have not yet been sent.  This is mostly used to deal with the indeterminate response order when gathering information multiple actors at once (for example, when requesting configuration information from many actors for saving to a single file).  

 

post-18176-0-60696700-1413403686.png

This allows the gathering of multiple response messages into a single “Message Bundle†that can be acted on as a single step.  The Bundle can be easily converted into an array or cluster of the required type. (Originally described in this conversation.)  This is a form of the Scatter-Gather messaging pattern.

 

post-18176-0-53523400-1413403699.png

This helper actor deals with the fact that replies to a series of requests can come back out-of-order, leaving the last reply not consistent with the last request.  I developed this in an app where mouse-move events over icons representing running actors triggered a request-reply to the relevant actor to get a description for display to the User.  Fast mouse moves could exceed the response time of some busy actors, resulting in an inconsistent description.  This is a form of the Resequencer pattern.

Edited by drjdpowell

Share this post


Link to post
Share on other sites

Quick note on possible changes in the next version:

 

We currently have two types of message “notifications†with the “ObserverRegister†helper object:

 

1) Event —> send the message to all currently registered observers.

 

2) State —> compare to last notification message, and if different, send to all registered observers

              —> send the latest notification message to any newly-registering observer.

 

State notifications have more overhead than Events, due to the saving of a copy of the message.   But the ability to send a message immediately to new observers is very valuable, eliminating the need to query or repost unchanged state information, removing the worry about the order of startup of different actors, and making dynamic “hookup†of new actors near instantaneous.   

 

However, the other feature of State notifications, only sending updates of state change, is more problematic.  The reasoning of this feature was to reduce overhead by not sending new messages if nothing has changed, but in day-to-day use I find this isn’t that valuable, and I often actually want all the messages, even if unchanged, because I use them in a Chart/Graph or otherwise have actions that I want triggered regardless.  So I am considering changing it so State notifications are always sent.

 

I am also considering making the matching of message labels in the Observer Register be case insensitive.

 

Comments?

 

— James

Share this post


Link to post
Share on other sites

Notes on the 1.6 version:

 

1) there is a “Create Actor From Template†option under the Tool>>Messenger Library menu.

post-18176-0-83751300-1440274982.png

the DEV template is what I mostly use, though there is also a JKI “Statemachine" version.

 

2) One can now make actors non-reentrant.   To do this one must rename the “Actor.vi†to “ActorNR.vi†in addition to making it non-reentrant. 

  • Like 1

Share this post


Link to post
Share on other sites

I'm experimenting with this framework, and my early impressions are that it's very powerful and flexible. In my experimentation I created a Messenger Event for registering with a sub-actor Observer registry. In the code I can give the User Event a custom label to make the Event Structure case more understandable (green highlighting in image), but I'm struggling to set a custom label for the Messenger Event, which I store in the actor internal data shift register (Blue highlighting in image). Is there a recommended approach that I'm missing? Applying a typecast always fails.

 

post-33316-0-53819900-1452075848.jpg

  • Like 1

Share this post


Link to post
Share on other sites

As the code grew I incidentally came across this solution. Not great, and I should make it a typedef, but it's better that it was.

 

post-33316-0-56073800-1452090170.jpg

Share this post


Link to post
Share on other sites

As the code grew I incidentally came across this solution. Not great, and I should make it a typedef, but it's better that it was.

 

Darn.  I actually had a DEV template modification that demonstrates some of this, but I removed it from the published package because I thought it was too niche.  See a screenshot below.  I also need to use a subVI to name the event registration.   You can also see a “Private Messenger†in the JKI Actor Template.

 

Note, BTW, that I don’t use EventMessengers in the way you are: a different Messenger for each message.   Instead I have multiple messages coming in through one event case, with a case structure on the message label.   The only reason I sometimes have a second EventMessenger is to separate “Public†(or “Externalâ€) messages from calling code, from “Private†(or “Internalâ€) messages coming from my subActors.  So I can prevent calling code from being able to send me private messages, and I can limit what subActors can do (like not tell me to shutdown, for example).

 

post-18176-0-90207100-1452092837.png

  • Like 1

Share this post


Link to post
Share on other sites

I have multiple messages coming in through one event case, with a case structure on the message label. 

 

So do I... now. Much cleaner  :yes:

Share this post


Link to post
Share on other sites

Another question James, this one is more academic. I'm trying to maintain zero-coupling between my actors, and in the current situation I have two independently designed actors called View and ConfigData.

 

ConfigData stores and retrieves the application information from file, including an array of a cluster that contains two elements: the Title (string) and Path (path) of the most recently opened Projects.

 

View actor hosts the User Interface and customises the menu to show the four most recently opened projects, stored internally as an array of Title strings.

 

Whenever the application opens a new Project it must inform ConfigData of the project name, and if this results in a change to the arrayed list of recent projects then the revised list must be shared with View so that it can be ready to show the latest information at all times.

 

So, there are numerous options available for sharing the revised list between ConfigData and View, but I want to maintain zero-coupling - that is, View cannot know of the existence of ConfigData, and likewise ConfigData cannot know of the existence of View. I can use the Observer-Notification registry in ConfigData actor to push updates out, and subscribe to these in View as events. But something else needs to create this binding because these actors can't know of anything outside of their simplified existence. In steps the launching actor Main.

 

Main launches View and also ConfigData and can establish a communications link between them such that changes in ConfigData are immediately sent out on the Observer register to subscribed View. But their datatypes are different, so they can't simply pass the information between themselves because ConfigData's cluster array isn't the same as View's string array. To maintain zero-coupling I cannot simply change one of their datatypes, so there needs to be some conversion of the data somewhere between. (Actually, it probably helps in this example to consider these two actors as something much bigger, with far more complex data payloads to share. That way the simple answer to change one to match the other cannot be recommended).

 

I see from the Messenger Library there are Translator methods, which can cause messages between actors to be mutated, and the data payload adjusted. How do you properly use these to morph the outgoing message from ConfigData into a suitable message for View? I expect that getting Main to establish this translation link will mean both View and ConfigData remain zero-coupled, and also Main doesn't have to maintain a role in the communications whenever a message is passed, which is good for performance of Main.

 

The key for me here is:

1. View and ConfigData must remain zero-coupled (the ideal for any system of reusable actors)

2. Prevent the need for the parent actor Main to be continuously aware of and monitoring the communication path, translating the payload data each time and distracting it from it's primary duties.

 

If I understand Translate properly, it looks to me like an excellent way to establish links between zero-coupled actors without having to establish a route through a parent actor, or any other actor. But maybe there's a better/simpler way?

Edited by Thoric
  • Like 1

Share this post


Link to post
Share on other sites

Hi Thoric,

 

As a quick aside to other readers let me note that it is a goal of Messenger Library that one NOT have to be a whiz at LVOOP to use it.  The standard API is meant to be used by itself to make inter-loop communication easier, without the need to create new classes.  But because it is based on LVOOP internally, one can extend it powerfully using inheritance.

 

 So with the proviso that these are “optional bonus power ups†that are rarely used, then yes, the “Translation†polymorphic VI on the “Observer†pallet is specifically intended for one to alter messages. The standard translations that I use commonly are “Relabelâ€, “Add Prefixâ€, and “Outer Envelopeâ€, but there is also “Add Translatorâ€, which allows one to provide a custom child class of “Translator.lvclassâ€.   The only method that must be overriden is “Translate.viâ€, though you may want a creation method if your Translator need internal info.  See the “LabelPrefix.lvclass†for an example.  The “Translation†method is quite straight forward (see an example below).  

 

post-18176-0-89300800-1452207575.png

 

Even more generally, the “Send†parent class, usually thought of as a generic “addressâ€, can also be considered a “Callbackâ€.   The “Reply†and “Notify†API methods are really callback hooks, where calling code can “hookup†functionality to an actor.  The only important restriction is that the callbacks should not interfere with the operation of the actor (such as by blocking or taking an unreasonable time to execute).  So one could do things like average data before it is sent, or log debugging information, for example.

 

BUT, don’t get too fascinated by the fancy tools in the toolbox.  Most of my messages contain only one piece of data.

 

— James

 

PS, if you want to explore another powerful LVOOP class that can be extended, look at “Async Action.lvclassâ€, which is intended to make it easy to make actions that can be run in parallel to any loop (with action results sent back in a message). 

 

  • Like 1

Share this post


Link to post
Share on other sites

Here is an illustration of what Thoric is discussing.  Calling code starts two actors: an “Instrument" actor that is publishing a “Readingâ€, and a “Furnace†actor that expects to receive “Furnace Temperature†messages.  If the two messages both use the same data format (a numeric in Kelvin, say) then we use the code on the left to connect the two actors directly.  We configure the messages to be relabelled with the “Translate†polymorphic VI.  Looking inside that API call we see that we are providing a “Translator†child class to actually do the relabelling.

 

However, if Instrument publishes its “Reading†as Kelvin, but the Furnace expects â€œFurnace Temperature†to be in Fahrenheit (horror!) then we can supply a custom child Translator class.  This class only needs one method, an override of “Translate.vi†(shown), which adapts the messages to the form required by Furnace.   We provide this as a second translator as shown in the code at right.

 

post-18176-0-11064000-1452255857.png

 

[Opps: just noticed I misnamed the Translator child class as degF to Kelvin when it is really Kelvin to degF]

  • Like 2

Share this post


Link to post
Share on other sites

Another question, if I may.
I have a View actor with a subpanel. It dynamically launches child actors (that I'm calling subViews) and docks them in the subpanel. Numerous subViews exist, they can be switched using controls in the View UI. View actor registers for all notifications from the subViews, as these are often requests for information, to which it can reply.

When a new subView actor is loaded and launched it needs to populate it's internal data, so the Self:Initialise case includes in the list of actions "Request Project Data", a case which calls a Notify Event labelled "Request Project Data". As the View actor is listening to all notifications, it replies with a copy of the latest Project data, which the subView actor receives and stores in it's private internal shift register.

The problem I have is that it appears the View actor is failing to register for the subView's notifier event messages in time to notice this "Request Project Data" notify event. I'm wondering if a caller's request to register for notifications is processed after the child actor has completed its Initialisation cases. If that's true then I have a race condition here and I need a way to ensure the child actor doesn't send the notification message until the caller has registered itself to its notifier event messages.

 

I suspect I'll be advised to try a different approach, such as explicitly sharing the caller's message queue so that the child actor can directly post messages to its caller. Is that better, or does it have other drawbacks?

  • Like 1

Share this post


Link to post
Share on other sites

I suspect I'll be advised to try a different approach, such as explicitly sharing the caller's message queue so that the child actor can directly post messages to its caller. Is that better, or does it have other drawbacks?

Never sharing the Caller’s Queue with a subactor is a guiding principle of how I program actors.   I was trying to get this across on your conversation on the AF group when I talked about a “Service hierarchy".  Just don’t do it!  

 

Three solutions:

 

1) An immediate solution to your problem is to use a State Notification rather than an Event Notification (call it something like “Project Data Requiredâ€).  It’s a weakness of the current framework that one cannot receive events from actor initialization, because the actor hasn’t processes any incoming messages yet, including the Registration messages.  However, this doesn’t affect State Notifications.  A quick summary of the difference between Event and State Notification:

 

Event: “Something Happenedâ€; everyone now registered for this event is notified.

 

State: “This is now the current stateâ€; everyone registered is notified, and anyone registering for this State in the future is notified of the last recorded state.

 

Basically, State Notifications keep a copy of the message, in order to inform future people registering of the current state. This avoids all race condition issues in startup, and allows one to hook up new actors to existing state without needing to have observed actors keep re-notifying their (unchanging) state.  A very valuable simplification.  But it does mean more overhead, so one wants to avoid using State Notification with large data messages.  Most of my notifications are State Notifications.

 

2) If the Caller has the “Project Data†just send it to the subUI; why does the subactor need to Request it?   Or Register the subUI to receive the Project Data from whoever has this info (published as a State Notification, or course).

 

3) Give the subUI the address of a third party (“DataSourceâ€) from which it can request the data (request directly, not by notification, synchronous or asynchronous).   Note that this address cannot be the Caller, as we are placing the subUI above DataSource in the service hierarchy (subUI is below Caller by definition).

 

— James

Share this post


Link to post
Share on other sites

Here is a slide for a talk I gave a couple of years ago that shows an actor’s relationships, as see them:

 

post-18176-0-93200900-1453063619.png

My notes from the Powerpoint slide:

 

Processes (or “Actors) respond to commands from, and make information available to, higher calling code.  But do not explicitly require information messages to go anywhere.
Calling code initiates information messages by request-reply or publish-subscribe mechanisms.
Calling code can direct information messages to third parties, or to multiple parties.

 

In this diagram, none of the three actors “know†about their calling code.  The main “Process†actor knows about the two sub-actors, but not about the code that is calling it.  One sub-actor is sending a message to the other, but neither sub-actor knows the other exists.  It is the main actor that is directing one sub-actor to message the other, which is why the diagram places a dashed line that include the sub-actor communication as being “part†of the main actor.  

 

If this seems strange, let me try and list the benefits of having an actor not know anything about or explicitly depend on it’s calling code:

 

1) Lower-level actors are simpler; one doesn’t have to understand the higher level code while debugging the lower level actors.  Often, low-level actors can run by themselves for testing, or run from a simple test harass.  

2) Lower-level actors are more cohesive, modular, and reusable, as they aren’t coupled into the higher-level design.  One can re-architect the high-level design without redoing all the sub-components.

3) Actor interactions can often be understood by only looking at one block diagram, the caller’s, as this is where all the interactions are specified.  One can know, for sure, what messages one subactor can send to who, without needing to dive into that subactor’s code, because it can only send the notifications the calling code has registered for.  This greatly aids debugging.  

4) Because sub-actors do not require anything from their caller, and the caller sets up direct communication between subactors, the calling actor is free to use synchronous, wait-for-reply messaging with the subactors. 

 

Number (4) is actually very valuable, because synchronous interaction is simpler than asynchronous, and complex initialization, shutdown, or reconfigurations of the actor system are much easier with synchronous interactions.   Having the calling actor not itself participate in the asynchronous message flow between subactors means that it is fully free to setup and shutdown that flow.

 

  • Like 1

Share this post


Link to post
Share on other sites

Here is an extension of the above to a tree of actors: Main has launched A and B, A has launched A1 and A2, and B has launched B1.  At this point, the following is true:

— Main can send commands (including registration messages to receive notifications) only to A and B.  It cannot communicate with the sub-subactors A1, A2 or B1.   Main can direct replies to commands (or notifications) from A to either itself or B, but no-one else.

— A can send commands/registrations to A1 and A2, but not to any other actor, including Main, or any subactors that may have been launched by A1 or A2.   A can direct replies or notifications from A1 to either itself or A2

— B can only send to B1, and can only direct replies/notification from B1 to itself.

— A1, A2 and B1 can’t send commands or registration messages to anyone.

Actor Tree.png

 

Note that, though we can establish direct communication between actors (across the tree) using registration messages, we can only do this between “siblings", not “cousins".  So this system is not the same as one where any actor can be connected to any other actor by something global (as in named Queues).  The actors cannot "see through" the levels: Main cannot interact directly with A1/A2/B1.  Thus there is significant “encapsulation" in this system.  “Main", as an encapsulated component, consists of the Main, A and B actors, and all there interconnecting messages.   Component “A" is A, A1 and A2 and their interconnections.

 

It would be possible for Main to pass the address of B to A in a message.   In which case, A can use B as if it were a subactor, like A1 and A2.  But this should be rarely used.   Main should never pass itself to A, or pass A and B to each other, as this makes actor subcomponents of each other, and that can lead to problems.

 

If you follow this structure, and set up notification-based direct communication between “sibling" subactors, you get the benefit that actors can act synchronously on their subactors, sending commands and waiting for reply, without the potential for deadlock or other “blocking" problems.  Synchronous is much simpler (and easier to debug) than asynchronous for doing the complex work of initialization, shutdown or other multi-step actions, so this is a major benefit.

 

— James

Edited by drjdpowell
Fix minor corruption of some characters

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×

Important Information

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