Legrand / Raritan / Server Technology Xerus™ JSON-RPC API
|
Lua - A powerful and fast scripting language, simple to learn and use
PLC - Programmable Logic Controller
The LuaPLC feature lets you write small programs and execute them directly on the PDU. Scripts can be deployed using the web GUI, via JSON-RPC or with USB flash drives. They can be started and stopped manually, triggered by event rules or scheduled with timer events.
Lua scripts have full access to the PDU data model as well as a few selected configuration interfaces. Additionally, scripts can use JSON-RPC to control remote PDUs.
Lua is a powerful scripting language, easy to learn and simple to use. See the links section below to find about more the language itself.
There are a few details worth mentioning in the script above:
pdumodel.Pdu:getDefault()
to acquire a proxy object for the pdumodel.Pdu interface. This proxy object can be used to invoke the methods defined in the Pdu.idl file. It's the starting point for all scripts working with the PDU data model. All other PDU-related objects (inlets, outlets, sensors, etc.) can be acquired from here.getMetaData()
method on the Pdu proxy object. Note the :
character between object and method name, it's a critical part of the syntax! As defined in IDL, the call returns a pdumodel.Pdu.MetaData structure which includes the model name in the field nameplate.model.getInlets()
method on the Pdu proxy. The method returns a list of proxy objects for the pdumodel.Inlet interface defined in Inlet.idl. For a typical PDU with a single inlet, the list contains exactly one item.for
loop in the next line iterates over the returned inlet proxies. ipairs
is a Lua-builtin function that enumerates the elements in a list. For each iteration, it returns the index and value of the next list element. Since we're only interested in the value (the inlet proxy), we assign the index to a variable called _
.getMetaData()
and getSensors()
on this proxy. Again, take note of the :
syntax to invoke object methods.getReading()
on that proxy to retrieve the latest sensor reading and prints the result to the script output.The following table shows how IDL data types are mapped into Lua:
IDL | Lua |
---|---|
boolean | boolean |
int | number |
long | number |
float | number |
double | number |
string | string |
time | number |
enumeration | number |
structure | table |
vector | table |
map | table |
interface | table |
Each IDL file is mapped to a Lua library that must be loaded with the require
statement before use. For instance, to use the Pdu interface defined in the Pdu.idl file, use the statement require "Pdu"
.
Loading a Lua library automatically loads all directly or indirectly referenced libraries. For instance, requiring the Pdu automatically includes many other modules, like Inlet, Outlet, NumericSensor or PeripheralDeviceManager.
An IDL enumeration value is represented by a number in Lua. Additionally, there are constants for each element of the enumeration defined in IDL. For instance, to set the startupState field in the pdumodel.Pdu.Settings structure can be set to any of the values in the pdumodel.Pdu.StartupState table:
References to IDL interfaces are represented by Lua tables containing the defined methods. To invoke a method, append the method name to the object reference separated by :
character. IDL in parameters are mapped to required function arguments in Lua. Methods return a tuple of IDL-defined return value (unless void) and output parameters (if any).
Root objects are the entry points for using the IDL-defined API. They are single instances of IDL interfaces that can be acquired using a static method (getDefault()
, getInstance()
or new()
). References to other interfaces, like inlets, outlets and sensors, can be reached directly or indirectly from one of these root instances.
The following interfaces contain a static getDefault()
method to acquire a root instance:
Some root objects require additional parameters and can be created with getInstance()
:
Lastly, the following objects are part of the system interface and can be created with a call to new()
(possibly with additional parameters):
Remote objects are proxies that communicate with a remote object instance via JSON-RPC. Remote proxies can be created for any supported interface using the static newRemote()
method. Expected parameters are a resource ID (URL suffix) and an HTTP agent (an object containing a reference to a remote PDU's JSON-RPC service).
The signature to create a new HTTP agent is: agent.HttpAgent:new(host, user,
password [, port [, useTls]])
. Required parameters are a host name or IP address, a user name and a password. Optional parameters are a port number and a boolean flag whether to use HTTP or HTTPS.
If the PDU is a cascade primary unit it is possible to communicate with remote object instances on the link units. This is done by using a remote proxy, created with the static newRemote()
method as described above. As before, the expected parameters are a resource ID (URL suffix) and an HTTP agent. The HTTP agent for accessing a certain link unit in the cascade is created by calling agent.HttpAgent:getForLinkUnit(linkId)
.
HttpAgent
uses keep-alive to maintain a built-up connection. While this has performance benefits, it also causes higher memory usage.
If you experience Out of Memory errors, you can try to prevent them by cleaning up unused HTTP connections via the agent.HttpAgent:disconnect()
function.
Lua scripts can have arguments that are specified upon startup. Arguments are key-value pairs that can be accessed through the global table ARGS
. Arguments can be specified persistently in the script options or using the "Start Script
with Arguments" function. Arguments passed at script start override the default arguments from the script options.
If a script implements a global function named ExitHandler()
, that function will be executed when the script stops unexpectedly, either because it crashes or is terminated. As a best practice, put this function at the top of the script (right after the require statements) and do not use any global variables within.
The built-in sleep function can be used to pause the script for a defined time. The argument is specified in seconds, but fractional numbers are supported for sub-second delays.
Without precautions, Lua scripts are terminated upon system errors in an IDL model calls (e.g. because using an object reference that doesn't exist). The built-in Lua function pcall(f [, args...])
can be used to execute function f in protected mode. With pcall, the function to be called and its arguments must be specified separately. This includes the reference to the object the method should be invoked on, which must be passed as a first argument. The convenient syntax with :
character cannot be used unless the method call is wrapped in an anonymous function:
There are some Lua script limitations:
The actual limits can be queried using the getEnvironment()
method of the luaservice.Manager
interface.
Memory usage per script is limited. A script allocating too much memory will be killed by the system. A message like LuaSvcScript: Out of Memory. Aborting ...
is written to the script output.
Scripts can be deployed via the following interfaces:
In addition to uploading scripts to the PDU, script files can be executed directly from a USB mass storage device. You need to put the script on a USB drive, along with a control file called (for historical reasons) fwupdate.cfg. The control file needs to supply the administrator credentials and a reference to the script file:
my_user:my_pass
with Default Credentials of your product, or your own credentials.The script will be started as soon as the USB drive is plugged into the PDU. Any output will be stored in a separate log file on the drive. If the script is still running by the time the USB drive is disconnected it will be terminated. You can register an ExitHandler, but its runtime is limited to three seconds before the script forcibly is killed.
Running scripts from a TFTP server works similar to the USB drive method. Check the appendix in the PDU User Guide for details about the required configuration of the DHCP and TFTP servers. Runtime for scripts started via this method is limited to 60 seconds. Script output will be written back to the TFTP server, if the server allows it.
Lua scripts can be started or stopped as an action in the event rules engine. Use the web GUI (Device Settings > Event Rules) to create a new action. Select Start/Stop Lua Script as action, then select the script you want to control.
Scripts started by an event rule will receive a number of arguments containing information about the matching event rule and the event triggering it. Arguments are stored in the global table ARGS
.
Some Lua links: