LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Design problem: RS232 communication

Hi,

 

I have a design problem for communication with a device via RS232. Since I'm normally a C++ programmer I might just look at the problem from a wrong angle and hope for some hints how to do it in LabVIEW.

 

The scenario:

 

A device is communicating with the PC via RS232. The device permanently sends data packets. At the same time, commands can be sent to the device and it returns replies. Data packets and reply packets are arbitrarily mixed, i.e. after sending a command there could be a couple of date packets before the reply comes back but the packets can be distinguished by an identifier.

 

At least one, ideally several VIs should communicate with the device. Commands should be sent by pressing buttons and the incoming data should be parsed (the packets contain mutliple data streams) and shown on graphs or saved to files.

 

My initial idea:

 

Coming from C++ I wanted to build a class for the communication that permanently reads the incoming data and splits it to reply and data packets. This class would then have a function to send out a command and would return the reply or a timeout and it would be possible to register and unregister listeners (I wanted to use queues for this) for the various data streams.

 

The problems I ran into:

 

There were a couple but the two most pressing problems were: how could I communicate with the constantly running sample VI (e.g. to stop sampling) and how could I propagate changes to the class to it (e.g. new listeners). Since it is not returning I don't see a good way to implement it as in instance funcion (i.e. pass it the object). I could probably not let the sample function run continously but call it periodically from outside. However I planned to implement the class as a singleton, so it could be used parallely from different VIs.

 

Is there a best practice for a case like this?

 

I'm glad about any hints or ideas.

 

Thanks,

 

Tobias

0 Kudos
Message 1 of 8
(2,193 Views)

The best bet is to use a single subVI that handles all of the RS-232 communication.  It would be launched dynamically and run parallel to any other code you have.  It would handle the continuous reading part.  When the special commands are sent, they would be placed in a queue that the subVI reads and handles accordingly.  If you need to have a section of code pause until it gets the response back, it could wait on a notifier.  When the subVI gets the response back, it would place it in the appropriate notifier.

 

The one tricky part is know whether a response is to a query, or part of the continuous data stream.  But this is an issue you'd have no matter what programming language you use and  would have to be solved in the communication protocol between the two devices.  Does the serial device have any codes it prepends to the data to tell whether it is responding to a query or whether it is giving its general data?  However you determine the two types of messages, it is best to have a single intelligent subVI  handle that and act as the interface with all the rest of the program.

 

I believe this is comparable to what you are saying with your class idea.  Only I wouldn't call it a class because the concept of classes and object oriented programming in LabVIEW is a complication in itself (search on LVOOP).

0 Kudos
Message 2 of 8
(2,178 Views)

First of all, thanks a lot for your reply. It seems that at least I'm not totally wrong with my solution. However I feel that I couldn't make the problems I see very clear.

 

The distinction of reply and data packets is not a problem. They have unique IDs and can be detected.

 

My first question on what you wrote is: how do I launch the subVI dynamically? And where? The reason why I wanted to implement it as a class was that I  wanted to implement it as a singleton to communicate with it from different other VIs at the same time. For this I also wanted to use an array of queues where VIs could add or remove their own queues. And the continously running VI would put the incoming data in all of the registered queues. However, if the VI is already running, I can't see how to best propagate changes to this "listener array" to the VI. I could use global variables (which I understand is not recommended and as a C++ programmer I'd say for a good reason) or I could put it in a queue (yielding a queue with an array of queues - not the most elegant concept). Coming from C++ I thought I could have this array of listeners as a member of the communication object and the continously running VI as a member function accessing it (would be an interrupt function in C++, called by a timer). But since there is no this-Pointer in LabVIEW this is not possible as far as I can see.

 

A similar problem arises from how to stop the continously running subV. I could tell it to end itself via the commands queue. But this seems strange to me somehow.

 

I hope I could make my problems a little more clear. I'm not a native english speaker so this is kind of hard sometimes. 🙂

 

Tobias

0 Kudos
Message 3 of 8
(2,156 Views)

Here is an example I cut out of one of my applications for how to start a dynamic VI.  It was a separate data logging process.  First I created a queue with an enum for certain commands  (Such as Init, Stop, Log).  That queue reference gets passed into the main loop of the program.  I also sent it into a single frame flat sequence (not necessary, but I did it to visually group the code).  It opens a reference to a VI and launches it.  It passes in a queue reference and error cluster. 

 

That VI will subVI block will continue to run until I pass in the stop command to tell it to exit its while loop.  Only then does the close VI command execute and that flat sequence frame ends.  Using the queue as a means to end the subVI is a perfectly legitimate way to do.  It may seem strange, but it is a good dataflow practice.  If you put the stop command at the end of the queue, it gives the subVI a chance to process all commands in the queue ahead of it.  If you need to stop the subVI immediately, then you can queue up the stop command at the front of the queue and it will be the next command processed.

 

