04-07-2014 10:53 AM - edited 04-07-2014 10:53 AM
Hi all,
I have a project which is beyond my current (very basic) labview skills and I am looking for some advice on the best way to structure my code as well as some functions and resources that I may find useful. I would like to learn as much as possible about good labview coding practices, how to implement design patterns in labview etc. I have Lab View 2013
I need to do the following:
I've been reading a bit about actors and message queues and think that actors might be quite useful for point number 3 but am struggling a bit to see how to use them.
Any advice is greatly appreciated. Thanks in advance
04-07-2014 11:49 AM
Could you quantify what your current labview skills are?
I believe a logical progression to learning is more effective than jumping right into the deep end, so in order to suggest some learning material I would need a starting point
- What design patterns have you used so far? How comfortable with them are you?
- You mention polymorphism, is this in relation to LV classes, or the polymorphic vi? How familiar are you with LV Classes?
- Regarding the Actor Framework, you do not need to understand it in order to use it. It is a logical progression from the Queued Message Handler - are you familiar with this design pattern?
- How certain are you that you will want to implement a plug-in architecture? There is a fair amount of information on the factory pattern which has been repurposed as a plug-in architecture in LabVIEW.
04-08-2014 02:09 AM - edited 04-08-2014 02:13 AM
HI yenkip,
thanks for the answer. Unfortunately I have not used design patterns in LabView before but, I have in C++. Unfortunately this means I also haven't had any experience with LabView classes. I will start reading up on them now.
When I mentioned polymorphism, I was thinking in terms of classes. I was thinking that perhaps polymorphism may be a way to implement the different behaviour of the message parsing code.
The plug in architecture would be cool to have. It would be cool to have a basic program now, and then be able to plug in modules in the future.
EDIT: regarding the actor framework, I have looked at bothe the actor framework and the message queue handler. I have created some basic projects from the templates. The message queue handler was somewhat easier to get my head around
04-08-2014 05:19 AM
I came to LabVIEW from C++ as well, and basically learnt LabVIEW by trying to implement a few design patterns that I knew from other languages.
There is so much one can learn that I won't fit it in one post. I'll give a suggestive overview here, so you can ask more specific questions as they come up, and you have a few tag words to look for whitepapers and examples.
You have probably seen the project templates in the getting started window or the "New..." menu option, so to continue from your last post:
Try to create this in a state machine. Even better, look at the state machine template and make your own from scratch.
This is an event driven producer/consumer pattern. Imagine that your state machine from above can now be sent messages from another loop as well as its data acquisition. Add a few more consumer (state machine) case frames to handle the new events.
Alternatively, you could create 1 event driven producer loop and 2 consumer loops; one to manage the events, and one to manage the data capture. You will need 2 send queues. An interesting thing to learn here would be reentrant VIs. Wrap the queue pallette in a set of you own VIs which define datatypes. Set your VIs as reentrant and use those same VIs in each loop.
This is a good time to introduce classes.
Create a base class "device" and define the various methods that are common to each.
Use right click -> New static vi / dynamic vi. A dynamic vi is similar to a virtual method in C++, it allows you to override the vi in a child class and is the mechanism for dynamic dispatch.
Create 2 new device classes which inherit from the base class (right click -> properties -> inheritance).
Right click -> New -> VI for override, will create an override method for a previously defined one in the parent
Regarding the plug in architecture, there are a few options. For instance you could opt for a class based factory pattern, or maybe a standardised messaging protocol that each plugin knows how to parse. Leraning the above should hopefully allow you to make an informed decision.
Addendum:
Some other things to look up would be:
04-08-2014 08:46 AM - edited 04-08-2014 08:50 AM
Hi yenkip,
First of all, thank you for the excellent post. It is extremely helpful.
I’ve been having a read of the different topics, and looking at examples. I think I’m starting to get my head around things.
Could you please elaborate on what you said when talking about how I could send message “Alternatively, you could create 1 event driven producer loop and 2 consumer loops; one to manage the events, and one to manage the data capture. You will need 2 send queues. An interesting thing to learn here would be reentrant VIs. Wrap the queue pallette in a set of you own VIs which define datatypes. Set your VIs as reentrant and use those same VIs in each loop.”
Also, I read a bit about writing device drivers in LabView. It seemed quite good. Do you have any experience in this? I’m not yet, entirely sure how easy it would be to choose which driver to use. I guess I could have some sort of factory pattern that uses the Vis from the right driver.
I think, for now, I will leave the plugins and focus on basic functionality. In the future I may look at using a sub panel and loading stuff from libraries.
I am going to try to write a prototype for message handling. My device class will contain a state machine that handles the processing of incoming messages and an event queue for handling user events. I guess this will be separated into 2 Vis that will run in parallel.
Here is a list of resources that I found helpful, just in case anyone else reads this:
Functional global variables http://labviewwiki.org/Functional_global_variable
Action Engines http://forums.ni.com/t5/LabVIEW/Community-Nugget-4-08-2007-Action-Engines/td-p/503801
Creating LabView Classes http://zone.ni.com/reference/en-XX/help/371361J-01/lvconcepts/creating_classes/
Static vs Dynamic dispatch calls http://forums.ni.com/t5/LabVIEW/static-verse-dynamic-dispatch-call/td-p/739497
Top 5 rookie mistakes (includes info on local variables) http://www.ni.com/newsletter/51735/en/
Plug ins example https://decibel.ni.com/content/docs/DOC-33365
Reentrancy http://zone.ni.com/reference/en-XX/help/371599H-01/lvfpgaconcepts/fpga_determine_re_nre_subvi/
http://zone.ni.com/reference/en-XX/help/371361J-01/lvconcepts/reentrancy/
04-08-2014 10:51 AM
I'll admit that sentence wasn't very clear. I got over excited with the thought of using reentrant VIs 😛
I've attached a small project which uses my simple Queued Message Handler (QMH) API. The demo shows how one might split the process data from UI data using 2 loops, and I hope demonstrates reentrancy.
All the VIs in the QMH API are reentrant, meaning each instance of the VI is a duplicate, in its own memory space. This is a direct contrast to the Action Engine principle, where the VI has a single instance and everywhere it is used refers to the same memoy space.
Could you define your meaning of 'creating a driver'? I suspect your meaning to be wrapping an API around communications tools like VISA, DLL calls etc. You can create a developer-friendly API by wrapping each RS232 / RS485 etc. command in a new sub VI.
If you mean genuinly writing a device driver, there was someone on the forums a few years ago who demonstrated how to write a USB driver in LabVIEW, can't find the posts now.
Lastly, you mention sub panels; one thing to look at is the GERM - basically an Action Engine in a sub panel. Might be an interesting thing to learn!
04-10-2014 10:37 AM
Hi yenkip,
thanks for the example code. I've had a read through it and have tried to use it, and other stuff I've learnt, to implement a quick and dirty event Q/incomingSerial State machine. I haven't yet looked at the GERM examples but, from reading through the forum, it sounds like it will be really useful.
When I talked about creating a driver, I was referring to the "Instrument Driver" project template. http://www.ni.com/white-paper/3511/en/#toc3 It seems just like a more integrated version of what we were talking about with creating an abstract Base class for my devices, and using dynamic dispatch to implement the virtual methods.
I've attached my code, if you are interested. I'd love to hear what you think. I do warn you, it isn't really documented and has lots of bugs and unhandled cases in it. I wanted to get something working just to test out how it will fit together. I have 3 things working in parallel in my main VI:
What it's made to do is connect/disconnect to a serial device at 57600 baud and receive incoming characters. I'm not really sure how to handle errors or how to cleanly stop my tasksyet as i have 3 different tasks running in parallel. . I haven't put much thought into it yet, i guess i could have a stop or exit event posted and set a flag in my class that the 3 tasks check.
In the future I plan to have another "data handling class" that will do stuff like plotting and data logging. Again, i will have base class and inherit this for each of my devices. That way I can plot and display the data differently for each device. I think I will pass the data between my classes using a message queue again.
Once again, thank you for all your help
04-10-2014 12:36 PM
Bugs and unhandled cases I can overlook if I can see an understanding of principles.
This sounds stupid, but the first thing that I noticed was almost every Front Panel and Block Diagram (FP and BD) window was maximised to the monitor. This makes code reviews really awkward to do if I have to keep resizing everything to see where [this] VI fits into the rest of the application.
There is an interesting divide over people wo like to use a state enum and state string. Well done on making the enum a typedef control, that's exactly the right thing to do.
Sub VIs
Yup, using Sub VIs prevents a big unmaintainable mess of spaghetti code.
You don't have to draw a picture, but putting a name or description or something in the VI Icon will greatly improve readability
Action Engine
Your Action Engine (AE) is a member method of your class. Just as in C++, a class is not a data store, it is a definition of a data structure which is then instantiated in one or more locations. In LabVIEW your class would be the stored as the data of an AE. As it stands you are only able to create a singleton instance of your class to facilitate the use of the AE.
Class
I can see you are trying to use the AE like a pointer, as you would in C++
A class method acts like an access/mutator method. If you create a new class method by right click->New->VI from Static / Dynamic Template then you can see the intention to use the (un)bundle nodes to access and mutate class private data.
You have also blurred the separation of the system data and the device data. By system data I mean the Queue reference, and by device data the COM Port and readings. Design one class as a device, and use a LabVIEW Library or another class if suitable for the system design.
Nested While Loops in HandleIncoming.vi
I think your intention here is to place a state machine inside a message receiver? Nested loops are often (not always) a big cause of problems, so try to avoid them.
Rather than trying to combine two design patterns, imagine that the state enum is action in the message handler.
User Interface Controls
Use an event structure! They are more elegant and much more scalable
Actions
Post that up and we'll see about expanding the project to include more stuff.
04-15-2014 06:33 AM
Hi yenkip,
I've been having a go at implementing the changes you described and I must admit, I'm a bit confused about implementing the suggested changes for processing incoming serial data. How could I create a task that waits for, receives, and process incoming serial data without nested loops?
I've attached what I have do so far, just without handling of serial messages. I think I've understood and implemented all other changes.
In the meantime, I am going to look at using subpanels for displaying groups of buttons and indicators. I guess I would use a listBox to select which panel to show. I think a listBox might be a good option because you can programatically add items to the list so, if I was to implement plugins, I could search a plugins folder on start up and add them to the sub panel list.
After that, I will look at seperating the device interface and GUI more so that, if I ever want to, I could create a DLL for interfacing with the device.
Looking forward to your reply