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.
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.
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.
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:
I made a minor but important update to the code and presentation file today (November 2nd 2011).
It now uses the 'Get Exported File Path" function available in the tools pallette under 'File I/O>>Advanced File Functions>>Packed Library' to get the path to the plugin class within the lvlibp.
This will now work even if the plugin has dependencies scattered in various locations on disk. Previously it would output error code 7.
Michael:
Thank you for writing this up. Your step-by-step instructions are really good.
As hard as this sequence of steps is, even harder is the steps needed to build the plugin architecture when the code is already written without that in mind -- i.e., there's one project with the application, a parent class that isn't in its own library, and a bunch of already written child classes that are successfully doing dynamic loading. Making the conversion at that point is more complicated -- something I've tried to do a time or two and always botched. It's really important to have the end design in mind when writing the original code. It is my hope that in the years to come LV gets some better tools for making that transition (that really is a hope, not a backward way of saying anything like that is in the pipeliine; I don't know the roadmap for the packed project library features).
Especially high kudos for documenting so well the "get the class path" portion!
> Step 11 : Create as many plug-ins as you need for now, giving each a new project and built lvlibp.
You don't actually need a separate project for each plugin class. You just need each plugin class to be in a separate .lvlib and to have a different build spec for each one to build independent .lvlibp files. I would in fact recommend using a single project since that makes it easy to pop up on "Build Specifications" and select "Build All" to have them all get created in one go.
> Step 17: Create an installer for the application.
One question that I've been asked in the past is whether there is a way to have the PluginInterface.lvclass be inside the .exe instead of being in a separate .lvlibp. This would make it so there is only one file for the app (the exe) and one file for each plugin (each a .lvlibp). To the best of my knowledge, there is no way to reach this nirvana. Without breaking PluginInterface.lvclass out into its own packed library, each of the child plugins will include a copy of its parent class inside of itself (which has its own set of serious problem issues since the parent class isn't public).
Again, thank you for writing this up.
Michael,
It seems like you are jumping through a lot of strange hoops that could be more easily accomplished if 'Packed Classes' were a feature of the LV language.
If that conceptual idea makes sense to you, do you agree?
~Norm
Michael,
This is excellent! I'll have a few more comments and questions later. For starters, thanks for mentioning the issues with 2010 in Step 9. I experienced the same. And thanks for the highlight of the new VI in Step 14.
Norm,
I like it! I'm assuming Protected VIs would be visible as well as Public VIs. And even though the class data is private, it would be visible as well.
Norm,
That is a great idea - I have several packed libraries where the only content to the lvlib is a single class - the lvlib is only there because I cant pack a class on its own (despite a lvclass being related to a lvlib)
Hi Michael,
Thanks for your wonderful presentation and example code.
I was trying your method to create the packed project library based plugin architecture and faced with following error.
While creating Plugin Overide VI LabVIEW 2011 generates following error:
Error 1035 occurred at Property Node (arg 1) in Apply Configuration For Source Separation.vi->MemberVICreation.lvlib:CLSUIP_Apply New VI Settings.vi->MemberVICreation.lvlib:VIRetooler.lvclass:CLSUIP_CreateNewVI.vi->MemberVICreation.lvlib:VIRetooler.lvclass:CLSUIP_RetoolVI.vi->MemberVICreation.lvlib:CLSUIP_CreateOverride.vi->MemberVICreation.lvlib:CLSUIP_CreateOverride.vi.ProxyCaller.
Steps follwed: 1. Created interface class and able to build the Interface packed project library. 2. Added the Interface Packed project library in the Plugin project. 3. Created plugin child class. 3. Able to inherit from Interface class present in packed project library. 4. While creating VI for override labview throughs above error.
Kindly help me...
Ruska..., I developed the code in LV2011 and I never ran into the issue you mention above. If you have the service program with NI, you might want to call them or shoot them an email. It could be something wrong with your LabVIEW installation. It sounds like you're following the steps to the letter, so I'm not sure how to help... sorry. One idea: Make sure the project created in step one (interface class) is closed before adding the built packed project library to the plugin project. Perhaps even close and reopen LabVIEW after building it, just to make sure it's no longer in memory. Please let me know if you resolve this, a revision to the presentation or example code may be in order if this turns out to be a common issue.
I'm having hte same problem with the 1035 error. Ideas?
It appears that as soon as I attempt to create the Override VI, error 1035 occurs and then the method I was trying to override in the base class (within the packed project library) shows a broken run arrow.
Here's the workaround: Create a VI for Dynamic Dispatch then turn it into the desired override VI by matching the connector pane and saving it with the same name as the base class method. Obviously there's a LabVIEW bug here that needs to be fixed by NI.
The error code is actually different (and perhaps more insightful) in LabVIEW 2010 :
------------------------------------------------------------------------------------------------------
Error 1012 occurred at Property Node (arg 1) in CLSUIP_ClearBlockDiagram.vi->MemberVICreation.lvlib:VIRetooler.lvclass:CLSUIP_RetoolVI.vi->MemberVICreation.lvlib:CLSUIP_CreateOverride.vi->MemberVICreation.lvlib:CLSUIP_CreateOverride.vi.ProxyCaller
Possible reason(s):
LabVIEW: Cannot load block diagram.
Property Name: Block Diagram
The block diagram for NULL could not be loaded but is required by this property or method.
--------------------------------------------------------------------------------------------------
Of course it can't load the block diagram, it has been removed because the VI was built into a packed project library. My question for NI: why does auto-creating an override VI require the parent block diagram?
I suppose when I made this demo and presentation, I didn't use 'New>>Override VI'. I must have done the workaround I described above. Good catch guys, and I hope this post has enough visibility that NI gets on it ASAP for bug fix in LabVIEW 2012.
I was able to make the method you described work. Thanks for describing how you created the override VIs; I wouldn't have thought that the New-> override VI would have caused a problem like that.
Hello Everyone,
I reproduced the error described and filed CAR 334314 for further investigation.
Thanks for the work on this!
Hi Michael,
Great document.
You mentioned that step 14 now uses 'Get Exported File Path.vi', but I'm runnig an older version of LabVIEW. What was the code before this VI was there?
Matt, it was messy, it looked like this:
Getting the path correct can be a little tricky. If you're not sure, temporarily add a class method that displays its own VI path, then run it from the dev. environment after building it in a packed project library. This'll show you what the path is to that method within the lvlibp, then you'll know how to correctly form the path to load that plugin in your client application.
This seems like a great leap forward compared to source distributions. My concern is that future changes in LabVIEW will break this capability. Can anyone from NI comment on if this approach will be supported in future versions?
Yes, it can be compared with source distribution, but the only feature which is really missing (and very useful for PlugIn architecture) is Exclude.
Feel differences:
Source Distribution:
and Packed Library:
As result we will get "VIs overhead" in PlugIns...
The other thing that I miss with the packed library approach (as opposed to a LLB based approach that I used to use) is that I cannot use custom extensions.
I otherwords, if I distribute plugins as LLB (and manually face the dependancy pains / lack of namspacing etc) I can build a LLB with a custom estension (e.g. *.psu for power supply unit drivers etc) that can (a) make it easy to find plugins, and (b) I can then add the built files to my project that builds the installer for the main APP and wont have to deal with conflicts etc.
i don't believe that we'll get an exclude for packed libraries. they are modeled after dlls which, by definition, are a self-contained set of files with no external dependencies.
There is bloat to the lvlibp that in some circumstances may be unnecessary. But, the problem with loading source distributions as plugins is that if any of their subVIs are already loaded in memory in the application from a different path than the plugin expects, then they are broken and won't load. This is the 'dependancy pains' shew82 mentioned above. The way around this with source distributions is to check the box in the build spec to 'Apply prefix to all contained items', that way they're guaranteed unique.
Besides this trivial point, the reason I'm not particularly fond of source distributions as plugins, is that they appear as editable LabVIEW files on disk; sometimes many,many files. I feel uneasy having all this stuff exposed to my end user. I've experimented with zipping them and changing the file extension (.plugin), then having my application unzip them during runtime and delete extracted files when shutting down, but this is quite a hassle and prone to error. Packed Project Libraries give a much cleaner look (on disk) to deployed applications, in my opinion.
Just for completeness - in the past, I used LLBs with custom extensions as that means that your end user has to not only have an appropriate install of LabVIEW but also has to actually know that the internal data format of the file in question is a LLB. That certainly reduces some of the risk of source distributions (removing block diagrams etc as part of the build process takes this further still).
Hello LVB,
Right now this is "the" way to create class plugins. We do not intend on breaking this functionality. If something does change to break this it would be considered a bug and we would fix it. We have brainstormed about ways to make this process better for our users. More info is here.
Can this architecture be used to share a single packed project library (shared resource) with multiple LabVIEW applications (EXE)?
JonS, thanks for the prompt confirmation this functionality will remain in the future. This is great news.
Certainly, multiple applications can load plugins from a shared directory. Each application will load its own copy of it in memory. Be aware that if your plugin performs file access or hardware I/O that this scheme doesn't prevent conflicts of those resources, you'd have to work around that the same way you would otherwise with multiple apps (whatever that may be).
What if the intention is to share resources (file handle, hardware IO, ...) across multiple apps? Is there a way to configure the packed project libraries for this operation?
Sharing handles between processes is usually not an option. Almost all the refnums in LabVIEW are application reference (aka project specific) and other handle like objects like most Windows handles are only meaningful on a per process base. Some Windows kernel objects are system wide, but most higher level Windows APIs don't use them or don't expose them.
I love your plugin architecture.
I think that the way you separated it into several projects is the correct way or else I won't be able to compile some of my plugins because LV will say they are busy.
The interface and the plugins shouldn't be located in the same project.
However, I run into some problems implementing your design.
When I run your exe from the example both on my local machine that has LV and on a remote machine that doesn't have LV your exe works just fine.
Yet, when I tried to do the same with my own plugin which is much more complex and uses many vis from other projects my exe run perfectly on my local machine with LV yet the exe failed to run on a machine that had no LV pre-installed (yet it contained the correct runtime) and generated 1003 error.
Basically, what I did is:
1. placed my plugin class inside a lvlib
2. converted the lvlib to lvlibp
3. set the plugin's lvlibp to inherit from the interface lvlibp
4. added the interface lvlibp to the main program
5. searched from the main program's exe the plugin lvlibp as you described
At first, I thought my problem was a static vi reference in the plugin since the static vi is actually a vi from the lvlib and not from the lvlibp.
Thus, I changed my model to search and return the vi from the lvlibp (through a similar search) instead of using a static vi ref that didn't get updated when I converted to lvlibp.
Alas, it didn't help.
Oh, i had another problem with the design: if my main application is using a vi from the interface I must use the vis from the lvlibp or else I'll have to change all the vis once I'm done since the plugins inherit from the lvlibp interface and won't recognize the lvlib interface as valid casting option.
Won't it be better if the interface itself remain as lvlib with the plugins inheriting from it instead of from the lvlibp interface?
P.S. - at the moment I dealing with another question: how do I use functional globals or networked shared variabled with lvlibps. Will the queue be empty clones of the originals?
The reason for the packed libraries is so executables can load them. You only need the run-time engine at that point rather the development environment. This means I can hand a customer a professional looking disk with installer and they have no idea they are running LabVIEW code.
I know what the plugins are for, I just tried to test the exe and it failed on the customer side after it passed on mine.
As a matter of fact there are many more benefits to the plugin architecture with one of them is using less memory and load time resources along a greater separation in the code development.
After some more investigation it appears that a HW driver that I'm using is causing the problem.
Even though I can see that the LVLibP added the dll that a vi from the Hardware Abstraction Layer Project in it uses, once the exe searches for the dll vis it fails with 1003 error.
Once I remove the vi from the HAL the exe works just fine.
P.S. - since it is hard to debug a compiled code from LVLibP, for example, especially since LV crashes a lot when I open debug mode, I started working with NTTrace.
However, just a few seconds result in 10MB of logged events. Is there an easier solution?
Great article and comments!
How would one go about implementing this on an application that has, say, 1000 plugins? Prefereably without spending 5 years on it?
My application - which has those said 1000 plugins - works in the dev environment rather well. I created the classes and established the hierarchy using VIScripting to avoid taking 5 years. Would it be possible (or sane?) to attempt to put together all these libraries and build specs using VIScripting?
Hi,
Update to the exe problem using LVLibP:
After a more thorough investigation the issue was resolved.
There was no problem on the LVLibP side (which works now amazingly and I adore it!).
The problem was a bug in the dll provided to us by Data Translation.
Again, LVLibP works great! I tested every aspect and I use it in a very complicated scenario.
Thanks!
It sounds like your application is better suited for a source distribution and dynamic VI loading. I tend to think of the packed project library as a "DLL for LabVIEW". The packed project library should contain a "set" of VIs that are used together. Given this, do you know of any commercial applications that use 1000 DLL's in the program files folder?
I currently wrap my plugins in an LLB using the OpenG Builder. I do this because I can remove source code and namespace them on the fly at the time I build the LLB. This protects me from crosslinking issue between one LLB of plugins and another LLB of plugins or the EXE itself.
I am considering switching in PPLs but I am concerned about a few things after reviewing your presentation and the comments.
1. Is there a way to group multiple plugins into a single PPL? I currently do this so that I have one LLB containing common plugins and then each dev can create their own custom LLB of their plugins. The EXE can call a plugin out of any of those LLB and be sure that there will not be a conflict. The advantage to me is less files to install and therefore a cleaner footprint on disk. To me, this is more like a DLL since I can have multiple functions within my 'dll' of plugins. Your solution looks more like a lot of DLLs with only one exported function per DLL.
2. I have some shared memory between my plugins and my EXE. This is in the form of a SEQ but could be any other functional global type object. I exclude the VI that wraps this from the plugin LLB so that all plugins are forced to use the one in memory that the EXE provides. Is there a way to achieve something similar with your system without the ability to exclude VIs from the PPL build?
3. How do you handle versioning of your plugins? I need the EXE to log the version of the LLB that a plugin was loaded from. I do this by having a VI in the LLB that includes the version string as a static output. This is incremented in the LLB build process. I would want to have a similar method of storing, auto-updating and reading a version string for each plugin PPL.
4. When I am developing my EXE and plugins, I am able to test the EXE using the source code version of the plugins or the LLB ‘compiled’ version. (when running the EXE, I only can target the compiled version, since I would normally only have the RTE installed for the EXE). It appeared to me that you solution requires the source for the EXE to calls PPL versions of the plugins only and not source. Correct me if I misunderstood. If I am correct, it would seem that editing and debugging your plugins would be difficult as you would have to rebuild and test each time and not have access to traditional debugging tools.
Thanks for any answers you can provide.
-John
John,
Here is my response to the concers you raise:
1. In the example, I only put one method in each PPL for the sake of simplicity. You could put as many methods in there as you'd like; as long as they're defined in the interface class then the main application can call them.
2. Yikes... functional global variables tend to muck up my architecture. I prefer to pass data on a wire if at all possible, or though a tightly managed communications mechanism. If I wanted to share stuff between an application and plugin or from plug-in to plug-in I would create a method in the interface and in the plugins that takes a queue reference (or object reference, or user event reference, etc) as an input, then store that in the plug-in's private class data.
3. You're asking me to give away my secrets! Here's a couple tricks to get version info without having to write your own method in each plugin or do any pre-build actions to update it:
option 1: Load the version of the class within the lvlibp. Pick a method within your plugin class, get the path to it within the lvlibp, open it using OpenVIReference, then use VI Server to get it's containing class ref and the version number of that class.
option 2: No guarantee this will work forever; NI could change this without notice. The lvlibp version is buried within (near the end) of the lvlibp file as text. So it's possible to read the lvlibp as if it's a txt file, then do string parsing to extract its version.
4. The example application does indeed load the built packed project libraries, but it doesn't have to. It could just as easily point to the classes (source code) on disk rather than look for the classes withing the PPLs. I wouldn't use this plugin scheme if it wasn't easy to debug in development environment.
I think LLBs are a fine alternative to PPLs for doing plugins, but I've tried this approach before and banged my head against the wall a lot trying to get build specs correct to prefix files and extract common code. Of course then, I didn't have a straightforward approach using interface classes... so perhaps it's worth revisiting.
Thanks for the comments.
Thanks for the response. I have a few followup questions:
1. regarding packaging multiple plugins in the same PPL, you mention that you could have multiple methods in your plugin class? I am wondering if instead you chould have mupliple classes (all of type plugin) withing the LVLIB thayou then use to create the PPL? My 'plugins' are actually test modules called by my test executive. I would like to have each module be a child of the plugin class but I still want to group several of them into the same PPL.
2. see your point and that is basically how I have done things except in my case this tech datees back quite a ways and uses a variant tree to store system wide data. I could implement this as a named queue or pass the Q ref to the plugin as well. Many optinos here to work around the exclude limitation.
3. In your example you show how to get the version of the class within the PPL. (I think that is what you are doing). Does that get bumped automatically everytime you build the PPL? If not, then option 2 seems like the only way to do what I want. (assuming that version is incremented on build). I have used pre build actions in the past to version web services. That might be the best way to do this for now. I hope that they expose a proper interface to the version information in PPLs in the future.
4. Good to know. I need to give this process a try.
thanks again,
-John
John,
1. Cool idea. A large application might benefit from a grouping strategy like that. It could better organize plugins plus you'd save some build/deploy time by doing them in batches. The main app would just need slightly more complex code for finding & loading the plugins since multiple plugins could be in one lvlibp, but not necessarily every class in the lvlibp is a valid plugin (some could be helper classes).
3. Good point; dependencies may change that don't affect the plugin class version, but a rebuild would increment the lvlibp version. It would indeed be helpful if the PPL palette included a 'Get Version' function!
Best of luck! If you do refactor your test executive or use these ideas in future projects, please post back here to let the community know how it turned out!
Good point LVB...I was able to reduce the number of plugins down to around 15 and still cover our entire product line.
Question Michael:
I got this method to work really well for a class hierarchy that is one level deep, but trying to add another level down generated errors which prevented compilation of the packed library. Here's what I did:
1. Build the interface class and packed library.
2. Build plugin class in new project, inheriting from interface class packed library.
3. Compile plugin class packed library and delete additional copy of the interface class library.
- Everything works great up to here.
4. Start a new project and add the plugin class packed library.
5. Create a class which inherits from the plugin class packed library.
6. Can't compile the 2nd tier packed library because the interface class library is not found.
My application is more or less an options dialog, so each plugin nests a new display into a common front panel (using a strategy similar to http://forums.ni.com/t5/LabVIEW/An-experiment-in-creating-compositable-user-interfaces-for/td-p/1262...). As such the code to find and load plugins is contained in each plugin UI, which sends the qualified name back to the main app.
Actually I think this sounds rather similar to what jlokanis is doing. Do you have any ideas for extending this framework to multiple levels of hierarchy?
Thanks,
Scott
Scott, I can envision use cases for overriding or extending plug-in functionality by adding another layer to the hierarchy. I'll take some time soon to experiment with this idea.
If you liked the composite UI idea you linked above, be sure to check out the "more successful" experiment here.
Michael, this looks fantastic thank you. I was wondering if this works where the plugin is an actor according to the Actor Framework? I will try and figure it out and post back myself, just wondering if is something that has been done already.
AristosQueue wrote:
As hard as this sequence of steps is, even harder is the steps needed to build the plugin architecture when the code is already written without that in mind -- i.e., there's one project with the application, a parent class that isn't in its own library, and a bunch of already written child classes that are successfully doing dynamic loading. Making the conversion at that point is more complicated -- something I've tried to do a time or two and always botched. It's really important to have the end design in mind when writing the original code. It is my hope that in the years to come LV gets some better tools for making that transition (that really is a hope, not a backward way of saying anything like that is in the pipeliine; I don't know the roadmap for the packed project library features).
AristosQueue,
What you describe above is exactly the situation that I am in and would appreciate any suggestions/advice. How would you suggest going about this transition (especially since you said that in the past you have seen lots of difficulties with this process)?
Right now, we have a newly developed plugin architecture and we did not anticipate these types of challenges with creating successful EXE builds, thus we did not plan for this. However, at the moment, this seems to be about the best option for creating EXE’s that allow for: 1) leveraging the power of the plugin architecture and 2) keeping the build file structure clean and we would like to once again start creating EXE builds.
As you mentioned, I do hope better tools come out soon for managing this, please keep us posted if anything pops up on the horizon.
Amiri
Will this work on the CRIO?
Is it possible to unload a packed project library?
I am having difficulties to update a plugin (copying a new version of the packed library and loading it).
If I am not mistaken if the PPL is loaded once, he will keep this version in memory and work with that one, even if a new one has overwritten the old one.
You can't control when LV unloads anything else from memory; I'd be surprised to learn that PPLs are different. Maybe if you only reference the PPL's contents in a dynamically launched VI that you can stop and restart?
Hi,
I am trying to implement the plugin-architecture using the ppl at run-time. Therefore I have my main-class from which all other classes inherits. Dynamic dispatch has been done and all is ok without ppl. The "To More Specific Class" throws the error "1448". What is the reason why I can't cast my object down to a child element at run-time? Hints?
Best Regards,
Joachim
Hi Joachim,
One possible reason could be the "Dependancy" issue. Do check if you are trying to load the plugin's in source code form (which has the dependancies in LabVIEW vi.lib or instr.lib folder) from the application (in executable form).
In this case, the apllication (executable) does not have the ability to search and load the plugin's (in Source Code from) which has the Dependancies (Sub VI's or Driver VI's used in the Plugin) as against to the LabVIEW IDE.
Hope this helps...
Best regards,
Vijay.
Vijay,
Thanks for your hint. Unfortunately, also after a build I get the same error...
Hi Joachim,
I was trying to recreate your scenario. The error with respect to the dependency issue is 1498. Error 1448, in your case it seems to be "Something to do with the inheritance".
The child class will have the copy of parent data type along with it, when inherited from the parent class. When the plugin (child) or object loaded from the desk and passed to "To More Specific Class.vi" checks for the "Target Class" data Type to the "Reference" data type. I guess there is a miss match in your case.
"Application as well as Plugin must have to use the Interface(Parent Class, *.lvlibp)"
I will give a try to reproduce the error 1448. Can you explain the step by step procedure used in your case?
Best regards,
Vijay.
Vijay,
thanks for your hint - now it works - I've separated the plugin code from the using code into two projects - now it works, because I am only using the classes from the ppl.
Best Regards,
Joachim
Hi Vijay,
one problem still remains: everything is working perfect when using the ppl-Classes with all dependencies via the development environment. When I build an application I get an addition ppl in the data-folder (the "master" ppl is on a network share) and the call of the lv-classes gives me the error 7 --> Get LV Class Default Value.vi<APPEND>.
What is going wrong here?
Best Regards,
Joachim