LabVIEW Development Best Practices Documents

cancel
Showing results for 
Search instead for 
Did you mean: 

Plug-in Architecture using Packed Project Libraries (lvlibp)

Features

Plug-ins:

  1. Abide by a specified interface.

  2. Are independent of each other.

  3. Don’t result in name-conflicts when used in a built application.

  4. Installation is independent of main application installation.

Main Application:

   1. Is extensible without code modification to main application.

   2. Depends only on plug-in interface, not on specific plug-ins.

   3. Loads plugins dynamically.

   4. May utilizes plug-ins as UI elements or OO Design Pattern such as Strategy or Command.

   5. Requires no modification to successfully run in development or run-time environment.

   6. Installation is independent of plug-in installation.

Instructions

  • Download the PowerPoint presentation to follow step-by-step how to create a plug-in architecture using packed project libraries. 
  • Download 'PluginArchitecture_2011.zip' to get the source code created in the demonstration. 

The download is available for LabVIEW 2011.  If you want it in a previous version, follow the steps in the presentation to implement it yourself.

I'm interested in receiving community feedback, so please comment or ask questions.

Comments

In response to questions about displaying plug-ins in a sub-panel and communicating with message queues, I attached a version that does this in the simplest manner I can imagine:

  • Communication mechanism is string messages using front panel control value change events.
  • Plug-ins are given a reference to the sub-panel so they insert themselves and run using Asynchronous Call-And-Forget.
  • "Exit" message (sent automatically on Panel Close event), signals the plug-in to safely end execution.
Comments
Vijay_J
Member
Member
on

Hi Joachim,

Reason for Error 7 in general is "LabVIEW:  File not found". Check the path from where the application is trying to Load the Plugin Class.

Second: PPL is just like DLL, it is already in built form. Basically LabVIEW loads it and uses in source code as well as in application. This will be case for Daqmx driver dll as well. In source code these "DLL" are loaded from windows system32. LabVIEW application builder copied in "data" support directory folder, when building the executable.

Hope this helps.

Best Regards,

Vijay.

Joachim082
Member
Member
on

Hi Vijay,

obviously the ppl is trying to load the "Get LV Class Devault Value.vi". I have attached it in the ppl and also in the "Always Included" section of the using exe. The exe shell use the class from a network share \\[IP]\[Share]\eval.lvlibp, but the ppl is also duplicated in the data folder of the using exe.

Best Regards,

Joachim

Vijay_J
Member
Member
on

Hi Joachim,

Honestly i could not understand your issue completely from the above description.

Application builder copies those files(Plugin or *.lilibp) in "data" folder while creating the executable, since you have added those plugins(PPL) in the "Always Included" section of the build. By the way what is the need to add those plugins in the "Always Included" section? If you add them in executable, than it is no loger a plugin architecture.

Easy way to check the file path from which you application(executable) loads the plugins is to use the pop up message to display the actual file path.

Best Regards,

Vijay.

Joachim082
Member
Member
on

Hi Vijay,

I have posted an example of the problem on the forum --> http://forums.ni.com/t5/LabVIEW/LV-OOP-Plugin/m-p/2435574#M749796 with an example code. I hope this helps to unterstand my problem.

Best Regards,

Joachim

JPSaez
Member
Member
on

Hi,

I don't know if this post is still active, but I am using this architecture to create some of my plug ins. The problem I have is that my main application tells the plug in what action to perform by means of a queue, I load the plug in and I do not receive any error, but my queue only shows the default value, I'd say that this is what is happening.

https://decibel.ni.com/content/message/47486

I wonder if someone has tried this before and if you have discovered any other method to communicate between executables

Active Participant
Active Participant
on

JPSaez -

Queues don't work across contexts ("application instances" like DLLs, EXEs, LVLIBPs, etc.). You'll have to expose "enqueue" and "dequeue" VIs through VI Server and call them remotely from the external code. NJKirchner has a very nice library for handling communication across contexts (called REX) that he obstinately refuses to publish for the general public's consumption!!! Please send him a private message to heckle him about it.

JPSaez
Member
Member
on

Thank you David.

What you describe is what I understood that I had to do after having a chat with FabiolaDelaCueva, but I wanted to know what the people could suggest as other solutions.

I will contact NJKirchner to see if I can borrow that library and get another handy tool to play with

