Jump to content

I feel so dirty


Cat

Recommended Posts

I've got my current piece of code pretty much done. The users love it, since I've taken what used to be a manual, 6 hour long test, filled with all sorts of room to make mistakes in data taking, and turned it into a 22 minute completely automated, push a button, walk away, come back and see your neatly tabulated results sort of test.

Then they say, "We'd like to be able to abort the test in the middle of running it."

Hmm. I've got your typical state machine built. TypeDef Cluster of all the needed variables going into and out of each of the 10 or so states. It's all very neat and tidy. Now I've got to put a reference to the abort button in the cluster, unbundle it, get a property node for it, read it, feed it into a case statement for each of the states and their various subvis. This adds all sorts of bundles and big references and wires cluttering up the screen. Things are no longer neat and tidy. Some of my BDs that were just under one screen are now bigger than one screen. Eek!

So I was a bad girl. I used a global variable. I still have case statements, but there's no unbundle wire, and no unbundle box, and no big value property node. Just one little tiny global called "stop!".

Please don't hate me!

Cat (too upset to count)

Link to comment

What we do to handle this is use a traditional producer/consumer model and allow the producer (event structure) to catch the abort request. Our state machine is a queued state machine so when we catch the abort we can inject an abort state into the running state machine and exit gracefully. This works very nicely and we don't need to use any global variables or references to controls in the state machine. In addition, we don't have to check the abort state after/before every state change.

Link to comment

Cat,

No one deprecates LV2 (or intelligent) globals. A zillion years ago, I came up with my Running Global <http://www.sullutions.com/LabVIEW/GUIasSubVI%20Folder/Running.zip>, mainly for stopping parallel loops. Lately (since LV7.0), I've used it with event structures. The Value Change event of the abort button "stopping" this VI and a sprinkling of read-only copies (or even stoppable ones where the code detects that the test should be aborted) at suitable places throughout the code would be an easy retrofit to any code and may assuage your feelings.

On the other hand, one little global does not merit sack cloth and ashes, even during Lent.

Link to comment

As Paul Mark said, using something like a queued state machine, but since you don't have that, I think you chose the correct solution based on your description. I'm one of those who feel that a global with a single writer is often perfectly legitimate.

One point, though, is to consider whether you want a global. Currently, you have a single writer. In the future, you may have more (e.g. maybe you have an E-stop button). One simple way of working around this is creating a simple functional global with two inputs - Stop and Reset (both default to F) and one output (Stop?). When stop is T, you raise the stop flag. On first call or if Reset is T, you reset the stop flag. You can then stop the code from several locations.

Edit - I started posting before seeing the other replies.

Link to comment

QUOTE (Mark Yedinak @ Mar 18 2009, 01:24 PM)

... when we catch the abort we can inject an abort state into the running state machine and exit gracefully...

Mainly for lurkers,

The abort, being an abort, is likely queued "at the opposite end" so it takes precedence over regularly scheduled tasks. The simple insertion responds neatly between the regular cases of the queued state machine. Anywhere that has access to the queue can Preview Queue Element to truncate its own lengthly operations. If there are prolonged periods within subVIs that don't already need the queue, the LV2 global is probably a better solution.

Link to comment

QUOTE (SULLutions @ Mar 18 2009, 01:44 PM)

Mainly for lurkers,

The abort, being an abort, is likely queued "at the opposite end" so it takes precedence over regularly scheduled tasks. The simple insertion responds neatly between the regular cases of the queued state machine. Anywhere that has access to the queue can Preview Queue Element to truncate its own lengthly operations. If there are prolonged periods within subVIs that don't already need the queue, the LV2 global is probably a better solution.

What I have generally done is to insert the abort command at the front of the stack. It will be invoked as the very next state regardless of where the state machine is. Granted, it does require that a state complete its processing and it doesn't result in a state exiting after partial execution. The processing of the abort state will run through any clean-up states that are required. You are correct though that this does not reach down into subVIs. In our applications we have several regular objects that we use and for the ones that do have lengthy subVIs (such as communications with long timeouts) we provide an abort method on the class to handle this type of action. We when process the abort event in addition to queuing the abort state when invoke teh abort methods on the appropriate classes. This really is a combination of both approaches.

Link to comment

Cat,

I'm with SULLutions when it comes to the LV2 global variable. His VI is too old for me to read (in 5.1, I am now down to only 8.6), but I made a VI with similar functionality. I saved it in LV8.0, so hopefully everybody can read it.

Download File:post-11268-1237404447.vi

But still, don't feel bad for using a global variable. They are there for a reason, and I think this was one of them.

SULLutions,

Can you repost a newer version of your abort vi? I'd just like to see how you handled it.

Link to comment

I also use a similar stop functional global concept to trap aborts from parallel processes. Mine also has a reset input so you can clear the abort after you've acted upon it.

