LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Manual Control Read/Write slight issue issue

Back again with another likely (hopefully) easily resolved question.

I have a set of VIs that work more or less as intended.  They use 3 global variables to read/write data to each other dynamically and read out to hardware.  I have gathered that Globals are ill-advised and not so popular here - that said, I really have not been able to figure a better way to manage dynamic, non-data flow oriented processes in the LabVIEW environment, and my program needs do seem to meet the less than common criteria for appropriate use of Globals. 

An important part of the system is the Manual Control user interface, where the user can control a number of pneumatic vacuum valves at will, overriding automatic cycle controls if they so choose.

The way I have set up this control is to have a Read case (reading to controls from global), and a Write case (writing to globals from controls), which flip flop between each other every 50 ms.  This works well enough, in the sense that it works at all with the other automatic modules and write to hardware functions.

My issue at the moment is that, probably due to the peculiarities of timing, reading, and writing, that a user clicking on the front panel valve controls can't always get the switch to "stick" on the first, second, or even third click.  Usually if you make a solid mouse press and hold it, or click it a number of times, it will switch.  This is nominally good enough (at least it's good enough for my manager, apparently).  However, I am curious if there is some sort of workaround that avoids the flip-flopping read/write case structure, or at least makes the user experience of clicking the controls more consistent/satisfying.

All of this is in LabVIEW 2012.  I've attached the relevant VI here, hopefully it's fairly easy to replicate.  Any thoughts are appreciated.

Thanks!

0 Kudos
Message 1 of 9
(3,561 Views)

Oh, boy, not only are you using Globals, you are using Local Variables within your strange "Flip-flop" routine.  Someone needs to sit down with you, get you to explain what you want to do with all of those Valve controls (are you manipulating them, is the software manipulating them, or are both of you doing it?), leaving the "how", for the moment, out of the picture.

 

Are these "Valves" truly On/Off switches?

 

Bob Schor

0 Kudos
Message 2 of 9
(3,526 Views)

Yes, that would be ideal : )  Unfortunately, despite the fact that I only picked up LabVIEW less than a year ago, I probably know as much if not more than anyone else in our labs besides one guy at the director level in a different lab - who I can't exactly harass for personal one-on-one mentoring time.  So I promise I'm doing my best to not make too many good design errors.

1) That is correct, both the user and the automatic routines need to be able to control the valves.

 

2) The valves are true On/Off switches, in that they control real solenoid valves.

0 Kudos
Message 3 of 9
(3,522 Views)

This structure already IS a giant race condition, even apart from any other part of the program that may *also* be writing to those globals.  So I'd very much disagree with your assessment that your app is one of those rare cases that needs them.  In fact, your app is an excellent illustration of their dangers.

 

To wit:  suppose the code queries the state of valve 70 as F a moment before you toggle it T on the gui.  Well, too late.  The queried F value is used to set the global variable, and your gui interaction overlaps with the 50 msec period wait.  Then the program switches over to the mode where it queries the F value from the global and sets the gui control to F.  This could keep happening any number of times.  You've put together a system with a bunch of manual controls for valves, and you can never predict whether the valves will respond to your gui intentions.

 

I don't know the nature of your fluid system, but many systems exist where such unpredictability or unreliability can be downright dangerous.  The general distaste for globals is partially fueled by the insidious side-effects of the race conditions and poorly-managed state transitions they accommodate oh-so-readily.

 

I have worked on a system with an analogous control panel gui.  You could manipulate the gui controls to cause changes in the otherwise autonomous system.  And normal autonomous system state changes would cause the gui to update and show the real system state.

   I don't recall details now, but standard globals were *not* used.  Instead there was a layer (or maybe two?) of mediation that managed conflicts between what the user requested and what the autonomous system requested or reported.  There were clear rules defined about how to handle those conflicts.  And any proposed updates from gui to system or system to gui were reconciled for consistency.

 

It was more complex to code, but resulted in a much safer and much more predictable system to operate.

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy came to an end (finally!). Unfortunately, pricing favors the captured and committed over new adopters -- so tread carefully.
Message 4 of 9
(3,516 Views)

Thanks for the feedback.

 

I certainly agree - figuring out how to resolve the race condition while retaining the functionality present is sort of my issue, I guess.

