‎02-20-2009 03:43 PM - edited ‎02-20-2009 03:51 PM
Feedback on the GUI was excellent and the following changes will be made:
We now have the GUI roughed out and the method of data acquisition determined. It is time to look at program architecture. It is highly tempting and very easy to simply paste the demo acquisition code behind the GUI concept and call it good. This may be OK for a home project (which this actually is), but does not result in robust, maintainable code.
As is normal for this type of code, there are two main loops — GUI and data acquisition. For GUI use, I prefer a synchronous, event driven state machine. For data acquisition, a simple state machine will work. Data acquisition will be bundled into an object. Communication will be handled with queues and user events.
Let's look at the communication in a bit more detail, since it will be a major determinant of the stability and ease–of–maintenance of the code. Do not neglect planning at this stage. It will save you endless headaches later on. Build in flexibility. You can expect things to change as you learn new things in the course of development.
We will need the following communications pathways:
‎02-20-2009 03:44 PM - edited ‎02-20-2009 03:51 PM
Now for the data structures. We have identified the following:
While we could conceivably put these all in the same data set, the code will be more scalable if we break it up. We will use LabVIEW objects to encapsulate the data. This also allows us to associate methods with each data set. We can also make singleton data objects from them if, as in the data acquisition case, we would like to access the same object in different parts of the code. This is done by loading the object into a single–element queue (you can also use an action engine).
Are there any other objects we may want to create? Yes! We need a sound analysis system to determine the resonant frequencies and the notes they represent. The data for this is just the configuration data (mode intensity cutoff, trigger level, data length, A frequency, etc.). Thus, the methods for the configuration data will be the analysis. We will be using even tempered tuning for now, but future use may expand to other tuning styles such as Pythagorean or one of the various meantone tunings.
Finally, is there any code out there we can reuse? The answer to this question is almost always yes. However, make sure you look at integration costs and licensing issues. In this case, there is a project that has a lot of code we can use. Check out NI Musical Instrument Tuner.
Were this a real project, I would write all this information up into a specification and put it under source code control as a living document. Said specification would be updated as the project advanced and would serve as input for the help and documentation for the project. We would also look into such subjects as panel resizing for vision impared users, color–blindness issues, and localization. Since it is a home project, I will not do this.
Next week, we start actual coding.
‎02-21-2009 01:39 PM
Thank you very much again. I actually was waiting friday afternoon for that post of yours, to get some inspiration for my own architectural design studies. Following such a project week by week is a greate way to exchange and learn. Once this series is finished, we should discuss to continue with a new project, maybe some of the great architects of the community could maintain one... (I guess Breakpoint would be the place to talk then...).
But to get conversation on techs: I not yet really get the complete archtecture. Maybe you could provide as with a scetch/draft. I like graphics (and my favorite programming language is...), simple bubble-arrow-style is standard in my own coding notes, uml earns bonus points.
Here are my open questions:
* Concerning GUI loop: will it be that Event-State moves to proper react on event state or do you drive a queued state machine from the event loop?
* Concerning DAQ loop: where are the in and out points of the queue located? Do we write to a data object via queue? Is it a producer/consumer pattern inside the Aquire state? Do we send the data to the GUI loop(s)?
* Configuration Windows: How will you call them, to be in need of the event? You could just place the Configuration Dialog in a locked Event case.
Some comments on the concept (as far as I understood it now):
* Data acquisition data queue, assumes a single consumer: Easy to scale up creating a multiplexer VI (input queue is dequeued, output queue array gets the data enqueued in a for loop).
* Reuse Code: Never forget OpenG. I personally like the Configuration VIs.
* Instead of boolean data type for 'Exit User Event' I would use an enum (in this case only the element Exit), because I can scaled it if necessary and the overhead is zero.
* For the communication pathways, there was a brilliant nugget by Ben how to link queues to a type defed data (and it works with events, notifieres as well). Makes it easy to change the code if necessary.
Felix
‎02-23-2009 09:35 AM
Open questions answered:
Concerning GUI loop: will it be that
Event-State moves to proper react on event state or do you drive a
queued state machine from the event loop?
The event structure will be a state in a queue driven state machine, placing commands on the queue. The queue data type will be a strict typedef enum command and a variant for data. The queue cluster will also be strictly typedefed. Thus, the commands will execute synchronously with the events. I have used separate event structure and command state machine loops in the past, but have always had more issues with it than combining the two.
Concerning DAQ loop:
where are the in and out points of the queue located? Do we write to a
data object via queue? Is it a producer/consumer pattern inside the
Aquire state? Do we send the data to the GUI loop(s)?
Input of the queue is in the acquisition loop. Output will be either in analysis or calibration, depending on what is running at the time. My original idea was to use a user event to signal the GUI loop to analyze the data (either for display or calibration). Data rate is fairly low and the update rate will be about 10Hz, so the GUI loop should not be flooded due to this. However, a cleaner architecture may be separate loops for the analysis and calibration, said loops (state machines) being embedded in the appropriate objects and always running. The loops would trade off being consumer, depending on what functionality is currently active.
Configuration Windows: How will you call them, to be in need of the
event? You could just place the Configuration Dialog in a locked Event
case.
The configuration windows would be called using the Run method and auto cleaning their references when the button for that window is pressed. They would be modal so interaction with the root window would not be possible while the configuration window is up. This allows the GUI loop to keep executing while the configurations are up.
I originally intended to include some graphics for the architecture, but ran out of time.