JPSaez
Member
Member
on

Here I have another question...

I am loading the plug-ins in a subpanel, and before using this architecture I was passing some data from my Main Application to the plug-in by using the invoke node that allows you to write the values of the VI controls in a subvi. I was reading other data by using the same invoke node to get the values from the indicators. By using this architecture it looks as if I couldn't perform these operations (I don't get any data back)

Does anyone know about any issue using these invoke nodes to write and read to/from the VIs in a packed library??

mike_nrao
Member
Member
on

I just uploaded a modified version of the example that loads the plug-in into a sub-panel and incorporates 2-way messaging. It's really a bummer that lvlibp loads in a different application instance; I would definately prefer to use queues or user-events rather than VI server.

Next step with this modified example would be to replace the string controls/indicators with message objects and use the Command Pattern.  Though using a common Message class shared between the main application and the plug-ins requires some extra steps to successfully build and compile.

JPSaez
Member
Member
on

Hi,

Sorry, there is no problem on passing data by using the invoke node to read the indicators, I did a silly mistake and I was trying to convert different data types, and this was something that I wasn't able to debug as the method to convert the data was a method of my generic plug in.

Sorry,

Jpina
Member Member
Member
on

Hi,

In relation to the question I did to use a queue to communicate my main program with the plug in using a queue, this is the solution I have found.

If we define the queue reference as part of the Generic Plug-In and we define the methods to Create, Enqueue, Dequeue and Close the queue as statics methos of this class. We can always call these methods in our main application after loading the desired Plug In, this will create the queue in the context of the Child Plug In (the desired one), so we will be able to communicate between our main and our plug in.

I upload some pictures that show how I do it, I would upload the code, but I don't find the way to do it. I hope you find it helpful

Generic Plug In.jpg

Plug In with queue.jpg

Main Program.jpg

Jpina
Member Member
Member
on

Hi all,

I have found another way to communicate the from the plug-in and this is the "User Events". I don't know why yet, but the User Events seem to keep their references across different contexts. This characterestic allow us to create and register the User events in our main program and then generate events in the plug ins.

We can use the user events then to perform different actions depending on the events or to send different kinds of data back from the plug ins to the main program.

Again, I cannot add code, but I add some pictures explaining how did I do it. If anyone is interested in the code, get in touch with me.

This is the PluginInterface Class with the methods for the User Events

PluginInterface Class.JPG

This is the plugin and the display method generating the user events

Plug In.JPG

This is my new TestApp VI using an "Event Driven Producer consumer", in this picture you can see how I send the references to the plug in once I convert it into the desired one.

Main Block Diag.JPG

And finally the main VI calling the desired display VI.

Call the plug in.JPG

Well, so here it is another way to communicate the plug ins with our Main VI. I hope you find it usefull

TimBotsM
Member
Member
on

After finding that a source distribution isn't the best solution for plug-ins I found this tutorial (great tutorial by the way)

While doing step 13 I realized that the code can be a bit shorter than demonstrated in the tutorial is you have LabVIEW 2012 or higher. This is due to the fact that LabVIEW introduced conditional tunnels. So my code has become this:

Locate all plugins 2012.png

If you haven't found the conditional tunnels yet you can find it using "right mouse button" on the output tunnel and create a conditional indexing tunnel.

Tunnel mode.png

Hope this helps someone.

shew82
Member
Member
on

If you use the "pattern" input of the "List Folder" vi, you can lose the for loop completely (without having to worry about conditional indexing):

lose the for loop.png

mike_nrao
Member
Member
on

Thanks shew82 and TimBotsM !  I love a cleaner block diagram.  I have some other updates planned to the presentation document that I hope to do in the near future. If you don't mind, I'll include and reference your contribution somehow.

matt26
Member
Member
on

Is it possible to add more functions/ methods to the plugin interface class once you have compiled it?

Jpina
Member Member
Member
on

Hi matt26,

I am not really sure of what is your question, but you can add new methods always that you create a new version of your PPL. Does that answer your question?

matt26
Member
Member
on

Hi Jpina,

Thanks for the response, that answers the question.

daniel.coons
Member
Member
on

Hello,