The queue itself is just an enum with a command which drives a case structure.  And the other is a variant so various types of data can be passed in and processed.  The command sent will tell the subVI how to decode the data sent in the variant.  Think of it as a producer/consumer architecture, but the consumer loop is broken out and placed in its own subVI.

 

I would also recommend you look at Ben's action engine nugget.  While this isn't technically an action engine, I think the concepts presented there will be useful to you.  And the action engine behaves like an object where it can contain data and have methods just like a class/object/property/method part of object oriented programming without getting into the complications of LVOOP.

 

I think a listener array of queues is a good concept.  This may be an array of single element queues in the event you want the latest data and don't want to accumulate old data which might happen if the data is created faster than the parts of the program that are dequeueing the data are able to do it.  Also, the action engine nugget was a more advanced extension of the LV2 style global or functional global which is a way to transfer data to different portions of your code.

 

To propagate changes to the listener array, you could do that by way of the queue.  You could have a Register Listener and an Unregister Listener commands as part of the command enum.  In those case structures, it would add a new element to the listener array, and remove an element.

 

What you want to do here are some more advanced concepts for LabVIEW programming that I didn't get a good feel for until I took the LabVIEW intermediate classes.  But based on your C++ experience, I think you are doing it the right way.  Keep working on it, keep searching and reading the forums where these concepts are discussed, and keep posting questions.   Also, your English seems just fine to me.  I wouldn't have realized you were a non-native English speaker.

0 Kudos
Message 4 of 8
(2,137 Views)

Hi,

 

thanks. Since almost the same thing was suggested to me in a German forum I guess this is really common practice (using one VI with different methods controlled by a queue). It still seems a little "unnatural" for me but my biggest concern (bad interface description) was shattered by the suggestion in the link you sent me to wrap these functions with wrapper VIs, thus caller VIs won't have to deal with the call-by-queue-mechanism. This might also be easier to port to a different implementation later. However I still see the danger that the continously running VI could easily become bloated. 

 

It also requires me to change the way I have looked at VIs until now. In our course they told us that VIs are basically functions. Using this design patterns, the VI becomes more of a module, really (Like a C module implemented in a C-Source file). But I will try it. It sounds as if it could work. 🙂

 

