LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Architecture for Data Acquisition, sending commands, and polymorphism

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:

  • Continuously receive messages over a serial port, parse them, and update values which I will use in several realtime plots  and to write to a file
  • Be able to send messages when the user presses a button, even while the program is receiving messages.
  •  Be able to easily swap out the message parsing code and change the values that are used for plotting without huge amount of rework. I have 2 custom devices that although similar have slightly different message structure and contents. eg, one version of the software will need to interface with device 1, that sends 3 temperature values and one pressure value- Version 2 of the software will need to interface with device 2 that sends 2 temperature values, 2 pressure values, and a power value.
  •  It would be nice, in the future, to have some sort of plug ins feature where i could choose to use different vis for added functionality.

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

0 Kudos
Message 1 of 9
(3,618 Views)

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.

_____________________________
- Cheers, Ed
Message 2 of 9
(3,600 Views)

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

0 Kudos
Message 3 of 9
(3,579 Views)

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:


  • Continuously receive messages over a serial port, parse them, and update values which I will use in several realtime plots  and to write to a file

Try to create this in a state machine. Even better, look at the state machine template and make your own from scratch. 


  • Be able to send messages when the user presses a button, even while the program is receiving messages.

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.


  • Be able to easily swap out the message parsing code and change the values that are used for plotting without huge amount of rework. I have 2 custom devices that although similar have slightly different message structure and contents. eg, one version of the software will need to interface with device 1, that sends 3 temperature values and one pressure value- Version 2 of the software will need to interface with device 2 that sends 2 temperature values, 2 pressure values, and a power value.

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:

  • LV2 Style Global / Functional Global Variable / Action Engine
  • Understand Local Variables. They don't do what you think.
  • Sequence structures. In almost all cases, you are better off making a state machine
_____________________________
- Cheers, Ed
Message 4 of 9
(3,567 Views)

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/

 http://labviewwiki.org/Reentrant_VI

0 Kudos
Message 5 of 9
(3,559 Views)

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!

_____________________________
- Cheers, Ed
Message 6 of 9
(3,547 Views)

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:

  1. handle incoming serial, this is a really basic state machine at the moment
  2.  An event queue handler, this handles event like connect, disconnect etc. This uses the example code you gave me, I just changed the Action type to be an enum instead of a string.
  3.  a really basic handling of buttons. These buttons call vis that post events into the event queue so that my event queue handler can run them.

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

 

0 Kudos
Message 7 of 9
(3,507 Views)

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

  • Create a LV Library for the system architecture.
  • For the sake of learning, create a design pattern based on the event driven producer/consumer.
    • In the consumer put your COM Port VISA Reference in a shift register. The consumer is now the data store for the device data.
    • Learn to use the Event structure to send instructions to the slave
    • Use the enum / variant data type to send data to the class. e.g. set a new COM Port, set a new baud rate
    • Set the consumer timeout to be 1000ms and in the timeout case, read the data from the device and update an indicator
  • Rewrite your class so that it is just related to the device, and that each method has a class in -> modify -> class out layout
  • Replace the VISA Reference in your consumer with a class constant, and make each case a wrapper for the class methods. The consumer is now the data store for your class data.

 

Post that up and we'll see about expanding the project to include more stuff.

_____________________________
- Cheers, Ed
0 Kudos
Message 8 of 9
(3,494 Views)

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

0 Kudos
Message 9 of 9
(3,448 Views)