Maybe someone here could help me out with my current issue.  I am following this architecture for building my executable.  I have a Test Executive which is my main UI that is the brains of the program.  Under that I have a TEST class that is the parent class of all of my UUT plugins.  Each UUT is a child of the TEST class and has its tests defined as grandchildren of the TEST class.  That way I can load in the class and run a sequence of tests using a dynamic dispatch VI.  I also have instrument code defined in the same manner:  Instrument grandparent ->Oscope (etc) child -> Agilent Oscope (etc) specific instrument. 

I am using packed libraries now for the executable so that the TEST class has a .lvlibp that is the parent plugin (as in the tutorial) and the UUT class is the child plugin.  This is working with one issue:  I cannot figure out how to decouple the instrument code that it is pointing to disc rather than compiling with the UUT packed library.  With this issue, if I made a change to some of the instrument drivers, I would have to recompile all of the UUT plug-ins. 

Is there a way that I can tell the build specification to exclude pulling the instrument references into the packed library build?  Or do you have any other ideas for how to handle this situation? 

Thanks!

-Daniel
Technology Service Corporation

Jpina
Member Member
Member
on

Daniel.

I have already built something like that. There are few things that you should keep in mind.

  1. clases are context dependant, so your instruments must be only in the grandchildren. If not you should have to find a way to pass the classes between contexts.
  2. if your instruments classes are within the test application library (the grandchildren) once you build the ppl LV should include a copy of those intruments classes with the ppl and the compiled code should refer to them.

I hope this helps, if this does not answer your question let me know, I may be able to help you.

daniel.coons
Member
Member
on

1.  The instruments are only in the grandchildren - just where measurements/IO is happening. 

2.  This is what I am trying to figure out how to abstract more I guess.  The ppl is building a copy of the instrument classes into the grandchildrens' lvlibp.  However, instead of building it into the ppl, I would like it to point to an alternative location so that if I update an instrument driver, I don't have to recompile the grandchild packed library.

Current:  LVLIBP includes test code, instrument code

Desired:  LVLIBP includes test code, but points to the instrument code on disk rather than making it a part of the build.

Is this possible?

-Daniel
Technology Service Corporation

Jpina
Member Member
Member
on

Are you trying to use in your grandchildren a class that is in your parent? When you build your grandchildren you are creating a new context and this class is not the same anymore.

If I were you I would call my instruments in my grandchildren and only in there.

If you have instruments that you normally reuse create packages with the vipm and put them in tge vi.lib instead of placing them in your generic test library.

Does this help??

Jpina
Member Member
Member
on

Hi Daniel,

Sorry I did read to fast before. Ok I understand what you want to do. I don't know if you can get what you want, as I see it building a ppl is like building an executable, I guess that you could create a disk hierarchy when you make the build and take a copy of the instruments with the ppl.

This would take the source code of the instrument with the ppl, but as it happens with the lv exes, I imagine that if you try to call a vi that you weren't previously calling or if you try to use a vi from tge vi.lib that you weren't using before. You will probably need to recompile your ppl.

I belive that this will be more helpfull...

mike_nrao
Member
Member
on

Daniel,

I need a little more information:

Are the instrument drivers shared by multiple plugins?

Do you expect the instrument drivers to change often but keep the same API so calling code can remain static?

There are a few ways to solve this, but understanding the code structure and your motivation is key picking the right solution.

daniel.coons
Member
Member
on

The instrument drivers are shared by all of the plugins.  Each of the TEST grandchildren are the actual tests that call the instrument functions.

The instrument drivers could potentially change - the main cases would be with instrument obsolecense (where the API will be static, but the specific driver functions will change) or in the case of adding additional hardware to the bench for new UUTs.

-Daniel
Technology Service Corporation

mike_nrao
Member
Member
on

Sounds like the instrument drivers are prime candidates for another layer of abstration.  So, here's one option: Implement a 2nd layer of plug-ins.

PlugInLayers.png

Jpina
Member Member
Member
on

I only see a problem to what you say. If I am not wrong LVLIBP are LV version dependant, so if you modify your instrument driver using a newer version of LV then you will need to re compile all your test applications, won't you??

mike_nrao
Member
Member
on

Unfortunately, that is are correct.  Though cumbersome, it may be worth the effort in the long-run to create a maintain a single project that contains all plug-ins and build specs so you can just do a 'Build All' to generate lvlibps and a single installer to deploy them all.

