Manual for release 2.0

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.


Contents

Appendices


About Lua for LabVIEW

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.

About Lua

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.

Requirements

Lua for LabVIEW requires LabVIEW 7.1.1 or higher for Linux, Mac OS X, or Windows.

Installation

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.

Upgrading to Lua for LabVIEW 2.0

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 5.1 Changes

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.

Generally speaking, most scripts should just continue to run without any problems. There are some areas which may require attention. For instance for the Lua for LabVIEW unit tests there were two places where a long string was embedded inside another long string. While the old long string format without equal signs between the brackets is still supported for single level long strings, Lua 5.1 does explicitedly disallow embedding of long strings inside long strings using the old syntax. So the unit test had to be modified to use the new long string format in those places.

Lua for LabVIEW State

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.

Getting started

Lua has all the typical features you might expect of a text-based language: a means of executing code conditionally (if, then, else), looping constructs (for, 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

print("Hello world!")

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.

What use is it?

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.

Making an application scriptable

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).

Why all the other stuff?

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.

Lua for LabVIEW reference

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.

Calling LabVIEW from Lua

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.

Function libraries

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 loadlib function. 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:

Registering function VIs

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 functions (register.public, register.public_dir) 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 functions register.private and register.private_dir 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.

The LabVIEW API

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.

Scripts

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 do_script 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.

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 run_task and 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.

Though the standard Lua C libraries contain functions that perform similar actions at run time (e.g. 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 task.mode 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 tasks

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 swap_task 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 remain available.

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 cleanup 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 task.active 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 task.mode function.

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.

Configuration

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.

Error handling

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 do_task) 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 (pcall and lv.pcall functions), throwing errors (the error function), and performing cleanup actions irrespective of any errors (lv.addcleanup function). The cleanup mechanism was implemented so as to remove the main reason for having to catch errors.

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.

User interfaces

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.

Modules

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 VI.open_module 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.

Exporting module functions

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.

Lua-driven state machines

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.

Plug-ins

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 call_plugin 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.

Calling Lua from LabVIEW

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.

Unexpected features

Lua for LabVIEW has some features that work differently from what a Lua, C, or LabVIEW programmer would expect:

Glossary

To qualify the different ways in which one bit of code can be started from another, the following conventions are used:
Execute
To perform code synchronously (in-line with an existing execution context).
Do
The combination of synchronously initialising (loading and/or compilation), executing to completion, and cleaning up code.
Run
To have code execute asynchronously (inside a separately created execution context).
Call
To execute code while being able to pass it arguments and have it return results.

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.

G
The graphical language that is part of the LabVIEW distribution was originally named "G" by National Instruments. These days, the language tends to be called LabVIEW as well. The Lua for LabVIEW documentation does not attempt to fight this: wherever the word LabVIEW is used, you will have to judge from the context whether it pertains to LabVIEW-the-language or LabVIEW-the-platform.
C function
A Lua callable function implemented in C. The standard function libraries that come with Lua and the additional "lv" library that comes with Lua for LabVIEW consist of C functions.
Lua for LabVIEW function
A Lua callable function implemented in LabVIEW. These come in various flavours.
LabVIEW-side function
Same as a Lua for LabVIEW function.
Lua for LabVIEW function VI
A Lua for LabVIEW function implemented as a single VI that is called by reference. To be callable, the VI must be registered, which in turn requires that the VI is given a calling synopsis via the "Edit Documentation" option of the "Function Manager". The name of the VI determines the name of the function variable set on the Lua side.
Public function
A Lua for LabVIEW functionVI registered in the public registry. This makes it available to scripts that are subsequently started. This is likely to be the most common type of LabVIEW-side function as it makes things as convenient as possible for the script writer.
Private function
A Lua for LabVIEW function VI is specifically registered with each Lua for LabVIEW state that needs to call it. On first registration, the function VI will be loaded into memory. This can be used to hide dangerous functions from general use or make sure that functionality is loaded only when in use.
Indexed function
A Lua for LabVIEW function specified via a numerical index. Lua for LabVIEW does not specify the LabVIEW mechanism via which the index locates the function. This allows the programmer to implement custom calling mechanisms.
Lua-driven state machine
A state machine alike programming construct whose states constitute indexed functions and whose state transitions are driven by a Lua script.
Lua function
A Lua callable function implemented in Lua.
Lua for LabVIEW Task
An asynchronous execution context for a Lua script. Tasks can be easily instantiated and are provided with communication facilities and execution configuration and management.
Task signal
A mechanism via which a task can be requested to perform a particular action such as stopping, pausing, or resuming.
Task message
Tasks are provided with a message queue and related functions that allow the exchange of message strings between these asynchronous contexts.
Main script
The "top-level" script with which a task is started. This script can configure the task and import further scripts via pre-processor keywords.
Task script
Same as a main script.
Lua for LabVIEW module
A VI implementing multiple Lua-callable functions that can easily share configuration and state. Modules can be instantiated and their functions used from a particular Lua script. In addition, modules can export selected functions for use by any Lua script started after the module was instantiated. It is possible to swap module instances, e.g. for a reconfiguration or update, without risking errors on the part of scripts that use the export functions.
Lua for LabVIEW plug-in
A protracted bit of pure LabVIEW functionality that is dynamically loaded and called as a LabVIEW-side function that takes a configuration table as its single argument. Plug-ins that are called from a task context and run for an indefinite amount of time are expected to handle task signals. Typically, the task script calling a plug-in serves only to instantiate and configure the plug-in. No implementation is prescribed, but a template that can serve a a starting point is provided.
Method
In Lua this denotes a special type of function that takes an object (a table or userdata) as its first argument and is attached to the objects it can operate on. By separating the object and the method key with a ":" instead of ".", Lua allows methods to be called while implicitly passing the attached object as the first argument. This makes that Lua supports the compact method calling idiom familiar from object oriented languages.
Lua for LabVIEW method
A Lua for LabVIEW function that can be called as a method. Typically, all Lua for LabVIEW methods of a particular Lua for LabVIEW object class are grouped in a single table. It is advisable to start the name of such function tables with an uppercase letter so as to give it the appearance of a Java or Python class and discern it from normal function tables.
Lua for LabVIEW object
A special data type that allows a LabVIEW-side object or data item to be given the appearance of a Lua-native object.
Lua for LabVIEW reference
The reference number of a Lua for LabVIEW state.
Lua for LabVIEW state
To be able to compile and execute Lua code, a Lua for LabVIEW state must first be opened. This involves the creation of a new virtual machine instance and the allocation of state needed for resource tracking and LabVIEW-side calls. API operations on different states are either mutually independent and thread safe or serialized via a spin lock. Operations on a single state (as passed via a Lua for LabVIEW reference) must be performed serially. For this reason, all Lua for LabVIEW API VIs that operate on individual states have a Lua for LabVIEW reference input and output terminal so that data-flow sequencing can be used to ensure that no simultaneous operations on the same state are performed.

Go up to table of contents

Go to the Lua for LabVIEW main page