I'd like to address the use of cases to "case-out" code after an abort. Depending on the granularity of your state machine, you may be able to get away with handling the abort up front and avoiding the need to place the abort checking too deeply in your code. here's how I handle it using the JKI state machine.

post-2-1237413680.png?width=400

I try to avoid putting looping code inside subVIs where I have to later abort them. Instead I use the natural looping of the state machine to repeat test tasks.

Link to comment

Another solution would be having the UI be VI A and the state machine be VI B and instead of calling VI B as a subVI, call it using a VI Reference and the Run method, then when user hits the STOP button, you call the Abort method of the VI reference. Your app as a whole keeps running, but that state machine stops.

Is that more or less dirty? Can it be made acceptable somehow?

Link to comment

QUOTE (Aristos Queue @ Mar 18 2009, 05:07 PM)

Another solution would be having the UI be VI A and the state machine be VI B and instead of calling VI B as a subVI, call it using a VI Reference and the Run method, then when user hits the STOP button, you call the Abort method of the VI reference. Your app as a whole keeps running, but that state machine stops.

Is that more or less dirty? Can it be made acceptable somehow?

This results in a rather harsh stop of the state machine. This method doesn't allow you to clean-up gracefully. I prefer to be able to process the abort rather than have the rug pulled out from under me.

Link to comment

QUOTE (Aristos Queue @ Mar 18 2009, 03:07 PM)

Another solution would be having the UI be VI A and the state machine be VI B and instead of calling VI B as a subVI, call it using a VI Reference and the Run method, then when user hits the STOP button, you call the Abort method of the VI reference. Your app as a whole keeps running, but that state machine stops.

Is that more or less dirty? Can it be made acceptable somehow?

Aborting camera applications on RT causes the IMAQdx driver to be left hanging and the camera cannot be accessed until the system is rebooted, so I would say this is a bad.

N.

Link to comment
QUOTE (Aristos Queue @ Mar 18 2009, 03:07 PM)
Is that more or less dirty? Can it be made acceptable somehow?
It depends. Can the VI that is being aborted tell if it is? If so then it can exit gracefully. Otherwise, it's a no-no in my books. I would only abort a VI if it's a runaway process, a very very simple VI or I've exhausted all other forms of communication. Like a divorce is to marriage.

Link to comment

QUOTE (Cat @ Mar 18 2009, 06:59 PM)

Then they say, "We'd like to be able to abort the test in the middle of running it."

I have no idea who you are working for, or what tests are being done. But since this has not popped up before the application was finished, you should be 100% certain of what they actually mean. To "stop" the tests is usually not the same as "aborting" the tests. If it involves powerful/dangerous/(costly and easely breakable) equipment there could be a world of difference between those two concepts.

Link to comment

QUOTE (crossrulz @ Mar 18 2009, 03:31 PM)

Sorry, I thought I had posted an update long ago. Guess I should check my site more often. Here is a LV7 version that is a little better than the original LV5 and this is what it looks like:

post-1180-1237466783.png?width=400

And I learned the right way to post a BB link today. :)

Link to comment

QUOTE (neBulus @ Mar 18 2009, 02:35 PM)

I have been global free for almost two years at this point.

I just had an image of an AA meeting pop into my head...

"Hello, my name is Tim and I was global free for years and then I worked with some legacy code last Tuesday..." :)

Tim

Link to comment

QUOTE (bsvingen @ Mar 18 2009, 05:25 PM)

I have no idea who you are working for, or what tests are being done. But since this has not popped up before the application was finished, you should be 100% certain of what they actually mean. To "stop" the tests is usually not the same as "aborting" the tests. If it involves powerful/dangerous/(costly and easely breakable) equipment there could be a world of difference between those two concepts.

I agree here - if they want to shut down the test is a more or less orderly matter, that's one thing, but a true abort suggests "things have gone to hell and we need to stop this thing ASAP" - like the abort button when the Enterprise has started its self-destruct sequence. They should be handled quite differently. In most of the systems I've worked on, we never included a software or computer controlled "abort" although we did include stop and interrupt functions/buttons. If the system needed a real abort, that's what the big red mushroom switch that disconnected the mains or whatever was for.

Mark

Link to comment

QUOTE (Mark Yedinak @ Mar 18 2009, 02:24 PM)

I was just thinking about queued state machines this morning. I'm expecting the next request to be a "suspend" button... I was having a hard time wrapping my brain around a good way to do that with my current design. But a queued state machine (that keeps track of where it needs to go back to after the "suspend") seems to make more sense. Thanks for the suggestion.

QUOTE (SULLutions @ Mar 18 2009, 02:31 PM)

On the other hand, one little global does not merit sack cloth and ashes, even during Lent.

I appreciate the dispensation. :(

Also, thanks for the code sample. You're right, if I use a LV2 global, I'll probably feel a lot better. :)

QUOTE (neBulus @ Mar 18 2009, 02:35 PM)

.