daniel.coons
Member
Member
on

I was afraid that you might say that I would have to put the instruments into packed libraries also.  I was avoiding that because of packed libraries removing the block diagrams, but I suppose I could just constantly leave it as "debug enabled" during our development time.

I will play with that solution and respond with how it goes.  Thanks for the help! 

-Daniel
Technology Service Corporation

matt26
Member
Member
on

Hi,

I'm getting an error message when trying to make the overriding VI's for the plugins, error.bmp

Can someone point me in the right direction to cure this problem?

Thanks

mike_nrao
Member
Member
on

This is a LabVIEW bug.  You'll need to do a workaround to make your Override VI.  To do this, create a dynamic dispatch VI in the child class (plug-in), and give it the same name as the VI it will override; make sure the connector panes match. 

I'm guessing this is LabVIEW bug, because I don't see any reason why creating an Override VI should require the parent method's code, only it's front panel objects and connector pane.  It would be nice if NI would fix this, or at least catch the error and present a dialog stating "Can't auto-generate an Override VI from a method within a packed project library."

Jpina
Member Member
Member
on

Michael Lacasse wrote:

I'm guessing this is LabVIEW bug, because I don't see any reason why creating an Override VI should require the parent method's code, only it's front panel objects and connector pane.  It would be nice if NI would fix this, or at least catch the error and present a dialog stating "Can't auto-generate an Override VI from a method within a packed project library."


                   

I do agree on this, always that you don't try to change your compiled code but you try to create a "wraper" with extra funtionallity, it should be possible.

PaulLotz
Member
Member
on

If I understand the issue correctly from this discussion, this issue occurs, then, because the override template automatically calls the parent method.  (I argued a long time ago that this shouldn't be the default behavior.  It is simply one of the possible behaviors, and not the one that I use most often.)  If this is the case, it may be possible to modify the template (if it is just a simple template) to remove the call to parent method to avoid the error.  (I have modified the static template before--to remove the case structure and so on, but I don't recall modifying the dynamic/override template.)

Or maybe I am wrong and you should be able call the parent method even if it doesn't have its block diagram?  I guess you should be able to do that, too.  Well, it would be an interesting test to do....

BrianGShea@NGC
Member
Member
on

Are Comminuty Scoped VI's still buggy in Packed Libraries?

I've been working on a packed library that has an LVLib with some classes, the classes work just fine prior to packed library build. The classes are borken after the build with an explanation of cannot access community scoped VIs. The community methdos of the class are not accessed by any external VI of the packed library (99.9% sure) are not to be accesed outside of the LVLIB. I can send my packed library setup to someone for review, but would like to keep the contents restricted.

Thanks,

Brian G. Shea
Certified LabVIEW Architect
dsavir
Active Participant
Active Participant
on

Hi,

Is it possible to create a hierarchy of plug-ins?

