candidus Posted August 25, 2017 Report Share Posted August 25, 2017 I have to decouple the UI part of a medium size application (approx. 2000 VIs) from its core. When we created the application it was a simple state machine. Later we realized that some operations must be asynchronous so we added an event-based control loop that controls the state machine via user events. But it was still a single VI and over the years it grew bigger... Now I decided to move the state machine (approx. 70 states) into a subVI but since there never was a separation between the UI part and the non-UI part I now need a way to access the UI controls (approx. 40 controls) from the state machine. Currently I see the following options: 1) User events from/to the state machine. This seems to be a quite clean approach but means a lot of work. 2) Use something like Actor Framework. Probably the most clean approach but even more work. 3) Get/Set Control Values by Index. I prefer that over Get/Set Control Value because I often have to read/write several controls at once. I can easily encapsulate this in a nice class that maps my control names to indices. 4) Put the control refnums into a cluster and pass that to the state machine subVI. This is the approach I dislike the most. I already decided not to do it that way. Currently I like 3) best but I have mixed feelings about it: - It seems odd to use control access to separate application parts. Maybe that's just me but it feels wrong... - I have controls of which I have to access other properties than the value so I definitely need events here. - Searching here on LAVA brought nothing about Get/Set Control Values by Index which disturbs me a little bit. Do you have any any advice for me? Do I miss something important? Thanks in advance, candidus Quote Link to comment
JamesMc86 Posted August 25, 2017 Report Share Posted August 25, 2017 I like OO - so bear that in mind with my answer! Ideally you don't want your state machine to know how UI updates are done - it doesn't care, it just needs it done. So I would consider creating a UI class or multiple UI classes for the different parts of the UI with update methods which your state machine can call. Inside of their you can use any of the methods and chop and change between them. Within there my temptation would be to queue them as messages or user events to a separate UI loop but once the separation is done it is easy to try different techniques. Quote Link to comment
bbean Posted August 25, 2017 Report Share Posted August 25, 2017 Take a look at drjdpowell's Messenger library (available in VIPM). I believe the demo shows an example of how to do what you want. Its relatively easy to learn the basics, and its even more powerful under the hood. 2 Quote Link to comment
hooovahh Posted August 25, 2017 Report Share Posted August 25, 2017 The way I handle this is the UI event that is generated when the user interacts with stuff, will generate a user event for some work to be done. Then when I want that work to be done anywhere else in the application, all I need to do is generate that same user event. Now the code that my sequence calls, is the same code that my UI calls. This makes debugging sequence issues pretty easy since I can just manually perform the same steps as what the sequence would be doing, by performing the actions with the UI, which would generate the same user events as the sequence would. It is a lot of upfront work, and does feel like an unnecessary layer of abstraction, until changes in how something behaves is needed, and now I no longer need to update both the code in my UI event case, and the code in another case. Quote Link to comment
smithd Posted August 25, 2017 Report Share Posted August 25, 2017 (edited) This makes sense on the surface but I don't understand the real impetus for the change -- you kind of jumped from "its big and complex" to "here are solutions I came up with for the problem". Do you need to be able to reuse your core state machine inside of another application, or share behavior between implementations? Most of your solutions sound like they are still tightly coupled to a specific UI (eg get set control values), you're just adding an additional layer of code. More in reply to the other posts, since as I said I dont totally understand the problem you're trying to solve: the way I do most UIs is to have a single loop with the following features: Event loop async call references for any long running tasks w/dynamic event for result this is something drjd mentioned using in his messenger library as well I had made something which somewhat generalized it, which is where I had the conversation with drjd state cluster (or class)+functions which operate on it (the 'model' for lack of a better term) state cluster for any ui-specific data local, "redraw" user event which uses one or both state clusters to (in most cases) refresh the UI from scratch So my "decoupling" occurs in only one place -- my state cluster and the functions which act on it are separate from anything specific to a given user interface. Edited August 25, 2017 by smithd Quote Link to comment
drjdpowell Posted August 26, 2017 Report Share Posted August 26, 2017 On August 25, 2017 at 10:24 AM, candidus said: Currently I see the following options: 1) User events from/to the state machine. This seems to be a quite clean approach but means a lot of work. Note that there are two schools-of-thought on how to use User Events. One is to have a separate custom Event for every different message (about 40 in your case). For examples of this see the DQMH or JKI’s “State Machine Objects”. These rely on scripting tools to manage all the custom subVis that need to be generated. The other school is to use a single User Event to carry all messages, using something generic like a string “message label” and a Variant “data” payload. “Messenger Library”, if you look under the LVOOP hood, is basically this. At its simplest, you can do this later method with only a single “Send Message” subVI (with “label” and “data” inputs) that you place everywhere you were previously setting a control. So this is not a lot of work, or at least, not more than any other possible method. 1 Quote Link to comment
hooovahh Posted August 28, 2017 Report Share Posted August 28, 2017 I prefer having N user events, one for each message I want, and one for each reply (if applicable). If the data types are all the same (ie. Variant) you can put these user events into an array, and register for all of them at once. You will have one event case handling all of them, but the point is that you can have a publisher/subscriber model where modules (actors) can subscribe to N of them at once. Quote Link to comment
shoneill Posted August 29, 2017 Report Share Posted August 29, 2017 +1 for multiple User Events (why do away with the strict typing of LV) Quote Link to comment
candidus Posted August 29, 2017 Author Report Share Posted August 29, 2017 There is a reason to refactor: I have to add more features and I don't want my toplelvel VI to become any bigger - currently it has a huge block diagram... On 26.8.2017 at 6:21 PM, drjdpowell said: The other school is to use a single User Event to carry all messages, using something generic like a string “message label” and a Variant “data” payload. So this is not a lot of work, or at least, not more than any other possible method. That's it! I began to experiment with an event containing a variant-based lookup table, then I looked at Messenger Library and saw that all I need is already there. Thanks! Quote Link to comment
crossrulz Posted August 29, 2017 Report Share Posted August 29, 2017 On 8/26/2017 at 0:21 PM, drjdpowell said: Note that there are two schools-of-thought on how to use User Events. I tend to bound back and forth depending on the application. At some point, it just becomes way too many User Events to try to keep track of when you use individual events. I think I only had 1 application where this became an issue. But on the other hand, it is quite nice to only register for the events/messages that that specific process cares about instead of having to process all messages. Quote Link to comment
hooovahh Posted August 29, 2017 Report Share Posted August 29, 2017 1 hour ago, shoneill said: +1 for multiple User Events (why do away with the strict typing of LV) While I do multiple user events, the payload is all Variant, which is again how I can get away with building them into an array and registering for them all at once, or subscribing and unsubscriping to events at runtime.. The scripting code generates the VIs that flatten to, and unflatten from, the type def so everything is still strictly typed, but there is a common payload for all events. If there were N events, all with their own registered case in an event structure I couldn't do this as easily. But I admit other parts of the application would be cleaner. Quote Link to comment
shoneill Posted August 29, 2017 Report Share Posted August 29, 2017 23 minutes ago, hooovahh said: While I do multiple user events, the payload is all Variant, which is again how I can get away with building them into an array and registering for them all at once, or subscribing and unsubscriping to events at runtime.. The scripting code generates the VIs that flatten to, and unflatten from, the type def so everything is still strictly typed, but there is a common payload for all events. If there were N events, all with their own registered case in an event structure I couldn't do this as easily. But I admit other parts of the application would be cleaner. While I see the pacticality of this, it makes me feel all icky doing it....... Quote Link to comment
Popular Post Neil Pate Posted August 29, 2017 Popular Post Report Share Posted August 29, 2017 I usually have one user event per "actor" which is used to broadcast when its state changes. Recently I have been playing around with not using the event data terminal and instead having a global data repository implemented using a DRV to a variant (with the data stored as named attributes), the actors user event data is a timestamp that I set when I generate the event. If I had a cent for every time I have created and deleted the register event terminal which never propagates properly... 3 Quote Link to comment
Antoine Chalons Posted August 30, 2017 Report Share Posted August 30, 2017 23 hours ago, Neil Pate said: If I had a cent for every time I have created and deleted the register event terminal which never propagates properly... same here Quote Link to comment
smithd Posted August 30, 2017 Report Share Posted August 30, 2017 On 8/29/2017 at 4:46 AM, shoneill said: +1 for multiple User Events (why do away with the strict typing of LV) I used to be in favor of this, but I've found that user events are too buggy to use effectively in this manner without being sad. Quote Link to comment
shoneill Posted August 31, 2017 Report Share Posted August 31, 2017 On 29.8.2017 at 4:04 PM, Neil Pate said: I usually have one user event per "actor" which is used to broadcast when its state changes. Recently I have been playing around with not using the event data terminal and instead having a global data repository implemented using a DRV to a variant (with the data stored as named attributes), the actors user event data is a timestamp that I set when I generate the event. If I had a cent for every time I have created and deleted the register event terminal which never propagates properly... Yes, the really crappy propagation of registration refnums is perhaps the major obstacle to the 1:1 command:event organisation. That and the Event Structure mixing up the order of events whenever I add another event to the API...... 1 Quote Link to comment
drjdpowell Posted September 4, 2017 Report Share Posted September 4, 2017 Although strict-typing is a valid reason to argue for separate User Events for each “message”, the freedom to Register selectively for messages is something that can be achieved in other ways. The “ObserverRegister” in “Messenger Library” allows selective registration. Also the flip side of the advantages of strict-typing are the advantages of being able to write generic code. 1 Quote Link to comment
GregFreeman Posted September 26, 2017 Report Share Posted September 26, 2017 While we're on the user event bug train, I have seen this bug also manifest itself in the form of user events where I start getting bizarre coercion dots in my event cases and types not updating/propagating until I do a save all. It has resulted in "phantom time" being added to execution of subVIs where I benchmark around a VI that takes say, 400 ms to execute, but if I benchmark around all the code inside the VI it takes 50ms. I lose 350 ms in the ether. 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.