The Lua for LabVIEW toolkit can be used for scripting LabVIEW and for creating scriptable LabVIEW applications. The toolkit embeds the simple yet powerful Lua scripting language into LabVIEW. It allows Lua scripts to be run inside the LabVIEW runtime. Custom LabVIEW-implemented functions can be called from Lua so that there are no limits to what can be scripted. Lua for LabVIEW also provides tools for architecting modular LabVIEW applications with improved error handling.
Lua for LabVIEW is copyright
© 2003-2016 CIT Engineering Netherlands BV.
Current releases can be downloaded here. Commercial use requires a license. Lua for LabVIEW was implemented and documented by Albert-Jan Brouwer and Rolf Kalbermatter.
Lua 5.1 is an open sourced scripting language designed for embedding and extension. The language is copyright © 2003-2011 Tecgraf, PUC-Rio and is licensed under the terms of the MIT license.
Lua for LabVIEW requires LabVIEW 7.1.1 or higher for Linux, Mac OS X, or Windows.
To install Lua for LabVIEW, after having downloaded the installer package, you should use the VI Package Manager that can be downloaded from JKI Inc. The package is an OpenG package that the VI Package Manager will install in the desired LabVIEW installation. If VI Package Manager is not an option to use, you can try to download the old OpenG Commander and install it into your LabVIEW installation but this is an unsupported method, and doing so is at your own risk. It is important to restart LabVIEW after the installation of Lua for LabVIEW in order to get the project menus and function palettes refreshed.
Now Lua for LabVIEW should be ready for use. To verify that this is in fact the case, start "Unit Tester.." from the "Tools->Lua for LabVIEW" menu and let it run all tests. All tests should pass. If not, please file a bug report.
When upgrading from a previous release there are certain things you need to be aware of that could require some modifications to your existing application.
Lua for LabVIEW 2.0 uses Lua 5.1 as scripting engine, whereas Lua for LabVIEW 1.2.x and earlier used Lua 5.0. Accordingly all the syntax changes of Lua 5.1 apply also to Lua for LabVIEW 2.0.
Lua for LabVIEW 2.0 uses a new refnum control for its Lua for LabVIEW State in the LabVIEW diagram. Code that used the Lua for LabVIEW State typedef control, should port seemlessly, unless you disconnected the typedef for some reasons in your own code and used your own copy of this refnum.
Another point of concern (at least in older LabVIEW versions, are strict typedefed VI refnums for VIs that use directly or indirectly the Lua for LabVIEW State control in its connector pane. For some reasons LabVIEW does not update this VI refnum and maintains the old definition, despite the fact that the old typedef has been replaced with a new one. The same applies actually every time you change any control in a connector pane to a VI you want to call through a strict typedef. So when your application then tries to dynamically call this VI with the Call by Reference node, the type definition in the strict VI refnum does not match the new typedef of the actual VI connector pane in memory and you receive an according error from the Open VI Reference Node. The solution is to relink every strict VI reference to a properly recompiled VI
Lua for LabVIEW States were never autodisposed in the same way as other LabVIEW refnums. This means that a Lua for LabVIEW State stays open until it is closed explicitly, a named state is reopened with the same name identifier, or the LabVIEW system is closed. This can lead to zombie states that remain open and use up memory, if the calling program doesn't properly close a state because of a programming error. A new optional boolean input to the "LuaVIEW Open.vi" and lower level "LuaVIEW Open Base State.vi" and "LuaVIEW Open Private State.vi" functions allows to enable the state to autodispose as soon as the VI hierarchy that called that function returns to idle state, just like LabVIEW does with other refnums.
Lua has all the typical features you might expect of a text-based language: a means of
executing code conditionally (
looping constructs (
while, repeat-until), named variables, functions,
and the ability to handle primitive
types and compound data. To be suitable for
scripting by relatively novice users, the language was specifically designed so
that its basic features are simple, scripts are readable, and advanced features
remain hidden until required.
To quickly try out a script, load the "Task Manager" user interface VI located in the Tools->Lua for LabVIEW menu and run it. The "quick script" tab together with the "insert new" and "Run Quick" buttons allow you to write and run a small script such as
The first time you run a script there is a noticeable delay. This is caused by the loading of function VIs. Once loaded, they remain in memory. Use the "Function Manager" user interface VI in the Tools->Lua for LabVIEW menu to examine the loaded function VIs.
Lua has additional features that give it the conciseness and expressiveness expected of a modern scripting language. The official language book "Programming In Lua" provides an excellent introduction to the language and also covers useful techniques and advanced features. A reference manual, a HTML version of the book, and other documentation is available online. The tutorial offers a hands-on introduction. The short reference can be printed onto a couple of pages that serve as a handy reminder while programming. The parts of the Lua documentation that are concerned with the embedding and extension C API can be skipped because Lua for LabVIEW handles those details for you.
Though scripts can be modified using a LabVIEW string control, the use of a good text editor is recommended. There exist text editors with Lua-specific syntax highlighting, and even some integrated development environments (IDEs). See the Lua add-ons page for details. The typical use of Lua for LabVIEW will involve smallish scripts for which the SciTE text editor offers a good compromise between features and ease of deployment: it does Lua syntax highlighting and code folding, deploys as a single executable, and is available for both Linux and Windows. As a convenience, have the file association for the ".lua" file extension point to the editor you choose.
When you are not yet familiar with scripting, continue by reading the next few sections for a gradual introduction. When you are already familiar with scripting, you are probably wondering how your LabVIEW code can be called from Lua. This is done by creating Lua-callable LabVIEW functions as detailed in this appendix. Function VIs must be registered before scripts can call them.
Various examples are located in the examples/Lua for LabVIEW directory. They demonstrate how Lua and LabVIEW can be made to interoperate, and how to use some of the Lua for LabVIEW specific function VIs and C functions. The VIs in the examples directory can be loaded and run. The scripts (with a ".lua" extension) can be started using the "Run Local" button of the task manager. Beware that some of the examples demonstrate advanced features that make little sense without first having read the corresponding manual section. For an example of application design have a look at the client-server framework.
Lua for LabVIEW is intended for LabVIEW programmers in need of a scripting language. Programming languages are Turing-complete so that there is, in principle, nothing you cannot use Lua for. In practice, languages have particular strengths and weaknesses. Scripting languages are designed to minimize the time required to write or modify a program at the expense of speed of execution. Since programmer time is much more valuable than CPU time, this tends to be a worthwhile trade-off.
However, if all you need is a scripting language, there are plenty of other options. Lua for LabVIEW makes sense only when you need a scripting language that works well with LabVIEW, that is, when you are working on some LabVIEW-based project or prefer the LabVIEW development environment over alternatives when programming. Since this implies that you are comfortable with programming in LabVIEW, it is clear that Lua should not be used for anything that is easy to implement in LabVIEW. Specifically, LabVIEW makes creating graphical user interfaces trivial, is good at high-throughput data acquisition and analysis, and allows for easy interfacing to instrumentation on account of the many available drivers. This leaves plenty of scope for scripting, as is best illustrated with some "use cases":
Making an application scriptable involves exposing a subset of application functionality as Lua-callable functions so that the users of that application can write scripts that customize or drive the application, even when it is deployed as a built executable. Given the typical test and measurement use of LabVIEW, the scripting of test sequences is a likely example. Since Lua is well suited for data definition by virtue of its table syntax, using Lua for script-based configuration is another option. This type of use, which is further detailed in the next section, requires only shallow modifications to an application.
There is a strategy called "deep scripting" that involves chopping up an application into small building blocks that are glued together using scripts. See the client-server framework for an example of how to glue together an application. Defining the application architecture and initialisation through glue scripts simplifies architectural revisions, allows for bottom-up design, and provides better chances for the reuse of building blocks between projects. The toolkit predefines a handful of types of mostly LabVIEW-implemented building block that cover the most common application design requirements. VI templates for easy creation of these building blocks as well as examples of Lua glue scripts are included. The most useful type of building block is the Lua for LabVIEW function: a VI that binds some LabVIEW functionality for calling from Lua.
Since Lua scripts are interpreted (compiled to byte code and executed on a virtual machine) at run time, there are opportunities for making an application behave more dynamically. The Lua for LabVIEW toolkit makes use of both Lua and LabVIEW capabilities to provide methods for loading code on demand, late binding of functions, overriding functions, instantiation, and reloading/replacing/reprogramming functionality at run time.
Error handling for non-trivial LabVIEW programs is difficult since LabVIEW has no built-in exception mechanism. This makes it hard to separate error handling from error sources. By providing a simple exception mechanism to LabVIEW code that is run or called from scripts, the toolkit can markedly ease the task of implementing proper error handling.
During development there often are repetitive chores that could use some automation. Chores that are easy to handle with scripts include unit/regression testing of the software under development, and file management (shell scripting), e.g. to prepare for a build or prepare a distribution. See for example the unit tests that Lua for LabVIEW uses to test itself.
Lua can also serve for tasks other than scripting. Its conciseness makes it suited for prototyping new algorithms. Occasionally, there is need for an existing algorithm or data structure of which no implementation is included in the LabVIEW distribution. If performance is of little importance, using Lua is an easy solution since the vast majority of algorithms and data structures described in the literature translate readily into Lua. A dataflow implementation, on the other hand, will typically require a redesign.
Lua is a language that is complementary in nature to LabVIEW. LabVIEW (G) is a graphical dataflow language, Lua is a text-based language that supports imperative, functional, and object oriented programming styles. LabVIEW is compiled at edit or build time to machine code, Lua is compiled at run time to virtual machine byte code. LabVIEW is strictly typed, Lua is dynamically typed. The features of the Lua for LabVIEW toolkit are aimed at resolving "impedance mismatches" between both languages. The close integration of Lua with LabVIEW thus established allows a viable choice between Lua, LabVIEW, or a mix of both, when solving particular problems.
Executive summary: when you are married to LabVIEW, you can have Lua as your mistress without risking divorce.
To prevent the large feature set from being an obstacle, a guiding principle in implementing Lua for LabVIEW has been to provide convenient or automatic mechanisms for the most common types of use while providing additional tools that allow for exceptional use cases to be dealt with. To minimize confusion, the most common usage scenario, that of making an application scriptable by the end users of the application, will be outlined first.
A LabVIEW application can be made scriptable by exposing a subset of its functionality to Lua through custom LabVIEW-side functions that are callable from Lua. This allows users to automate repetitive application tasks without having to modify the LabVIEW source or having to rebuild and reinstall a new executable. Essentially, Lua and the set of exposed functions constitute an additional user interface to the application. As with any user interface, it is of importance that it "feels natural" and is properly documented.
The first step is choosing what application actions and information should be available in scripts. This depends on the application. Some examples of what might need scripting are:
The sub VIs, top-level VIs, globals, and so on that contain functionality to be made scriptable can be bound using a set of public function VIs: VIs that pull arguments from the Lua stack, execute LabVIEW functionality, and push any results back on the Lua stack. To Lua, these appear like normal functions. When a script starts, each registered public function is assigned to a variable so that it can be called via a descriptive name. Optionally, function names can be prefixed with a table so as to group related functions.
It is important to reflect on how LabVIEW-side functions appear when used from Lua. Directly wrapping subVIs might not result in easy-to-use functions. In cases where a reference or some other identifier of a LabVIEW-side object is to be passed around, consider using the object/method idiom. Another option is to create a Lua library (a script containing a set of Lua function definitions) that wraps more basic LabVIEW-side functions and can be imported into the user's script. This has as additional advantage that it provides a non-LabVIEW means for users to customize their scripting environment with additional convenience functions by way of adjusting or adding library scripts.
Once the functions are implemented, they must be documented. The Lua for LabVIEW toolkit contains a "Function Manager" that assists in the creation and documentation of functions and provides for the exporting of overview documentation of multiple functions as HTML. This provides the users with an indexed reference document in which the details of the various functions can be looked up while writing scripts.
Scripts will typically be run as a Lua for LabVIEW task. Tasks can be managed through the "Task Manager" and can make use of task configuration and inter-task communication features. If your application is synchronous so that it supports only a single script at a time, the "Task Manager" is not required. If none of the other task-specific features are required, scripts can also be executed in-line (like a subVI).
Lua for LabVIEW includes features that go beyond providing Lua as an alternate language for the LabVIEW platform. The toolkit makes such advanced or specialised functionality available at development time yet optional at run time: only required functionality need be included into an application. This optional functionality is likely to be useful when you have a large-scale application with involved configuration and initialisation requirements, when you are trying to set up a lean server-side application, or when you are trying to maximise robustness and minimize latencies.
This is no coincidence: Lua for LabVIEW was created as a subsystem of a scriptable platform for implementing tests commissioned by the ESTEC battery test centre, and has been spun out as an independent product. The remainder of this section details how the additional features of the Lua for LabVIEW toolkit met the needs of that particular application in order to provide a concrete example of their use.
The application involves a platform for setting up and scripting battery and fuel cell tests. Requirements were that test stations should be able to manage the testing of multiple batteries, have minimal downtime since the tests are costly and can take years, have a low latency when initiating test actions, be re-programmable without interrupting ongoing tests, allow for configurations to be traceable, and be amenable to later customisation and extension with support for new instrumentation hardware.
To reduce the risk of instability and be able to guarantee that any such issues are resolvable, the number and complexity of technologies other than LabVIEW and Lua was minimized. As part of this effort, the test station software runs as a built executable that does not include any graphical user interface code, which tends to cause latencies and increase complexity. Instead, the test station server software is accessed over the network via VI server from a remote client application that contains all graphical user interfaces.
Simultaneous test sequences are run as separate tasks. Tasks are also used to create all other asynchronous (top-level) contexts on the server, e.g. tasks for handling alarms, tasks for logging data, and so on. This reuse makes the task features available throughout the system. Task messages are used to pass alarm events. Asynchronous operations for which pure LabVIEW is best suited, such as handling high-bandwidth data streams, are implemented as Lua for LabVIEW plug-ins that are instantiated and configured from task scripts. Instrumentation management and other background services are implemented via Lua for LabVIEW modules. Downtime is minimized or avoided through task swapping in conjunction with the ability to replace in-use module export functions and incite services. SCADA tags are made available as Lua for LabVIEW objects to scripts. Expression evaluation is used to automatically update selected tags in terms of other tags.
Binary configuration files and external configuration tools are avoided. Instead, where possible, the configuration of system components is moved into the main script of their task or into dedicated scripts containing text-based configuration definitions. By logging these scripts when run or used, the system configuration becomes traceable. Since scripts are easily edited, the creation of configuration panels was only necessary for the small subset of settings requiring frequent changes. Bootstrapping, initialisation, and cleanup are also performed via task scripts so as to provide convenient error handling and make that the architecture is largely defined and revisable via Lua scripts. A large part of the operations performed by such scripts involves the running and management of additional tasks.
This reference section provides background information on the Lua for LabVIEW feature set and links to more detailed documentation. Though mostly intended for reference, an attempt was made to order the presented information such that later subsections build on information presented in prior subsections. If you are new to Lua for LabVIEW, it is advisable to give them a once over.
The Lua for LabVIEW toolkit provides a mechanism for calling LabVIEW-side functionality from Lua. This allows access to the speed and rich function libraries of LabVIEW while retaining the flexibility of the Lua execution environment. When LabVIEW is called, arguments and results can be passed. A choice of data types is available when passing information back and forth between LabVIEW and Lua. Obviously, there are some type translation issues that should be taken into account. Please refer to the linked document for details.
Most of the actions that are required when Lua calls LabVIEW occur automatically inside C code. Still, there are some implementation requirements that LabVIEW-side functions should adhere to. Also, LabVIEW-side functions can be created in various flavours that are optimised for different types of use. Please refer to this appendix for details on creating LabVIEW-side functions.
Scripts can make use of the "base", "debug", "io",
"math", "os", "string", and "table" C function libraries
that are a standard part of Lua. For convenience, the functions in the math library
are set as globals so that they can be called
without a "math" table prefix. Refer to the
Lua documentation for information on these function
libraries. In addition, a
Lua for LabVIEW specific C library, the "lv"
library, is provided. It mostly contains
functions that assist with interfacing Lua to LabVIEW, e.g. functions for manipulating LabVIEW-style integers.
The aforementioned C function
libraries are statically linked into the Lua for LabVIEW shared library and are therefore always available.
Further C libraries can be dynamically linked via the Lua-provided
Several useful libraries with Lua bindings can be found here.
The toolkit contains a collection of Lua for LabVIEW function VIs which are documented here. For these functions to be available to scripts, they must first be registered. When building an executable, it is advisable to include only those function VIs that are of use to your application. The most generally useful functions do not have a table prefix to their name, as is the case for the "base" C library. Other functions are grouped under tables/libraries:
Function VIs are either public or private depending on how they are
registered. Public functions are added to a public registry when registered.
When a new Lua for LabVIEW state is opened via the Open API VI, all public
functions in the registry are set for that virtual machine instance. This is
roughly the same as saying that the
public functions are set when a script or task starts since this almost always
happens immediately after creating a new Lua for LabVIEW state. Public functions cannot
be removed from the registry. They remain loaded while there are open Lua for LabVIEW states that
use public functions. When the last such Lua for LabVIEW state is closed, the functions
in the registry are either automatically unloaded or can be explicitly cleaned
up, depending on how you configure Lua for LabVIEW. API VIs and corresponding Lua for LabVIEW
exist for public registration.
Private functions are registered with a particular Lua for LabVIEW state, typically by a script
that executes under that state. Use the Lua for LabVIEW
to do so from a script, or the corresponding API VIs to do so from LabVIEW. When the function VI being registered is not yet
loaded, it will be on registration. When the Lua for LabVIEW state is subsequently closed, typically
when the script completes, all references to privately registered function VIs
are automatically closed as well. This will cause each function VI that is no
longer referenced from anywhere to be unloaded. The "Function Manager"
shows the use count of privately registered function VIs.
Lua for LabVIEW provides an application programming interface (API) designed for use from LabVIEW. The API consists of callable VIs that are guaranteed to remain upwards compatible with future Lua for LabVIEW releases. Use of Lua for LabVIEW VIs that are not part of the API should be avoided. Upwards compatibility is ensured by preserving existing connector pane inputs and outputs as well as their connector positions. Additional outputs or optional inputs might be added to an API VI as these will not affect existing use. When an API VI has a typedef input or output, corresponding diagram constants should remain connected to that typedef. This allows enums to be extended and elements to be added to clusters. Clusters should be bundled and unbundled by name.
The API VIs are documented through their built-in description strings and a corresponding indexed overview of the API. Most included Lua for LabVIEW functions VIs use this API: when a Lua for LabVIEW function is provided that serves a particular end, a quick way to learn how to do the equivalent from LabVIEW is to look at the diagram of that function. Note that the VIs that reside in "private" directories are not part of the API. They are likely to change and should not be used. The user interface related sub VIs and the VIs in the "library" directory are not part of the API either, though the latter are unlikely to change much.
Since Lua runs inside the LabVIEW runtime, all use of Lua must start out with a call to one of the LabVIEW API functions, e.g. so as to run a task script or compile and synchronously execute a script. Before doing so, the public functions used by that script must have been registered, e.g. via the registration API VIs. A flexible means of starting Lua from your LabVIEW application is to run a bootstrap script and arrange for all further registration and script execution from inside that script.
To execute Lua code you must provide a script to a Lua for LabVIEW state. Lua for LabVIEW will run this as a text string through the Lua compiler after which execution can commence. Convenience functions are provided that encapsulate the opening of a Lua for LabVIEW state, and subsequent script compilation, and execution. These functions accept scripts either as strings or as paths pointing to script files. Script search directories can be configured for the resolution of relative script paths.
It is advisable to use unique filenames for scripts even when these reside in separate directories. This avoids ambiguity when multiple script search directories are specified. Also, the name of script files is used when reporting errors but the path is omitted for brevity. Using unique filenames makes sure that the offending script can be tracked down given an error message. Though Lua for LabVIEW does not enforce nor require use of the ".lua" extension for script files, its use is recommended.
The Do Script API
VI and corresponding
function allow a script to be executed to completion inside the calling
execution context. More refined control over script execution can be attained by
using the low level API VIs. Lua for LabVIEW provides an implementation of such control
infrastructure in the form of tasks.
It can be desirable to manage the execution of scripts, or to use an autonomous script to implement some process or series of actions (think of a script containing test steps). Also, a powerful strategy is to split an application architecture into multiple asynchronous contexts. This prevents operations that should be independent from blocking each other, and provides a further means of dividing up the implementation so as to conquer its complexity. Lua for LabVIEW tasks allow for the easy creation of stand-alone script execution contexts and are provided with functionality for communication and management.
A "Task Manager" graphical user interface is provided
that allows tasks be run and managed manually. The Run Task and
Do Task API VIs allow tasks to be started from LabVIEW. The
do_task functions allow the
same from Lua. Note that scripts that normally execute via "Do Script"
can also be run as tasks, e.g. for testing purposes. The reverse is not
necessarily the case since task scripts can make use of the task infrastructure
and by doing so become incompatible with the simple execution context offered by
Do Script. The execution context of a task is created by instantiating a VI
and running it. Consequently, task
scheduling is done by the LabVIEW runtime. Each task is given its own Lua for LabVIEW state for executing scripts.
Several aspects of the execution environment of tasks are configurable. Since such configuration has to take place before the task script is executed, it cannot be set through executing the script. Still, it is desirable to make the task configuration part of the task script so that all information that determines the task's behaviour is grouped. To this end, Lua for LabVIEW has a pre-processor that extracts task configuration keywords from the task script before it is run. This also serves to import additional scripts such as Lua function libraries.
standard Lua C libraries contain functions that perform similar actions at run
require), the up-front loading and compilation enabled by the pre-processor moves some likely errors from
run time to
initialisation time and allows for the modification of script files without
affecting running tasks: each task caches its scripts in memory before
commencing execution. This difference
in philosophy stems from the fact that Lua is designed to run well on embedded
systems with small amounts of memory. Lua for LabVIEW, on the other hand, is guaranteed
to be running on hardware that can handle the LabVIEW runtime so that the extra
memory required for early compilation and script caching is likely to be small
relative to the available memory.
The list of tasks shown in the "Task Manager" displays a mode for
each task. A task can determine it's own mode via the
function. Tasks can be in one of the following modes:
Tasks can handle the following management signals. These can be sent programmatically or manually via the "Task Manager":
Several options exist to allow or disallow signals and to implement custom handling of signals in Lua. There is no obligation to use tasks and their features. If you do not require the extra functionality offered by tasks, simply run scripts in-line, e.g. using the Do Script API VI, and use the standard C library functions that come with Lua to do script management from within scripts.
Swapping a task involves replacing its script(s) without removing the task
from the task list. This can be done manually
via the "Task Manager" or programmatically via the Swap
Task API VI or
function. Though a task can be stopped after which a new script is run,
swapping a task minimizes downtime and provides better opportunities for
continuation of service. This is of importance when the task monitors, polls, or
controls some critical resource or provides a message-based service that must
When a task is
swapped, the new script and any import scripts are first loaded and compiled.
Only when this succeeds will the task be switched into swapping mode and sent the swap signal. The default handling of this signal causes
script execution to be interrupted and any
functions to be run. Next, the virtual machine instance on which the
old script(s) ran is closed. What is retained is the task execution context and message queue. The new
script or scripts are then executed under a fresh virtual machine instance. The task
remains in swapping mode for as long as it would remain in initializing
mode when run normally: by default the task switches to running or executing
mode when the first line of the main script is executed; when the defer
pre-processor keyword is used, this is postponed until the task
function is called.
Some task scripts cannot be swapped because there is no conceivable means of
cleaning up and restoring their service. Such scripts will want to disallow the swap signal. Other scripts will need to actively participate in the swapping
process in order to be swappable. For example, a script that manages some external instrument will want
to avoid resetting the instrument when it is swapped. Such scripts should either
handle the swap signal themselves or perform initialisation and/or cleanup
actions that are conditional on the result returned by the
Swapping is one of several features designed for applications that need to keep on running while allowing for changes to the implementation of services that are in use. Other features that fall into this category are the ability to override Lua for LabVIEW functions with alternate implementations and the ability to close and re open a module without breaking ongoing calls to its export functions.
The behaviour of Lua for LabVIEW can be tuned via a small set of configuration parameters. These parameters are implemented as LabVIEW globals contained in two files: luaview/glb_LuaVIEW Configuration Globals.vi and library/glb_Library Configuration Globals.vi. They can be adjusted at initialisation time. The default values were chosen to work well for most kinds of use. To learn more about individual parameters, read their description strings.
To assist with specifying an alternate configuration at initialisation time, a Bootstrap API VI is provided. It allows the configuration to be set via an initialisation script. For an example of bootstrapping see examples/Lua for LabVIEW/Bootstrapping.vi.
Many languages have an exception mechanism for error handling. This causes the ongoing function call to be interrupted and the error to be passed up through the various calling contexts until a context is encountered that catches the error. This allows error handling to be separated from the code that defines normal execution. The data-flow nature of LabVIEW makes it unlikely that it will ever be able to support such an exception mechanism. Lua on the other hand can throw and catch errors. By using Lua scripts to drive initialisation and other error prone LabVIEW actions and passing LabVIEW-side errors back to Lua, you can build exception handling into a LabVIEW application. On the LabVIEW side, error handling is done in the usual manner by passing error clusters via dataflow. On the Lua side, errors are strings that are passed implicitly to the catching context.
When an error occurs when executing a Lua script, the error is by default
propagated to the context from which the script was started. Script execution is
interrupted and the virtual machine instance and related state of the script is
closed. It is up to the surrounding context to
decide whether to process or further propagate the error. Lua for LabVIEW functions for executing scripts (such as
propagate errors to the script they were called from, causing that
script to throw an error as well. To allow error handling to be customised, Lua
and Lua for LabVIEW provide means of catching errors (
functions), throwing errors (the
error function), and performing cleanup
actions irrespective of any errors (
cleanup mechanism was implemented so as to remove the main reason for having to catch
When it comes to errors, running tasks are special since they are part of an asynchronous context that is detached from the launching context. Since the launching context might no longer exist, errors thrown by running tasks cannot be propagated. On the other hand, the launching context will often want to make sure that the task it runs makes it safely through its initialisation. To accommodate both needs, tasks that are programmatically run start out in initializing mode. While in this mode, the launching context is blocked and any errors that the task throws are passed back to it. This covers the often error prone but brief initialisation actions. After the task switches from initializing to running or mode, the launching context is unblocked. Any errors thrown by the task from that time onwards cause the task to switch to error mode. Manually run tasks always switch to error mode on an error. Though no longer running, a task in error mode remains accessible via the "Task Manager" for post-mortem analysis until the error is confirmed by a user. If you want such errors to be logged somewhere, specify a logging task via the "task error logger" global configuration setting. If instead you want to programmatically handle errors of a running tasks, it is possible to specify an error handler via the --#handler pre-processor keyword.
As opposed to LabVIEW convention, most error clusters returned by Lua for LabVIEW VIs are created with a description as the source string instead of making use of the error code to specify the error. Only when the error occurs inside some standard LabVIEW VI or node are error codes used, but such errors, when occurring as part of script execution, are immediately explained by means of the error code database. This was done so as to be able to return the specific cause and context of the error instead of being limited to some generic error as specified via a code: an error stating "could not open file" is pretty useless; it is much more useful to be presented with an error message that includes the path to the file that could not be opened, identifies the script that failed, and specifies the line number on which the offending call occurred. To indicate that these errors already are self explanatory, the special user-defined error code 9876 is used so that the handler displaying the error can be customized to skip access to the error code database.
When an error occurs, not all information required to fully describe its cause and effect is available. For this reason, an error is progressively enhanced as it is propagated through the calling contexts up to the catching context. Additional explanatory information is added to the front of the error string and context information (call chain and/or stack trace) is appended to the end of the error string. This implies that the explanation of the root cause can be found in the middle of the error string before the first stack trace or call chain.
As usual, call chains indicate the series of LabVIEW VI calls leading down to the error. Stack traces provide similar information for Lua: a list of nested function calls with function names and line numbers. An error can include multiple call chains and stack traces when it occurs inside nested calling contexts that refrain from catching it. This informs the user of all subsystems that were halted because of the error. Square brackets enclose script or function names and are followed by a semicolon and line number. This allows the script lines involved in an error to be located.
What is missing from this picture is a means of displaying and storing errors. Server applications will typically want to log errors. GUI applications will want to show a dialog or window. Lua for LabVIEW does not assume nor require a particular means since the best course of action will vary from application to application.
All user interfaces are optional: they are not required by the scripting core which is made up of the API VIs and their subVIs. The "Project Manager" and "Unit Tester" are of use only during development. The project manager can be used to load collections of VIs into memory and perform some management operations on them. It is not specific to Lua for LabVIEW, but is handy when used in conjunction with Lua for LabVIEW because there can be a lot of dynamically loaded VIs, such as Lua for LabVIEW functions, which renders the conventional solution of an "all top-level VIs" VI impractical. The unit tester allows the automated running of unit test VIs and scripts. A unit test verifies an implementation subset.
The "Task Manager" and "Function Manager" can be useful both during development or as part of an application. The task manager allows manual running of tasks, provides an overview of active tasks, and allows tasks to be signalled and monitored. When an application makes use of multiple parallel scripts that must be manually managed by the user, including the task manager or a similar user interface might be of value. The function manager lists Lua for LabVIEW or Lua functions and can be used to view, create, or export their documentation. Including the function manager in a build might be useful when the application is set up to allow loading of additional Lua for LabVIEW function VIs or when it is anticipated that users will create their own Lua function libraries.
All user interfaces show online help when the "F1" key is hit. Nearly all controls and indicators include a description which is shown in the floating context help window when hovering the mouse above them. All but the "Project Manager" persist their window size, window position, and the value of selected controls. The persistence data is stored in a subdirectory of the LabVIEW default data directory (whose location is platform and user specific). To assist with programmatically starting or restarting user interface windows, a session object is provided that also persists its data there.
When you build a user interface for use with Lua for LabVIEW, you will normally call the API directly. The "Task Manager" and "Function Manager" instead call API wrapper VIs through the VI-server protocol. This makes them suited for accessing a scripting environment running on a remote machine or in a separate LabVIEW runtime. The wrapper VIs also allow for server-side access checks and sometimes aggregate primitive operations so as to reduce network load and latency. This capability does however complicate the implementation of the "Task Manager" and "Function Manager" so that both are ill-suited as examples of building user interfaces that use Lua for LabVIEW. Since isolating GUIs and/or remote access is usually not required, the client-server functionality is disabled in the default Lua for LabVIEW install via stubs in the clientsrv/ subdirectory. To enable it, install the client-server application framework.
Function VIs as used for public and private functions have some properties
that can be cumbersome for particular types of use. They must be documented,
they must be added to a registry before use, and you can implement only a single
function per VI so that related functions require additional infrastructure in
order to cooperate or exchange data. Lua for LabVIEW modules provide a more convenient means of
creating a set of cooperating LabVIEW-implemented functions. Essentially, a
module is a VI that implements a set of related functions.
Since all functions are part of the same diagram, they can easily operate on
shared state. It is possible to open multiple instances of a single module
implementation. Just as with non-reentrant subVIs, there can only be one caller
of a module instance at any one time. Therefore, using modules avoids race conditions when
operating on module state or on some external resource, such as an instrument,
being managed via the module. To instantiate a module and make its functions available to a script, use the
function. To learn how to implement a module, have a look at the guidelines
for creating functions and review "Module Template.vit" in the
examples/Lua for LabVIEW/VI templates/ directory.
Sometimes it is desirable to make selected functions contained in a module available for public use by scripts other than the script that opened the module. Such exporting is possible simply by prefixing the synopsis of the subset of functions to be exported with an exclamation mark. Any scripts that are started after the module is instantiated will be able to call those functions. Care should be taken to use unique names for these functions. In particular, when a module might be instantiated multiple times, the export functions should be placed in a table with a name that is unique per module instance.
Exported functions provide a convenient synchronous interface to whatever resource the module is managing. A typical use case would be the management of an instrument. The script that instantiates the instrument module takes care of its configuration, initialisation, cleanup, and any periodic actions that must be performed with the instrument. The exported functions can then be used by test scripts to change experimental conditions. If multiple instruments of the same type are connected, multiple module instances can be opened from multiple script contexts, each supplying a unique configuration.
When the module is closed by its instantiating script, or that script comes to a halt, the VI reference of the module instance becomes invalid. One might therefore expect that any further calls to its exported functions, from other scripts, will fail. It is however possible to specify a timeout for such failure. If a new reconfigured instance or an updated version of the module is opened within the specified timeout, and it exports the same functions, such calls will proceed as usual. Thus, module instances can be swapped without bringing down other parts of a system. When allowing such swapping for a module, the implementation of its export functions should avoid relying on state coherence between calls.
LabVIEW does not contain a programming construct that easily allows the scheduling of operations in a flexible manner. The conventional solution is to create a state machine, typically from a combination of a while loop and case structure holding the various states. States are then scheduled via a state selector or a state selection queue that is passed between successive states. This is fine for small numbers of states and simple scheduling needs. However, complex state machines are painful because it requires flipping from state to state, and remembering the scheduling actions of each, in order to figure out what they do.
Obviously, a Lua script can be used as a much more flexible means of scheduling LabVIEW code. However for needs of moderate complexity, the separate script and functions, and the need to register the functions, or instantiate a module, is not as nicely self-contained as a LabVIEW state-machine that fits on a single diagram and does not require dynamic loading. To enable similar self-contained use, a construct for Lua-driven state machines is provided. To create one, specialize the template that can be found under examples/Lua for LabVIEW/VI templates. To see one in action, run examples/Lua for LabVIEW/Fractal.vi. Top-level VIs that contain Lua-driven state machines somewhere in their subVI hierarchy can be saved to a development distribution or built without having to worry about including any dynamic VIs.
Lua for LabVIEW plug-ins can be used when some pure LabVIEW operation must take place
for a prolonged or indefinite amount of time. A plug-in
is implemented as a special type of LabVIEW-side function that is called via the
function. Plug-ins can be configured from the script from which they are called,
and execute inside the context of the calling script, typically as a task. When
run from a task, plug-ins should respond to task signals. When a plug-in is not
in memory on being called, it will be loaded first and will be unloaded after it
returns. Plug-ins are therefore useful in cases where normally a top-level VI
would be loaded and run, but the same configuration and management as can be applied
to tasks is desired. Only weak implementation constraints are placed on
plug-ins. They should have the same connector pane as a function VI, pull the
provided configuration table, push any results they return, output an error when
they fail, and handle signals. A reference implementation of a plug-in can be
found in examples/Lua for LabVIEW/VI templates/. As is the case for modules, it is
possible to have multiple instances of the same plug-in implementation.
LabVIEW is provided with extensive VI libraries and toolkits that cover much more functionality than the C libraries included with Lua. The built-in Lua functionality therefore provides little cause for calling Lua. Also, LabVIEW diagrams are compiled to native machine code and therefore execute considerably faster than equivalent Lua code so that calling Lua from LabVIEW is not a good idea if performance is critical. Still, there are a few areas of functionality for which no good LabVIEW alternative exists and Lua can help out. These include:
To learn how to create LabVIEW-callable Lua functions read this appendix. Currently, there is no means of having LabVIEW call a Lua function that can call back into LabVIEW. Such functionality might be added in a future Lua for LabVIEW release, but it is not high on the list of priorities.
Lua for LabVIEW has some features that work differently from what a Lua, C, or LabVIEW programmer would expect:
sindirectly or calling
math.sinvia the math library table is both possible. Scripts run with Lua for LabVIEW will tend to be short and make frequent use of math functions so that this trade off between namespace pollution and more readable scripts was considered worthwhile.
falsein a Boolean expression, as opposed to C or Python. All values that are neither
true. Lua counts from 1 when indexing its array-alike tables, LabVIEW and C count from 0. When using quoted strings, the backslash character serves as an escape character. To add a backslash to a quoted string, use a double backslash. Alternatively, a string can be specified verbatim by enclosing it in a matched [[ and ]] pair.
io.writefunctions do nothing when used to operate on the default input or output streams as long as these remains set to the standard input and output. Use
io.outputto set files as default streams.
coroutine.resume) or as part of meta methods. A run-time error is thrown when such a call is attempted. For the same reason, handling of task signals is postponed until any ongoing C function or meta method returns.
lv.addcleanupfunction has been provided to assist with this task.
Instead of inventing a new vocabulary, constructs and building blocks provided by Lua for LabVIEW were given names that mirror the naming of similar concepts available in other environments. These names are often but, for brevity, not consistently prefixed by "LuaVIEW" and other adjectives so as to indicate their more specific meaning.
Go up to table of contents
Go to the Lua for LabVIEW main page