Fortunately or not in this case, needing to click on the button several times to open a valve, accidentally opening the valve for a couple seconds, or failing to open a valve for a couple seconds is not really a serious issue for the type of stable, long interval isotope chemistry being handled.  The primary purpose of the project is to allow the chemists to run the experiments overnight and over the weekend, which the previous system could not do, while retaining the manual control for daytime use that the previous system did well.  So it is not, I would say, "dangerous" in the sense that something could explode or get contaminated, but perhaps dangerous in that it is bad LabVIEW programming, sure.

Could you maybe expand on what you recall?  It is exactly how to build such a "layer of mediation" on which I am not very clear.

0 Kudos
Message 5 of 9
(3,491 Views)

Lack of physical danger from the non-deterministic behavior is definitely fortunate.

 

My memory on the mediation mechanism is pretty vague and incomplete.  The code structure for the mediation was already in place when I started working there.  I didn't develop the code, I only peeked at it a little bit.

 

As I recall, part of the mediation was done by encapsulating access to all the system variables with an action engine.  Code modules would send write *requests* to the action engine.  There was also a high-level supervisory system "heartbeat" that ran at something like 5-20 Hz.  I think it had primary control over when and whether the "requested writes" to system variables became actual committed writes.  This would happen once per heartbeat, at the very end.  Code modules would generally only read from the system variables once per heartbeat, at the very beginning.

 

The way this worked on GUI interfaces was that the gui would read in its values from the system variables at the beginning of each heartbeat, could update the gui as many times as it wanted within that heartbeat time based on whatever number crunching it did, and then put out a write request to push these values to the system variables.  The supervisory control would then convert these requests into actual writes at the end of the heartbeat.   Something like that.

 

But we were dealing with potentially explosive gas mixtures of hydrogen and oxygen where those layers of mediation were not just an academic exercise.  You aren't.  So here's a very simple first step toward getting a big improvement in behavior:  simply move the timer function to the left side of the loop and feed its tick count output to the case structure boundary.  This sets up a dataflow dependence where the wait happens first, then the gui buttons are queried and used to update globals immediately.  Then there's a wait again (while other parts of the system are able to update those globals), after which the globals are queried and used to update the gui immediately.

 

Not perfect, but it should be a significant improvement for your app, where minor imperfections are an annoyance and not a danger. 

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy came to an end (finally!). Unfortunately, pricing favors the captured and committed over new adopters -- so tread carefully.
Message 6 of 9
(3,473 Views)

Sorry for the delayed response, and thanks for your answer - I was off for a couple weeks.

Sorry if this sounds stupid, but what do you mean by wiring the timer to the case structure?  I've never tried using a timing function to drive/organize a case structure, so I am not sure how that works.  I am trying to look around online but can't seem to find examples.  Thanks for your patience!

0 Kudos
Message 7 of 9
(3,417 Views)

I have been considering switching to an event structure, but I am not sure if this will offer any improvement - would a timed structure perhaps offer any advantages?  Thanks

0 Kudos
Message 8 of 9
(3,416 Views)

All I meant about the timer was to wire its output to the case structure boundary in order to enforce execution sequencing based on dataflow.  The output value never gets used for anything, it's the mere presence of the output-to-boundary connection that causes LabVIEW to wait the full 50 msec before reading the present state of the controls or globals.  By ordering things this way, you greatly reduce the probability of getting burned by the race condition inherent to this design.  Again, this little change doesn't *eliminate* the race condition, but it drastically reduces the window of opportunity for it to cause mischief.

 

An event structure might be helpful in some ways, but wouldn't by itself eliminate the race condition.  The code in the "Write Global" case would get distributed over many distinct events for the distinct GUI controls, which could make it a little more effort to debug and maintain.

   In the long run, event structures are good things that help in many ways, but I'm not so sure it's the best "next step" for you.

 

A timed structure isn't going to do anything particularly helpful either.  

 

In the long run, because these GUI controls and global variables are both read and written, the way to get rid of the race condition is to code up some version of mediated access like I referred to earlier.  That's probably more than you're ready to tackle right now though.  So in the short run, just wire the timer output to the case structure boundary and see how much closer it gets you to predictable behavior.

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy came to an end (finally!). Unfortunately, pricing favors the captured and committed over new adopters -- so tread carefully.
0 Kudos
Message 9 of 9
(3,402 Views)