For example I have the abstract plugin parent Plugin.lvlibp:Plugin.lvclass, A.lvlib:A.lvclass who is a child of Plugin, and B.lvlib:A.lvclass that is a child of A. I tried doing this and it worked OK a few times by putting B in a separate project with A.lvlibp (already packed). However, I did a small change (I'm not even sure what it was) and all the arrows became broken, and I couldn't get it to work again.

Also even when it worked it was inconvenient as I had to pack the first level of plugins and move to a different project altogether for the second. Trying to do this without packing A first resulted in an error when trying to load the plug-in in the main program ("this library has errors..."). Any ideas?

Thanks,

Danielle

"Wisdom comes from experience. Experience is often a result of lack of wisdom.”
― Terry Pratchett
Vijay_J
Member
Member
on

Hi Danielle,

Only thing i could suggest is to check for the relative path.

I guess, you have a three projects one for each layers. You are also building the Abstraction Layers in the order from Top to Bottom. I mean, building Parrent class as a PPL (Packed Project Library) and using the same PPL in child project to order inherit and create the child PPL library and then using the parent and child PPL in creating the Grand Child PPL library.

Ensure or Enforce the relative path using "Destinations" in build options.

PPL are like executable or DLL. When you build the child PPL, put the parent PPL in the appropriate relative path folder. When you build the grand child ensure to put both parent and child PPL is appropriate relative path.

Hope this helps.

Jpina
Member Member
Member
on

Hi Danielle,

I am not sure of having understood your question, but seems to me that you were in the right direction initally and then mixed up the concepts. You need to build your plug-ins first to be able to create your successive childs, bear in mind that the hierarchy is created on the compiled code, not on the source code of the packed library, and that is the reason why you may need different projects to create the "son" and the "grand-son " of the generic plug-in.

It is also due to the reason I underline that you need to build your plug-ins before create a child of them.

I hope this helps.

dsavir
Active Participant
Active Participant
on

Hi,

ViJay, I checked the path of the parent and grandparent lvlibp but not the rest. Maybe some other VI is the cause of the problem, thank you!

Jpina, I was hoping there would be a simpler way, but your explanation made clear that there isn't one. Thanks for the clarification!

Danielle

"Wisdom comes from experience. Experience is often a result of lack of wisdom.”
― Terry Pratchett
Jpina
Member Member
Member
on

dsavir wrote:


Also even when it worked it was inconvenient as I had to pack the first level of plugins and move to a different project altogether for the second. Trying to do this without packing A first resulted in an error when trying to load the plug-in in the main program ("this library has errors..."). Any ideas?

                  

Why did you need  to move the first plug-in? I suppose that the error in this case happened because LabVIEW works in terms of paths, so when you moved the first paked library on disk, you broke the first child, as the paths were probably different. I would recommend you to create your builds and keep them in one folder on disk and then add them to as many projects as you like but always accessing to the same packed library on disk.

matt26
Member
Member
on

Hi,

Thanks for the information, I've done the workaround and managed to get the driver to load, but I'm only getting one instance of the driver/ interface. The project I'm working on requires 4 of the same UUT driver. I've made one generic interface, and 2 drivers, but when tested with UUT - LabVIEW can only see one UUT. Have I overlooked something?

Thanks

Matt

K1200LT_rider
Member
Member
on

I've made an interface and plugin packed library set that work.  They are for a motion controller.  Now I'm trying to create another plugin for another motion controller.  However, I'm trying to figure out some of the rules/behaviors of classes and this plugin architecture by experimentation that aren't working out so well, and I've caused multiple LabVIEW 2013 crashes while only coding (where LabVIEW just instantly disappears with no error message or anything).

To start with, are the following statements true?

- Every method and accessor VI must be in both the Interface and Plugin classes.

- No code in the Interface method VIs actually executes in the main app since every accessor and method VI must be overridden in the plugins.

- Any class private data accessed by accessor VIs must be replicated in all Interface and Plugin classes.

- The class private data in the Interface and Plugins doesn't have to be totally identical, but might it be a good idea to use a typedef for all the private data in the Interface and then simply include that in the private data of all Plugins?  I'm having headaches keeping data structures sync'ed between the interfaces and plugins.  Maybe I'm going about this in completely the wrong way.

Now, back to trying to fix my Interface and new Plugin so LabVIEW doesn't crash.

Thanks,

Brad

Vijay_J
Member
Member
on

Hi Brad,

Let me attempt to answere your questions

- Every method and accessor VI must be in both the Interface and Plugin classes.

Ans: NO, need not be and it is against the abstraction. What ever a private data (parameter) created in Interface (Parent) the same will be available to all the Plugins(Childs) using the interface class public accessors (Shall be a static).

Example: we create Interface calss or abstraction classs when we identify group of class (Child or Plugin classes) has a common properties or prameters (Private Data) and uses common Methods.

- No code in the Interface method VIs actually executes in the main app since every accessor and method VI must be overridden in the plugins.

Ans: We can create a accessors to be static public method, need not be always a dynamic one, in which can use the public accessor method in all the child class as well as in application

- Any class private data accessed by accessor VIs must be replicated in all Interface and Plugin classes.

Ans: Need not be. Depends on the application need.

- The class private data in the Interface and Plugins doesn't have to be totally identical, but might it be a good idea to use a typedef for all the private data in the Interface and then simply include that in the private data of all Plugins?  I'm having headaches keeping data structures sync'ed between the interfaces and plugins.  Maybe I'm going about this in completely the wrong way.

Ans: Please refer to the answere to the first question.

K1200LT_rider
Member
Member
on

Vijay,

Thanks so much for your clarifications.  I don't have much experience with classes in LabVIEW, but I understand the concepts of OOP and have some experience with it in another language.  For some reason I got the idea that for the plugin architecture (using packed libraries), all the standard OOP features wouldn't be available.  Based on your answers, I should now be able to fix up my Interface and Plugin classes properly to get rid of my errors.

Thank you for your time.

- Brad

K1200LT_rider
Member
Member
on

http://forums.ni.com/t5/LabVIEW/packed-library-bug/m-p/1741254/highlight/true#M610099

Vijay,

I just found this. Could this PPL inheritance problem be a problem for the plugin architecture?

- Brad

Vijay_J
Member
Member
on

Brad,

I have been able to create and use the plugin drivers as described in this article. Did not find any major issues as such.

PPL can have both override (Dynamic Dispatch) methods as well as static methods. Only Public scope methods or VI's will be visible after building PPL. It is something to do with the Access Scope settings in Library.

Regards,

Vijay.

shew82
Member
Member
on

That inheritance issue comes about if the parent class is not compiled to its own PPL. In that thread's issue, the parent class was "swallowed" (and had it's namespace changed in the process). If the parent class is in its own PPL, then neither of those instances happen.

