10-09-2009 02:57 PM
Siniz wrote:
I have one question though about the zip file I posted: Why does the event case not register when the boolean for the error? front panel object changes? I do have it as a case in the event structure.
You don't have Value Signaling on the property node. The only way the event structure will trigger for the Error? control would be if you actually presssed the button on the front panel. Look at the property node and change it to use property "Value (Signalling)". However it is a good idea to avoid using property nodes to set the value for controls and indicators. One exception though is for initialization code.
10-09-2009 03:04 PM - edited 10-09-2009 03:05 PM
Understood. Many thanks 🙂
I definitely have something solid to build on and I have many ideas. Just need to get down to work and actually implement them.
10-09-2009 03:06 PM
10-12-2009 10:15 AM
Mark, I am having loads of fun and I used your code as a template to build my own code. I figured I would learn a lot more that way than to just copy/paste your code and then make changes. And it's going good I might add!
I am going to be so bold as to ask you again for some guidance. Again, it is a question on what design approach to take.
If we start where you last left of you had the state machine continously add the Read Force Value state. Now I want to deal with this in another manner:
I would like the state machine to have states that are for example called: Start Read Force Value and Stop Read Force Value. I would then have a separate while loop outside of the state machine that would basically just start/stop looping when I call the respective states. The reason for this is that I have more peripherials than just the force gage, and I want each and everyone to have their own while loops so they become truely parallell. I also want to be able to start/stop all of these loops individually. (i.e. by calling different states).
How would this best be handled?
10-12-2009 10:42 AM
Siniz,
I would create a separate state machine for each process/task. I would create a subVI for each task that you want. This will work provided you aren't trying to access the same hardware from different tasks. The UI task (the event structure) can post the appropriate start/stop messages to all of the tasks. Each task would have its own queue. If you needed the ability for an individual task to start or stop an action in another task simply pass the reference to the task queue to the other tasks. You can use named queues so that you don't have to routes the queue wires all over the diagram.If you use named queues the subVI would simplyopen a reference to the queue using the name. Naturally if you hardcode the names it is not as flexible as wiring in the name or the actual queue reference. One alternative is to allow the subVI to take in a passed in queue name but use a default name if not wired. I would avoid making a single state which iterates over the queue. This makes it harder to inject the stop state. The way I have it coded it is easy to inject the stop condition without requiring special processing while looping over the data collection. Your concept of having a separate Start and Stop Read is fairly typical. You can actually generate the Start and Stop conditions from anywhere in the code provided the reference to the queue is available to that section of your application. Named queues can make things a little easier.
Your top level diagram would contain the loop with the event structure and then have the calls to the various subVIs containing your tasks in parallel to the event loop. Naturally the subVI would conatin a loop to allow it to continually process. If you were to create a subVI for the state machine loop in my example this would illustrate what I am talking about.
10-12-2009 11:51 AM
Thanks, I understand what you are saying. I will just copy/paste the current state machine, but use different queues. I was about to say 'this will become spaghetti heaven', but you suggested named queues which takes care of that. Cool 🙂
I have one hypothetical dilemma that can occur:
As you have the state machine now, it continously adds the Read Force Value state in the very same state. When I press the stop button, you place the Stop state at the beginning of the queue. Now in the Stop state you have it to add the Exit state, which I don't want, but this is simply solved by just removing that part of the code (I just want it to temporarily stop, not exit the state machine.) The problem I see with this is that it is possible that the Read Force Value state first adds its own state again. I then manage to press the stop button and the Stop state gets added to the beginning of the queue. The problem is however that the Read Force Value state is still in the queue, so it will process that directly after the Stop state, thus starting the Read Force Value state again. I can maybe see how this can be solved by flushing the queue in the Stop state, but I am afraid that I might maybe flush other states I added to the queue. What would the best solution be to this?
Also, to pass values (e.g. read force value) between the state machines, would you use queues for that as well? I'm tempted to use globals for the sole reason that the values I need are only written to in one place (so no race conditions can occur), they are written to fairly slowly and I am only interested in the last value when I read it (also read fairly slowly), I do not care about the intermediate values.
10-12-2009 12:20 PM
My original example implemented your requirements so that was why I had the exit from the state machine. But as you stated normally you only want to stop the process, not actually exit everything. In this case you would not queue the exit condition. When you simply want to stop the state machine you would flush the queue to remove any actions that were already queued up. You do need to be careful though because when you really want to exit the application you would need to queue both the stop and the exit states. When you flush the queue you need to check if an exit state is queued and requeue that state to handle your actual exit condition. Alternatively you could have a separate states for Stop, Process Exit Request, Teardown and an actual Exit. The Stop and Process Exit Requests would both call the Teardown state to clean stuff up such as closing connections or whatever other actions you need to take to stop your processing. The Teardown state would not queue any next state. It is the caller's responsibility to indicate what state would come next after the teardown code is executed. The Stop case would flush the queue and then queue only the Teardown state. Since no other states will be queued your state machine will be in the idle state. For the Process Exit state you would again flush the queue but then you would queue both the Teardown and Exit states. This would allow your state machine to actually exit.
I generally avoid using globals and therefore wouldn't recommend using them. I prefer to either use an action engine (functional global) or a queue/notifier for the data. If you use a notifier only the most recent copy of the data will be in the notification. In addition, multiple listeners could access the notification. I avoid globals because experience has shown that applications grow in scope and functionality and eventually you tend to run into issues with using globals. Better to plan for the future and give yourself the flexibility and benefits the other architectures provide.
10-12-2009 12:49 PM
10-14-2009 01:34 PM - edited 10-14-2009 01:35 PM
I have a button on my main UI panel which I only want to enable when all QSMs (4 or more) have managed to complete their respective Open Connections state without errors. How would this best be handled?
It feels like every little addition is getting more and more complex to add, considering I have multiple state machine heh.
10-14-2009 02:31 PM - edited 10-14-2009 02:36 PM
You could create a user event that eacho of your state machines would fire once they have completed their initialization succesfully. Your event structure loop could have a shift register which counts the number of "Initialization Complete" events you receive and once the threshold is reached enable your control. If you need to restart your application over simply reset the counter to zero and start the process over agin.
An alternative would be to use an action engine (functional global) that would effectively keep track of how many initialization events occurred. As part of the initialization action of the action engine you would pass in the reference to the control you want to enable. Again, once all of your tasks have initialized your could enable the control.