I have been global free for almost two years at this point.

Wow! After your experience, I can hardly blame you.

QUOTE (Yair @ Mar 18 2009, 02:38 PM)

One point, though, is to consider whether you want a global. Currently, you have a single writer. In the future, you may have more (e.g. maybe you have an E-stop button). One simple way of working around this is creating a simple functional global with two inputs - Stop and Reset (both default to F) and one output (Stop?). When stop is T, you raise the stop flag. On first call or if Reset is T, you reset the stop flag. You can then stop the code from several locations.

Yes, as I think I've mentioned, I'm already anticipating a "suspend". And then there's going to be the inevitable "exit". The FGV is probably a good way to go. Thanks!

Link to comment

QUOTE (Tim_S @ Mar 19 2009, 08:56 AM)

I just had an image of an AA meeting pop into my head...

"Hello, my name is Tim and I was global free for years and then I worked with some legacy code last Tuesday..." :)

Tim

GVA - Global Variable Anonymous. I like it. I have somehow been global free for 3 years. But are functional globals just a crutch or are they actually better to use? Hmm...

Link to comment

QUOTE (Michael Aivaliotis @ Mar 18 2009, 06:04 PM)

Due to code reuse, the code's not as granular in a couple places as I'd like it to be. And I agree completely about the loops, but there was one case where it just had to happen. So I have to tie the abort to that condition.

Thanks for your code example!

QUOTE (Aristos Queue @ Mar 18 2009, 06:07 PM)

Another solution would be having the UI be VI A and the state machine be VI B and instead of calling VI B as a subVI, call it using a VI Reference and the Run method, then when user hits the STOP button, you call the Abort method of the VI reference. Your app as a whole keeps running, but that state machine stops.

Is that more or less dirty? Can it be made acceptable somehow?

I have a clean-up state that it really needs to go thru before exiting the state machine. Otherwise that would probably be okay since there's not much else going on in the program.

Link to comment

QUOTE (crossrulz @ Mar 19 2009, 09:56 AM)

But are functional globals just a crutch or are they actually better to use? Hmm...

I was told (and maybe misinformed) that the way Global Variables were first implemented were very bad on resources, on top of the usual problems associated with using Globals (hard to debug problems and such) But I heard that the way globals are made natively today is much better than before.

As for functional global, I'd say it can be considered a crutch, but functional globals can do so much more than the built in global variables, that I think it's ok.

BTW been global variable free for 4 years now...well I haven't created any in 4 years, some NI examples use them, and I would use the examples in a prototype situation. Does that count?

Link to comment

QUOTE (bsvingen @ Mar 18 2009, 08:25 PM)

I have no idea who you are working for, or what tests are being done. But since this has not popped up before the application was finished, you should be 100% certain of what they actually mean. To "stop" the tests is usually not the same as "aborting" the tests. If it involves powerful/dangerous/(costly and easely breakable) equipment there could be a world of difference between those two concepts.

Yes, I'm looking ahead to the difference between "abort" and "exit". Thanks for the warning!

Link to comment

QUOTE (crossrulz @ Mar 19 2009, 08:56 AM)

But are functional globals just a crutch or are they actually better to use? Hmm...

Functional globals are different and in a way can be thought of as very simple objects in an OOP environment. Functional globals minimize race conditions since they only allow one access to the variable at a time. They do not eliminate race conditions but they do guarantee only only access will occur at a time. In addition, they allow you to add additional functionality that are not there for a native global. And from what I have heard they are better with respect to resource usage than the native global variables. It is also easier to provide sequence control via data flow since you can include an error in and error out allowing you to impose data flow using error clusters as opposed to the data value itself.

Link to comment

QUOTE (crossrulz @ Mar 19 2009, 03:56 PM)

But are functional globals just a crutch or are they actually better to use?

They are better to use, but only if they are functional. If they just have read/write options (or get/set or whatever) for a single value, then they're no better than globals and in many ways they're worse. The power of functional globals comes when you place actual code inside them. If you're not doing that (and even if you are), you might wish to consider other alternatives like GOOP or LVOOP (probably using single element queues to transport the object if you want to have globally available data).

Link to comment

QUOTE (Yair @ Mar 19 2009, 01:44 PM)

They are better to use, but only if they are functional. If they just have read/write options (or get/set or whatever) for a single value, then they're no better than globals and in many ways they're worse. The power of functional globals comes when you place actual code inside them. If you're not doing that (and even if you are), you might wish to consider other alternatives like GOOP or LVOOP (probably using single element queues to transport the object if you want to have globally available data).

Yair,

I have a project that still bears the scars of the Global on its bottom line (see the thread I linked about Globals causing Timed Loops to Finish Late).

When using globals to flag a stop condition, the Timed Loops would occationally finish late. That did not happen with LV2s and I never got a good explanation as to why.

So in that special case of using globals to stop a timed loop they performed badly.

Ben

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.