In the end however, the child PPL will only have files for it's own class - you'll need to use the parent PPL to use the parent VIs in calling applications. Sometimes, when creating intermediate layers, etc I've added simple override for parent VIs (with the only code in those VIs being Call Parent) just to make the VIs easily accessible form a user of the child PPL.

Regards,

Shaun

Mattias_L
Member
Member
on

Has someone been successful on trying this on a real-time system?

I tried to make it work on a sbRIO but when I'm trying to load the plugin the Get Exported File Path.vi throws error 6 at me. If I connect a Path Constant directly to the Get LV Class Default Value.vi, the Get LV Class Default Value.vi throws error 1124 at me.

Any solutions?

dsavir
Active Participant
Active Participant
on

If you are encountering LabView crashes when working on uncompiled plugins:

I had a project for the abstract parent plugin, and a separate project for all the children plugins using the lvilbp of the parent. This worked fine for four plugins, but when I created the fifth plugin I got LabView crashes (LV2011) after running test code of the plugins (uncompiled). Compiled code didn't crash, but it was driving me nuts!

Moving the fifth plugin into its own project solved this completely. It wasn't using anything that wasn't already shared between the previous four, it didn't use any dlls. Its only difference was that it used VISA (the rest used dlls). I'm not sure why it caused crashes, but in any case I will have separate projects for each child plugin in the future.

Thanks,

Danielle

"Wisdom comes from experience. Experience is often a result of lack of wisdom.”
― Terry Pratchett
Jpina
Member Member
Member
on

Hi Danielle,

Reading your problem I couldnt tell what is happening but it sounds to me as if you were trying to use in you 5th plug in something that is already in memory and that causes the crash.

Keep in mind that this architecure uses the factory pattern and that LabVIEW cannot kill the clashes that you load, so the reamin in memory (there is a VI shot chapter about this) and that LabVIEW keeps its items in memory till you close all. It my happen that you are having a conflict fo this reason...

I pass you this link where Elijah talks a bit more about this architecture and gives some handy tips.

http://ekerry.wordpress.com/2013/12/07/the-nuances-of-deploying-plugins-in-labview/#comments

dsavir
Active Participant
Active Participant
on

Hi,

Jpina wrote:


                       

dsavir wrote:


Also even when it worked it was inconvenient as I had to pack the first level of plugins and move to a different project altogether for the second. Trying to do this without packing A first resulted in an error when trying to load the plug-in in the main program ("this library has errors..."). Any ideas?

                  

Why did you need  to move the first plug-in? I suppose that the error in this case happened because LabVIEW works in terms of paths, so when you moved the first paked library on disk, you broke the first child, as the paths were probably different. I would recommend you to create your builds and keep them in one folder on disk and then add them to as many projects as you like but always accessing to the same packed library on disk.


                   

I didn't move the plugin. I meant that I had to use a different project, so I had project1 with the parent, project2 with the children (and hte parent plugin as lvlibp), project3 with the grandchildren (with the children as lvlibps) etc.

Thanks!

"Wisdom comes from experience. Experience is often a result of lack of wisdom.”
― Terry Pratchett
Contributors