02-27-2009 03:00 PM
We start coding this week with the top–level architecture — an event driven, queue based, synchronous task handler. Open the attached project and open the top–level VI, XylophoneTuner.vi. This was created from the UIConcept_I.vi with an updated in–tune boolean. The other concept UIs have been updated, as well, but we will not be touching them today.
Starting from left to right, the first thing done is creation of a user event. Since the default state of the task handler is waiting on an event, an event must be used to externally trigger it. The event needs to be registered before it can be used. The event data type is set to the command data type so one user event can be used to send any command.
The task handler queue is then initialized with the same data type. This is a strict typedef enum and a variant contained in a strict typedef cluster. Using strict typedefs allows new commands to be easily added in the future. The queue is loaded with an Initialize command. The queue reference is typecast to the strict typedef of the queue reference to avoid later type coercions.
Note the cluster of two booleans at the top left of the main WHILE loop. This is local state information. If needed, this can be moved inside an object and/or single–element queue later in development. However, this is the most high performance way to store state information. The two booleans correspond to the Start/Stop and Show/Hide Raw Data booleans on the front panel and are used to dispatch the proper command when one of these booleans is pressed.
There are several unused indicators on the bottom left of the block diagram. These correspond to functionality which is not yet implemented.
Moving into the main loop, the first item is a dequeue of the command queue. Note the zero timeout. If there is no command in the queue, the WaitForEvent command is selected and the loop waits in the event structure. This ensures that no excess CPU time is used processing events. As mentioned before, modules external to the main loop can send commands using the commandData user event.
The case structure will contain the implementation of the commands. It currently has a minimal set of code to test the basic functioning. Note that there are cases for both Run and Stop while there is only one Run button. Look at the “Run”: Value Change event and note how the two commands are dispatched from a single event. Command selections can and should be dependent on the current state of the application.
In the Exit case, note that the front panel close event is currently disabled. This will be enabled after development for proper operation.
Finally, there is a clear error VI just before the loop finishes a single iteration. In a finished application, error handling can be placed at this location or in the individual cases. We will be placing error handling in a currently non–existent case after implementing data collection.
Next week, we will start to fill in the functionality of our UI shell.
02-27-2009 05:48 PM
Very nice. It is particularly gratifying that it is so far completely cross platform. Good ol' Pure G and no dlls or active X type thingys.
One question on design of the top level structure is that you combine the execution work loop with the GUI event structure. I usually break these out into two different loops for parallel execution. In this case any significant work will make the GUI unresponsive and the user will start double clicking controls. All the events are just to queue data, shouldn't the processing loop work independently to cycle through commands sent from the gui?
It may be a bit more compact to put this in the same loop but why did you pick that design?
03-01-2009 06:41 AM
I personally dislike the use of the default case when using an enum as the case selector. If you don't use the default option and you add values to the enum, your code will break, thus forcing you to deal with the new values and protecting you from potential mistake.
Also, when using the enum+variant messaging, I like creating VIs which will construct the variant for each message which has data in its variant. Those VIs can also have the enum as a constant and already push the message into the queue. This makes your diagram cleaner and more readable and also protects you from cases where you need to change the message data.
03-02-2009 08:46 AM
sth,
I put the event structure in the command loop so that events are processed synchronously. In the past, I have run into a variety of race condition/synchronization issues when separating the event and command loops and prefer to keep them together to make the program structure simpler. As you mention, it also makes it painfully obvious when something which consumes a lot of time/processor is placed in the command loop. I typically handle large processing tasks in one of two ways - use a modal progress indicator or, if the user can still do something useful while the calculation is in progress, use a separate loop and light an indicator on the front panel. I know from experience that the processing in this particular case should not be an issue on a desktop computer. If it becomes one, I will add another loop. I have no problem with multiple command processing loops, but avoid them if I can to keep synch issues to a minimum.
Rest assured, this project will be pure G, start to finish. The goal was to create something that anyone can run and that I can port to my old Dell Axim x50v PDA, and pure G is a good first step. Stay tuned.
03-02-2009 12:21 PM
DFGray wrote:I put the event structure in the command loop so that events are processed synchronously. In the past, I have run into a variety of race condition/synchronization issues when separating the event and command loops and prefer to keep them together to make the program structure simpler.
It is simpler but you give up any multi CPU performance. That LV is inherently parallel is one (the only) real advantage over a text based development system. I can write good structured text that is almost as visually easy to parse as LV. With modern syntax coloring engines, the difference in icons vs. structured text is not that great.
But the challenge I give the traditional text coders is to keep track of many mulitple threads. This is why dataflow is a powerful concept. Yes it does lead to asynchronous execution but if you impose synchronicity on your code you are working against LV. I get most first time programs where everything is in a sequence structure. "Just to be safe".
Of course this is much better but my first design goal is that any application should take advantage of a much hardware as possible, degrade gracefully on slower hardware and play nice with other running applications. This simpler structure severely handicaps that first goal.
In this case it is simple enough to "get away" with it since as you point out that there should not be anything that takes significant CPU time. But that may not be true on the PDA?
If you are going to run it on the PDA will we be discussing techniques to seamlessly use both NIDAQmx Base and NIDAQmx in the same system? That is a real challenge. I look forward to more of this!
Cheers!
03-02-2009 01:40 PM
sth wrote:...But the challenge I give the traditional text coders is to keep track of many mulitple threads. This is why dataflow is a powerful concept. Yes it does lead to asynchronous execution but if you impose synchronicity on your code you are working against LV. I get most first time programs where everything is in a sequence structure. "Just to be safe"....
Ditto that. I have been troubled by what is sometimes called a Queued State Machine (the one where all of the states of a single loop are pushed into or poped-off a queue) since they effectively force all of those "other" operations to execute one after they other rather than speading the work around. Coming from a machine level programmer background, this was an easy construct for me to get my head around since I was always pushing stuff onto or popping the stack. But for LV I prepfer to have nice little critters that interact with each other rather than almighty hydras that eat their own tail.
If the sender of a command message or request needs to know the results or when it was done, I just put a queue ref into the packet queued up to make the request. When the work is done, whoever did the work puts the result in the queue specified by the requester.
Re: Strict Type definitions
I generally only use STRICT type defs on GUIs.
Thank you DF !
Ben
03-10-2009 04:49 PM
Hi Damien,
What's the significance of your project structure for Controls and Main | Controls?
If you are going the have a prefix on your VIs and Ctls names then you should be consistent.
Regards
Ray
03-11-2009 05:55 AM
Ray Farmer wrote:Hi Damien,
What's the significance of your project structure for Controls and Main | Controls?
If you are going the have a prefix on your VIs and Ctls names then you should be consistent.
Regards
Ray
Its confession time!
I realize that the "standard" says use prefix names etc etc. I understood this practice as recomendations to help organize files and avoid naming collisions. With the advent of the project and the ability to have vitual folders and libraries the prefix naming scheme gets in the way of re-use because the origninal contents of teh VI may not be the same in the new project.
If you stop and think about it LVOOP actullat prevents naming VIs diffenently if they are being used as over-ride VIs.
SO is the practice of prefixing VI names a practice that should go the way of the eight character names* ?
Ben
*DOS restriction
03-11-2009 04:05 PM
I am reading 'Clean Code' - Robert C Martin at the moment and in there it suggest that any prefix to names is not required these days, but I was wondering if its still essential with LabVIEW but as Ben points out maybe its not.
Ray.
03-11-2009 04:56 PM
Ray,
There is actually no significance other than that I missed moving the top level Controls into Main>>Controls. That's what happens when it is the end of the day on Friday
.
Ben,
The last project I started with new code was in prerelease 8.0. It had the distinction of being the first shipping large application ever designed with the LabVIEW project and LabVOOP. But wait, you say, LabVOOP did not ship until 8.20. Correct. I had to re-engineer with libraries. It was then I found out that most use of prefixes was unnecessary. In fact, the separate namespacing nature of libraries/objects greatly simplifies the creation of plug-in architectures. So, long-winded answer to a short question, I think that prefix namespacing should go the way of 8.3 filenames. I will correct the project going forward.
Thanks for your comments!