Jump to content

Handling Shared State: Best Practises?


AlexA

Recommended Posts

Hi guys,

 

I have a system which decouples UI and Process code in seperate VIs which communicate via queues. Some sort of MVC if that helps (I don't think in those terms). My questions are:

 

When both UI and process code need to be aware of some state, e.g. "Move Slowly" option is flagged by the user. Either the process requests that state before obeying a command to move, or that state is copied into the process whenever the user modifies the flag. Which is better and why?

 

Second, within the UI, I handle control changes with an event structure. This event structure then propagates the change to an internal control loop which handles performing calculations or alternatively updates graphs with data which it receives from the process. When I change the state of a control, I can either copy its state into a variable in the control loop, or I can maintain references to the controls and read it from the control loop as necessary. I have been doing the former, but I would like to do the latter as it would mean having one less copy of the data. Are there any gotchas I should be aware of?

Finally, when a control only has a single purpose, i.e. its state will not be read in more than one control loop action, is it best practise to read the control directly in the control loop, rather than seperate it into the event structure and either use a reference to read it, or copy its state.

 

Thanks in advance,

Alex

Link to comment

When both UI and process code need to be aware of some state, e.g. "Move Slowly" option is flagged by the user. Either the process requests that state before obeying a command to move, or that state is copied into the process whenever the user modifies the flag. Which is better and why?

 

Whenever possible, I forward changes as they happen, to any state machines that need to know that data. Whenever I've gone down the "request data and wait for reply" path, it's led to more complex messaging. I could definitely be overlooking an elegant implementation of that, but "forward changes on the fly" has worked well for me, and I don't (yet) have a compelling reason to change it.

 

 

When I change the state of a control, I can either copy its state into a variable in the control loop, or I can maintain references to the controls and read it from the control loop as necessary. I have been doing the former, but I would like to do the latter as it would mean having one less copy of the data. Are there any gotchas I should be aware of?

 

I also did the former for a long time but in some more recent applications, have switched to the latter. There's increased polling time for using property nodes, but none of my UIs have needed to be "ultra-responsive," so that hasn't been a problem for me.

 

 

Finally, when a control only has a single purpose, i.e. its state will not be read in more than one control loop action, is it best practise to read the control directly in the control loop, rather than seperate it into the event structure and either use a reference to read it, or copy its state.

 

My instinct tends to be to leave things open-ended and flexible.

 

There have been a few times where I've been 100% certain that I wouldn't need to do X or Y with a UI, only to find out months or years later that the end-user wants not only X and Y, but also some Z I'd never considered.

 

So if I have some control I think will only be used in one action, I'd probably still treat it like other controls that are used in multiple places. It's minimal extra work and makes it easier to use later if I end up needing it in other spots.

 

Just my two cents, I barely know what I'm doing.

Edited by Mike Le
Link to comment

 

 

When both UI and process code need to be aware of some state, e.g. "Move Slowly" option is flagged by the user. Either the process requests that state before obeying a command to move, or that state is copied into the process whenever the user modifies the flag. Which is better and why?

 

I have struggled with this as i get more and more into labview.  In general i force my process to handle its own state and GUI's to handle their own state and ignore messages based on their current state. In other words, the "Move Slowly" message could be sent at anytime to the process, but since the process is in charge of its own state, it can always choose to ignore it.   I usually push process states changes back to GUI's using the GUI's own message queue.  So in general i don't use the Request Response structure so much as the always send  and let the recipient of the message do with it will with the message based on its state. 

 

The Request Response option gives you a very loosely coupled system, but i'm with Mike that in most cases i have not found this method absolutely necessary.  In general, i usually keep a copy of the process state in the GUI but i haven't found many situations in my current work where my process needs to know my gui state.  In general i think of my GUI only as a way to send message to my process.  I usually keep copies of any information the process would need to function in the process so that in essence the GUI could change but the process wouldn't need to.  My GUI's are tightly coupled to my process but my process is not tightly coupled to the GUI.  

 

Others with more experience may have better suggestions.  

 

 

 

When I change the state of a control, I can either copy its state into a variable in the control loop, or I can maintain references to the controls and read it from the control loop as necessary. I have been doing the former, but I would like to do the latter as it would mean having one less copy of the data. Are there any gotchas I should be aware of?

 

 

 

I don't know if you use CRIOs, NI embedded options, or even other remote systems, but one argument for sticking with your current method is that you are truly decoupled in that situation and it makes it easier to move your code to other platforms if necessary.  That way you are only passing messages between loops/processes and not references which can't cross network boundaries and things like that.  Just a thought, it may not apply to your situation.  

Link to comment

Hi guys,

 

Thanks for the opinions.

 

@odoylerules The control references are not dynamically instantiated, or passed outside the GUI. It's just a way for the GUI messaging loop to manipulate control state if the process code updates it. One example is if a relative-position transducer signal is lost and then re-established, the apparent setpoint will change in the process (it automatically sets it to be the new position value, whatever it is), I propagate this change back to the UI so the user is aware what the setpoint now is.

Link to comment

Some sort of MVC if that helps (I don't think in those terms). 

 

Start thinking in those terms.   The Model IS the state.  The Model never requests anything from the UI (View Controller).  Nor is state “copied†into it.  The Controller commands the Model and the Model updates the View with a copy of any state info the View requests.  

  • Like 1
Link to comment

I have a system which decouples UI and Process code in seperate VIs which communicate via queues. Some sort of MVC if that helps (I don't think in those terms). My questions are:

 

When both UI and process code need to be aware of some state, e.g. "Move Slowly" option is flagged by the user. Either the process requests that state before obeying a command to move, or that state is copied into the process whenever the user modifies the flag. Which is better and why?

My $0.02... The Process' job is to run the process as fast as possible. As such, I see the UI requesting a change in the Process (for which the Process has the option of saying yes, no or go to hell). The UI needs the Process for data, but the Process should not need the UI to run (I can think of exceptions to this).

Link to comment

Start thinking in those terms.   The Model IS the state.  The Model never requests anything from the UI (View Controller).  Nor is state “copied†into it.  The Controller commands the Model and the Model updates the View with a copy of any state info the View requests.  

 

This. For sure.

 

I’ve been falling back to the MV/MVC architecture quite a bit recently. It. Just. Works. Some applications don’t even need a controller, hence the MV only option. I try to use either form when I can.
 
Key point is the model doesn’t know anything about the views. It’s just an interface to which the views can get data. Used properly, none of the views need to know about other views, as far as they’re concerned it’s just itself and the model.
 
This is how I like to see it happen: the model is some singleton resource, typically a data value reference. I try to keep the model passive. No actors or loops. If your model needs to do something in a loop, you’re already thinking about views that attach to the model. The model is the data interface and nothing more.
 
The model will expose one or more subjects to which any number of views can subscribe. When a subject is changed the subscribers to that subject are notified of the change with some contextual information.
 
For example, my model may have an ActiveItem property which can be altered. Some of my views care what this ActiveItem is so they subscribe as an observer to the property. When the ActiveItem is changed, they will be notified about it and be told directly via the notification what the new ActiveItem is. For some views this is all the information they need. Others may use the contextual data from the notification to further interrogate the model. It’s a nice mix of broadcasting small copies of contextual data by value, while large state information remains singular behind a shared resource. Each view only subscribes to what it needs and only copies what it needs to act.
 
When I first started doing this I used to hard-code the notification mechanism, but found myself often creating brainless loops where the sole responsibility was to go translate one transport mechanism to another (convert a queue to a user event, for example). When going about this, do yourself a favor and abstract the subject/observer interface. The subject shouldn't care if the transport mechanism is a notifier, queue, user event, or some derived construct. Make the subject take an abstract class, and have the observer decide how it would best like to receive that notification. Is my observer an Actor? Fine, it will supply a concrete observer class that packages the notification into a message and shoots it off. Maybe my observer is a primitive UI loop? Fine, it will supply an observer that packages the notification into a user event. Maybe my observer is a remote object so we use a class which pushes notification over TCP/IP. You can change all this without any modification to the model or other views which already use the model.
  • Like 1
Link to comment

Thanks for the opinions on MVC guys. To be honest, it's a little esoteric to me right now. Especially the concept that the model is just the data. I don't know how to fit that into my understanding where some process interacting with hardware is generating data. What part of that construct is the model?

 

I'm thinking that what I think of as the process' state data is the model? (The data required to control the hardware, parameters etc.) The process is a "controller" and my decoupled UI which talks to the process via queues is the view?

Link to comment

To be honest, it's a little esoteric to me right now

The basics isn’t complicated.  I don’t even have a formal MVC architecture like mje and couldn’t tell you exactly what’s the “model†in my projects.  The central idea is that state is held (“ownedâ€) by the component furthest down towards the actual thing that the state is about (data, as mje talked about, or hardware, as is more likely in my case).  This component is the Model.  UI commands flow down to this Model.  UI “views†(the information shown to the User) flow up from the Model.   There is a clear C —> M —> V linear chain of action.

 

The simplest MVC in LabVIEW is to call a write method on an object, setting it with a Front Panel control, then calling the read method periodically and writing to a local variable of the same control.  For example, if set “Output Voltage†to 6V and the limit is 5V, then my control will say 5V, the actual output value, rather than 6.  If an automatic safety feature kicks in a limits voltage to 1V, then my control will show 1V.  My FP control, acting as a “Viewâ€, will always reflect the true state of the system, while still serving its dual role as a “Controller†element.  

When I first started doing this I used to hard-code the notification mechanism, but found myself often creating brainless loops where the sole responsibility was to go translate one transport mechanism to another (convert a queue to a user event, for example). When going about this, do yourself a favor and abstract the subject/observer interface. The subject shouldn't care if the transport mechanism is a notifier, queue, user event, or some derived construct. Make the subject take an abstract class, and have the observer decide how it would best like to receive that notification. Is my observer an Actor? Fine, it will supply a concrete observer class that packages the notification into a message and shoots it off. Maybe my observer is a primitive UI loop? Fine, it will supply an observer that packages the notification into a user event. Maybe my observer is a remote object so we use a class which pushes notification over TCP/IP. You can change all this without any modification to the model or other views which already use the model.

Note: I also highly recommend abstracting away the transport method from the sender of information via a “callbackâ€-like feature.  That’s a central feature of the framework I use.  

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.