I will still look into the OOP solutions a little more, though. Do I understand you correctly that you wouldn't recommend using LVOOP because it's still buggy? What about dqGOOP for example? This sounds like it could do what I need (however it doesn't seem to implement things like polymorphism, late binding and inheritance so I don't quite see what's so OOP about it. It seems more like programming with structures in C.)

 

But back to your suggestion. I still have a couple of questions:

 

- How do you return values from the module? Would you use a queue for that as well?

- Where would the parameter queue be held (created and passed to the VI)

- My VI has to be constantly sampling and this shouldn't be interrupted too long by other functions as adding a listener. However both functionalities have to access the same kind of data. Is there an easy way to parallelize this? Would the sampling be a case in the case diagram that's always used if no command was sent to the VI or would it somehow run parallely?

- Would it be possible to make the VI reentrant and in this way use it simultaneously on different COM ports (using different parameter queues as well)? I'm not sure if I will need this but it would be neat if it could work.

 

Well, I will fool around some more. Thanks so much for your help. This is kind of exciting since the concepts are quite new for me. Btw, is there something like an academic theory (computer science) for LabVIEW? I came across functional languages in university but data flow languages are still a new concept for me.

 

Tobias

0 Kudos
Message 5 of 8
(2,113 Views)

Another question about the "dynamically starting" of the VI:

 

How is the path handled? Is it guaranteed that it always takes the VI from the project or does it just search for the first VI by that name it finds in the file structure? Does this still work when building an .exe from the project? What happens if the VI is already running? Can you test for this?

 

While I'm at it: is there a way to stop LabVIEW from searching for subVIs it can't find when openin a VI? This resulted in very unexpected behaviour sometimes where it would find the VI somewhere else (with the same name but maybe an older version).

 

Thanks,

 

Tobias

0 Kudos
Message 6 of 8
(2,109 Views)

tfritz wrote:

Hi,

 

thanks. Since almost the same thing was suggested to me in a German forum I guess this is really common practice (using one VI with different methods controlled by a queue). It still seems a little "unnatural" for me but my biggest concern (bad interface description) was shattered by the suggestion in the link you sent me to wrap these functions with wrapper VIs, thus caller VIs won't have to deal with the call-by-queue-mechanism. This might also be easier to port to a different implementation later. However I still see the danger that the continously running VI could easily become bloated. 

 

It also requires me to change the way I have looked at VIs until now. In our course they told us that VIs are basically functions. Using this design patterns, the VI becomes more of a module, really (Like a C module implemented in a C-Source file). But I will try it. It sounds as if it could work. 🙂

 

I will still look into the OOP solutions a little more, though. Do I understand you correctly that you wouldn't recommend using LVOOP because it's still buggy? What about dqGOOP for example? This sounds like it could do what I need (however it doesn't seem to implement things like polymorphism, late binding and inheritance so I don't quite see what's so OOP about it. It seems more like programming with structures in C.)

 

I don't know if LVOOP is buggy or not.  I think early on it was buggy and things have improved in recent versions. I have read that it doesn't have all the features that you would have in OOP like C.  I wouldn't recommend it only because I'm not familiar with it at all.  I can't recommend something that I'm not comfortable with.  If you go that route, plan on spending time in these forums and in LAVA to reading up on what others have done.  I haven't hard of dqGOOP.

 

But back to your suggestion. I still have a couple of questions:

 

- How do you return values from the module? Would you use a queue for that as well?

- Where would the parameter queue be held (created and passed to the VI)

 I would store all of these in a functional global variable.  This is the VI that stores data in shift registers.  Ben's action engine nugget is an advancement on that.  This allows for both the calling VI and the parallel running subVI to get and set the data as needed.  It runs quickly so neither process should be forced to wait while the other  VI is doing its thing.

 

- My VI has to be constantly sampling and this shouldn't be interrupted too long by other functions as adding a listener. However both functionalities have to access the same kind of data. Is there an easy way to parallelize this? Would the sampling be a case in the case diagram that's always used if no command was sent to the VI or would it somehow run parallely?   Yes.  There are a couple of ways of doing this.  One would be for the dequeue to have a timeout function.  In the event the dequeue times out, you run the code that is doing the acquisition.  I think a better method is that the code that does the acquisition enqueues its own command again to the end of the queue.  Let's say that is command A.  So when case A finishes, it enqueues A, which seeds itself to run again.  So if nothing else comes into the queue, it just executes A , A, A, A.  But let's say another section of code needs to do something such as command B.  It will slip B into the queue while A is executing.  So you would A, B, then A again, because A would get slipped back into the queue when the first A finishes, but B has already been put in while the first A was running.

 

- Would it be possible to make the VI reentrant and in this way use it simultaneously on different COM ports (using different parameter queues as well)? I'm not sure if I will need this but it would be neat if it could work.

I think you could do this.  It may be a case where the VI is saved as a template  (.vit) and you initiate it multiple times.  I haven't needed to do this before, so I'm afraid I can't provide any details or useful tips. 

 

 

Well, I will fool around some more. Thanks so much for your help. This is kind of exciting since the concepts are quite new for me. Btw, is there something like an academic theory (computer science) for LabVIEW? I came across functional languages in university but data flow languages are still a new concept for me.

 

Tobias



tfritz wrote:

Another question about the "dynamically starting" of the VI:

 

How is the path handled? Is it guaranteed that it always takes the VI from the project or does it just search for the first VI by that name it finds in the file structure? Does this still work when building an .exe from the project? What happens if the VI is already running? Can you test for this?

 

While I'm at it: is there a way to stop LabVIEW from searching for subVIs it can't find when openin a VI? This resulted in very unexpected behaviour sometimes where it would find the VI somewhere else (with the same name but maybe an older version).


In my case, I just had the path hardcoded.  It is my only instance, I'm not planning on moving the VI's.  If you don't have the path, it will take a VI by that name if it's in memory.  If it isn't in memory, it starts searching relative to the calling VI's path.  One thing I know, if you are dealing with relative paths, a subVI has a different relative path in an .exe as opposed to the development environment.  The name of the .exe becomes a folder.  So in development, if your sub VI is mySubVI.vi.  In an executable, its path is MyExe.exe\MySubVI.vi

 

For all of this, I recommend searching the forums to get more details.

 

If it is searching for a VI, you can hit ignore.  But of course you'd have to do it before it finds it.  When you are dealing with versioning issues, I recommend making a backup copy of the entire directory structure elsewhere.  Some location where it shouldn't stumble across it.

0 Kudos
Message 7 of 8
(2,090 Views)

Hi Tobias,

 

The design problem that you have asked in this forum exactly matches my design problem. I'm interested to know the approach you finally adopted? Did it work out well?

 

Thanks

Amit

0 Kudos
Message 8 of 8
(1,380 Views)