1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-09 00:00:00 +01:00
VILLASnode/lib/hooks/lua.cpp

813 lines
16 KiB
C++
Raw Permalink Normal View History

2020-12-07 21:30:22 +01:00
/** Lua expressions hook.
*
* @author Steffen Vogel <stvogel@eonerc.rwth-aachen.de>
* @copyright 2014-2020, Institute for Automation of Complex Power Systems, EONERC
* @license GNU General Public License (version 3)
*
* VILLASnode
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************************/
/** @addtogroup hooks Hook functions
* @{
*/
2021-02-19 06:50:25 +01:00
#include <vector>
#include <map>
2021-02-22 23:30:19 +01:00
#include <cstdio>
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
extern "C" {
2021-03-09 01:49:18 +01:00
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
2021-02-19 06:50:25 +01:00
};
#include <villas/exceptions.hpp>
#include <villas/hooks/lua.hpp>
2020-12-07 21:30:22 +01:00
#include <villas/node.h>
2021-02-19 06:50:25 +01:00
#include <villas/path.h>
2020-12-07 21:30:22 +01:00
#include <villas/sample.h>
2021-02-19 06:50:25 +01:00
#include <villas/signal.h>
#include <villas/signal_list.h>
#include <villas/utils.hpp>
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
using namespace villas;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
class LuaError : public RuntimeError {
2020-12-07 21:30:22 +01:00
protected:
lua_State *L;
2021-02-19 06:50:25 +01:00
int err;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
public:
LuaError(lua_State *l, int e) :
RuntimeError(""),
L(l),
err(e)
{ }
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
virtual const char * what() const noexcept
2020-12-07 21:30:22 +01:00
{
const char *msg;
2021-02-19 06:50:25 +01:00
switch (err) {
case LUA_ERRSYNTAX:
msg = "Syntax error";
break;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
case LUA_ERRMEM:
msg = "Memory allocation error";
break;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
case LUA_ERRFILE:
msg = "Failed to open Lua script";
break;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
case LUA_ERRRUN:
msg = "Runtime error";
break;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
case LUA_ERRERR:
msg = "Failed to call error handler";
break;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
default:
msg = "Unknown error";
break;
2020-12-07 21:30:22 +01:00
}
2021-02-22 23:30:19 +01:00
char *buf;
asprintf(&buf, "Lua: %s: %s", msg, lua_tostring(L, -1));
2021-02-22 23:30:19 +01:00
return buf;
2020-12-07 21:30:22 +01:00
}
2021-02-19 06:50:25 +01:00
};
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
static void
lua_pushtimespec(lua_State *L, struct timespec *ts)
{
lua_createtable(L, 2, 0);
lua_pushnumber(L, ts->tv_sec);
lua_rawseti(L, -2, 0);
lua_pushnumber(L, ts->tv_nsec);
lua_rawseti(L, -2, 1);
}
static void
lua_totimespec(lua_State *L, struct timespec *ts)
{
lua_rawgeti(L, -1, 0);
ts->tv_sec = lua_tonumber(L, -1);
lua_rawgeti(L, -2, 1);
ts->tv_nsec = lua_tonumber(L, -1);
2021-02-19 06:50:25 +01:00
lua_pop(L, 2);
}
static void
lua_tosignaldata(lua_State *L, union signal_data *data, enum SignalType targetType, int idx = -1)
{
int luaType;
2021-02-19 06:50:25 +01:00
enum SignalType type;
luaType = lua_type(L, idx);
switch (luaType) {
2021-02-19 06:50:25 +01:00
case LUA_TBOOLEAN:
data->b = lua_toboolean(L, idx);
type = SignalType::BOOLEAN;
break;
case LUA_TNUMBER:
data->f = lua_tonumber(L, idx);
type = SignalType::FLOAT;
break;
default:
return;
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
signal_data_cast(data, type, targetType);
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
static void
lua_tosample(lua_State *L, struct sample *smp, struct vlist *signals, bool use_names = true, int idx = -1)
{
int ret;
2021-03-17 10:23:03 -04:00
smp->length = 0;
smp->flags = 0;
2020-12-07 21:30:22 +01:00
2021-03-17 10:23:03 -04:00
lua_getfield(L, idx, "sequence");
ret = lua_type(L, -1);
if (ret != LUA_TNIL) {
smp->sequence = lua_tonumber(L, -1);
smp->flags |= (int) SampleFlags::HAS_SEQUENCE;
}
2021-02-19 06:50:25 +01:00
lua_pop(L, 1);
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
lua_getfield(L, idx, "ts_origin");
2021-03-17 10:23:03 -04:00
ret = lua_type(L, -1);
if (ret != LUA_TNIL) {
lua_totimespec(L, &smp->ts.origin);
smp->flags |= (int) SampleFlags::HAS_TS_ORIGIN;
}
2021-02-19 06:50:25 +01:00
lua_pop(L, 1);
lua_getfield(L, idx, "ts_received");
2021-03-17 10:23:03 -04:00
ret = lua_type(L, -1);
if (ret != LUA_TNIL) {
lua_totimespec(L, &smp->ts.received);
smp->flags |= (int) SampleFlags::HAS_TS_RECEIVED;
}
2021-02-19 06:50:25 +01:00
lua_pop(L, 1);
lua_getfield(L, idx, "data");
2021-03-17 10:23:03 -04:00
ret = lua_type(L, -1);
if (ret != LUA_TNIL) {
for (unsigned i = 0; i < smp->length; i++) {
struct signal *sig = (struct signal *) vlist_at(signals, i);
if (use_names)
lua_getfield(L, -1, sig->name);
else
lua_rawgeti(L, -1, i);
2021-02-19 06:50:25 +01:00
2021-03-17 10:23:03 -04:00
ret = lua_type(L, -1);
if (ret != LUA_TNIL)
lua_tosignaldata(L, &smp->data[i], sig->type, -1);
2021-02-19 06:50:25 +01:00
2021-03-17 10:23:03 -04:00
lua_pop(L, 1);
}
2021-02-19 06:50:25 +01:00
2021-03-17 10:23:03 -04:00
if (smp->length > 0)
smp->flags |= (int) SampleFlags::HAS_DATA;
2020-12-07 21:30:22 +01:00
}
2021-02-19 06:50:25 +01:00
lua_pop(L, 1);
}
static void
lua_pushsample(lua_State *L, struct sample *smp, bool use_names = true)
{
lua_createtable(L, 0, 5);
lua_pushnumber(L, smp->flags);
lua_setfield(L, -2, "flags");
if (smp->flags & (int) SampleFlags::HAS_SEQUENCE) {
lua_pushnumber(L, smp->sequence);
lua_setfield(L, -2, "sequence");
}
2021-02-19 06:50:25 +01:00
if (smp->flags & (int) SampleFlags::HAS_TS_ORIGIN) {
lua_pushtimespec(L, &smp->ts.origin);
lua_setfield(L, -2, "ts_origin");
}
2021-02-19 06:50:25 +01:00
if (smp->flags & (int) SampleFlags::HAS_TS_RECEIVED) {
lua_pushtimespec(L, &smp->ts.received);
lua_setfield(L, -2, "ts_received");
}
2021-02-19 06:50:25 +01:00
if (smp->flags & (int) SampleFlags::HAS_DATA) {
lua_createtable(L, smp->length, 0);
2021-02-19 06:50:25 +01:00
for (unsigned i = 0; i < smp->length; i++) {
struct signal *sig = (struct signal *) vlist_at(smp->signals, i);
union signal_data *data = &smp->data[i];
2021-02-19 06:50:25 +01:00
switch (sig->type) {
case SignalType::FLOAT:
lua_pushnumber(L, data->f);
break;
2020-12-07 21:30:22 +01:00
case SignalType::INTEGER:
lua_pushinteger(L, data->i);
break;
2020-12-07 21:30:22 +01:00
case SignalType::BOOLEAN:
lua_pushboolean(L, data->b);
break;
case SignalType::COMPLEX:
case SignalType::INVALID:
default:
continue; /* we skip unknown types. Lua will see a nil value in the table */
}
if (use_names)
lua_setfield(L, -2, sig->name);
else
lua_rawseti(L, -2, i);
2020-12-07 21:30:22 +01:00
}
lua_setfield(L, -2, "data");
}
}
static void
lua_pushjson(lua_State *L, json_t *json)
{
size_t i;
const char *key;
json_t *json_value;
switch (json_typeof(json)) {
case JSON_OBJECT:
lua_newtable(L);
json_object_foreach(json, key, json_value) {
lua_pushjson(L, json_value);
lua_setfield(L, -2, key);
}
break;;
case JSON_ARRAY:
lua_newtable(L);
json_array_foreach(json, i, json_value) {
lua_pushjson(L, json_value);
lua_rawseti(L, -2, i);
}
break;
case JSON_STRING:
lua_pushstring(L, json_string_value(json));
break;
case JSON_REAL:
case JSON_INTEGER:
lua_pushnumber(L, json_integer_value(json));
break;
case JSON_TRUE:
case JSON_FALSE:
lua_pushboolean(L, json_boolean_value(json));
break;
case JSON_NULL:
lua_pushnil(L);
break;
}
}
static json_t * lua_tojson(lua_State *L, int index = -1)
{
double n;
const char *s;
bool b;
switch (lua_type(L, index)) {
case LUA_TFUNCTION:
case LUA_TUSERDATA:
case LUA_TTHREAD:
case LUA_TLIGHTUSERDATA:
case LUA_TNIL:
return json_null();
case LUA_TNUMBER:
n = lua_tonumber(L, index);
return n == (int) n
? json_integer(n)
: json_real(n);
case LUA_TBOOLEAN:
b = lua_toboolean(L, index);
return json_boolean(b);
case LUA_TSTRING:
s = lua_tostring(L, index);
return json_string(s);
case LUA_TTABLE: {
int keys_total = 0, keys_int = 0, key_highest = -1;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
keys_total++;
if (lua_type(L, -2) == LUA_TNUMBER) {
int key = lua_tonumber(L, -1);
if (key == (int) key) {
keys_int++;
if (key > key_highest)
key_highest = key;
}
}
lua_pop(L, 1);
}
bool is_array = keys_total == keys_int && key_highest / keys_int > 0.5;
json_t *json = is_array
? json_array()
: json_object();
lua_pushnil(L);
while (lua_next(L, index) != 0) {
json_t *val = lua_tojson(L, -1);
if (is_array) {
int key = lua_tonumber(L, -2);
json_array_set(json, key, val);
}
else {
const char *key = lua_tostring(L, -2);
if (key) /* Skip table entries whose keys are neither string or number! */
json_object_set(json, key, val);
}
lua_pop(L, 1);
}
return json;
}
2020-12-07 21:30:22 +01:00
}
return nullptr;
2021-02-19 06:50:25 +01:00
}
namespace villas {
namespace node {
LuaSignalExpression::LuaSignalExpression(lua_State *l, json_t *json_sig) :
2021-02-19 06:50:25 +01:00
cookie(0),
L(l)
2021-02-19 06:50:25 +01:00
{
int ret;
json_error_t err;
2021-02-19 06:50:25 +01:00
const char *expr;
/* Parse expression */
2021-02-19 06:50:25 +01:00
ret = json_unpack_ex(json_sig, &err, 0, "{ s: s }",
"expression", &expr
2021-02-19 06:50:25 +01:00
);
if (ret)
throw ConfigError(json_sig, err, "node-config-hook-lua-signals");
cfg = json_sig;
expression = expr;
}
void
LuaSignalExpression::prepare()
{
parseExpression(expression);
2021-02-19 06:50:25 +01:00
}
void
LuaSignalExpression::parseExpression(const std::string &expr)
2021-02-19 06:50:25 +01:00
{
/* Release previous expression */
if (cookie)
luaL_unref(L, LUA_REGISTRYINDEX, cookie);
2021-02-19 06:50:25 +01:00
auto fexpr = fmt::format("return {}", expr);
int err = luaL_loadstring(L, fexpr.c_str());
2021-02-19 06:50:25 +01:00
if (err)
throw ConfigError(cfg, "node-config-hook-lua-signals", "Failed to load Lua expression: {}", lua_tostring(L, -1));
2021-02-19 06:50:25 +01:00
cookie = luaL_ref(L, LUA_REGISTRYINDEX);
2021-02-19 06:50:25 +01:00
}
void
LuaSignalExpression::evaluate(union signal_data *data, enum SignalType type)
2021-02-19 06:50:25 +01:00
{
int err;
lua_rawgeti(L, LUA_REGISTRYINDEX, cookie);
2021-02-19 06:50:25 +01:00
err = lua_pcall(L, 0, 1, 0);
2021-02-19 06:50:25 +01:00
if (err) {
throw RuntimeError("Lua: Evaluation failed: {}", lua_tostring(L, -1));
lua_pop(L, 1);
2020-12-07 21:30:22 +01:00
}
2021-03-17 10:23:03 -04:00
lua_tosignaldata(L, data, type, -1);
lua_pop(L, 1);
}
LuaHook::LuaHook(struct vpath *p, struct vnode *n, int fl, int prio, bool en) :
Hook(p, n, fl, prio, en),
L(luaL_newstate()),
useNames(true),
hasExpressions(false)
{
int ret;
ret = signal_list_init(&signalsProcessed);
if (ret)
throw RuntimeError("Failed to initialize signal list");
ret = signal_list_init(&signalsExpressions);
if (ret)
throw RuntimeError("Failed to initialize signal list");
}
LuaHook::~LuaHook()
{
int ret __attribute__((unused));
lua_close(L);
2021-02-19 06:50:25 +01:00
ret = signal_list_destroy(&signalsProcessed);
ret = signal_list_destroy(&signalsExpressions);
2021-02-19 06:50:25 +01:00
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
void
LuaHook::parseExpressions(json_t *json_sigs)
{
int ret;
2021-02-19 06:50:25 +01:00
size_t i;
json_t *json_sig;
2020-12-07 21:30:22 +01:00
signal_list_clear(&signalsExpressions);
ret = signal_list_parse(&signalsExpressions, json_sigs);
if (ret)
2021-02-19 06:50:25 +01:00
throw ConfigError(json_sigs, "node-config-hook-lua-signals", "Setting 'signals' must be a list of dicts");
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
json_array_foreach(json_sigs, i, json_sig)
expressions.emplace_back(L, json_sig);
2020-12-07 21:30:22 +01:00
hasExpressions = true;
2021-02-19 06:50:25 +01:00
}
void
2021-02-16 14:15:14 +01:00
LuaHook::parse(json_t *json)
2021-02-19 06:50:25 +01:00
{
int ret;
const char *script_str = nullptr;
int names = 1;
json_error_t err;
json_t *json_signals = nullptr;
assert(state != State::STARTED);
2021-02-16 14:15:14 +01:00
Hook::parse(json);
2021-02-19 06:50:25 +01:00
2021-02-16 14:15:14 +01:00
ret = json_unpack_ex(json, &err, 0, "{ s?: s, s?: o, s?: b }",
2021-02-19 06:50:25 +01:00
"script", &script_str,
"signals", &json_signals,
"use_names", &names
);
if (ret)
2021-02-16 14:15:14 +01:00
throw ConfigError(json, err, "node-config-hook-lua");
2021-02-19 06:50:25 +01:00
useNames = names;
if (script_str)
2020-12-07 21:30:22 +01:00
script = script_str;
2021-02-19 06:50:25 +01:00
if (json_signals)
parseExpressions(json_signals);
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
state = State::PARSED;
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
void
LuaHook::lookupFunctions()
2021-02-19 06:50:25 +01:00
{
int ret;
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
std::map<const char *, int *> funcs = {
{ "start", &functions.start },
{ "stop", &functions.stop },
{ "restart", &functions.restart },
{ "prepare", &functions.prepare },
{ "periodic", &functions.periodic },
{ "process", &functions.process }
};
for (auto it : funcs) {
lua_getglobal(L, it.first);
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
ret = lua_type(L, -1);
if (ret == LUA_TFUNCTION) {
2021-02-16 14:15:14 +01:00
logger->debug("Found Lua function: {}()", it.first);
2021-02-19 06:50:25 +01:00
*(it.second) = lua_gettop(L);
2020-12-07 21:30:22 +01:00
}
2021-02-19 06:50:25 +01:00
else {
*(it.second) = 0;
lua_pop(L, 1);
}
}
}
void
LuaHook::loadScript()
{
int ret;
if (script.empty())
return; /* No script given. */
ret = luaL_loadfile(L, script.c_str());
if (ret)
throw LuaError(L, ret);
ret = lua_pcall(L, 0, LUA_MULTRET, 0);
if (ret)
throw LuaError(L, ret);
}
int
LuaHook::luaRegisterApiHandler(lua_State *L)
{
// register_api_handler(path_regex)
return 0;
}
int
LuaHook::luaInfo(lua_State *L)
{
logger->info(luaL_checkstring(L, 1));
return 0;
}
int
LuaHook::luaWarn(lua_State *L)
{
logger->warn(luaL_checkstring(L, 1));
return 0;
}
int
LuaHook::luaError(lua_State *L)
{
logger->error(luaL_checkstring(L, 1));
return 0;
}
int
LuaHook::luaDebug(lua_State *L)
{
logger->debug(luaL_checkstring(L, 1));
return 0;
}
void
LuaHook::setupEnvironment()
{
lua_pushlightuserdata(L, this);
lua_rawseti(L, LUA_REGISTRYINDEX, SELF_REFERENCE);
lua_register(L, "info", &dispatch<&LuaHook::luaInfo>);
lua_register(L, "warn", &dispatch<&LuaHook::luaWarn>);
lua_register(L, "error", &dispatch<&LuaHook::luaError>);
lua_register(L, "debug", &dispatch<&LuaHook::luaDebug>);
lua_register(L, "register_api_handler", &dispatch<&LuaHook::luaRegisterApiHandler>);
}
void
LuaHook::prepare()
{
/* Load Lua standard libraries */
luaL_openlibs(L);
/* Load our Lua script */
2021-02-16 14:15:14 +01:00
logger->debug("Loading Lua script: {}", script);
setupEnvironment();
loadScript();
lookupFunctions();
/* Check if we need to protect the Lua state with a mutex
* This is the case if we have a periodic callback defined
* As periodic() gets called from the main thread
*/
needsLocking = functions.periodic > 0;
/* Prepare Lua process() */
if (functions.process) {
/* We currently do not support the alteration of
* signal metadata in process() */
signal_list_copy(&signalsProcessed, &signals);
}
2021-02-19 06:50:25 +01:00
/* Prepare Lua expressions */
if (hasExpressions) {
for (auto &expr : expressions)
expr.prepare();
signal_list_clear(&signals);
signal_list_copy(&signals, &signalsExpressions);
}
if (!functions.process && !hasExpressions)
logger->warn("The hook has neither a script or expressions defined. It is a no-op!");
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
if (functions.prepare) {
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
2021-02-19 06:50:25 +01:00
logger->debug("Executing Lua function: prepare()");
lua_pushvalue(L, functions.prepare);
2021-02-16 14:15:14 +01:00
lua_pushjson(L, config);
int ret = lua_pcall(L, 1, 0, 0);
if (ret)
throw LuaError(L, ret);
2021-02-19 06:50:25 +01:00
}
}
void
LuaHook::start()
{
assert(state == State::PREPARED);
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
2021-02-19 06:50:25 +01:00
if (functions.start) {
logger->debug("Executing Lua function: start()");
lua_pushvalue(L, functions.start);
int ret = lua_pcall(L, 0, 0, 0);
if (ret)
throw LuaError(L, ret);
2020-12-07 21:30:22 +01:00
}
2021-02-19 06:50:25 +01:00
state = State::STARTED;
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
void
LuaHook::stop()
{
assert(state == State::STARTED);
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
2021-02-19 06:50:25 +01:00
if (functions.stop) {
logger->debug("Executing Lua function: stop()");
lua_pushvalue(L, functions.stop);
int ret = lua_pcall(L, 0, 0, 0);
if (ret)
throw LuaError(L, ret);
2020-12-07 21:30:22 +01:00
}
2021-02-19 06:50:25 +01:00
state = State::STOPPED;
}
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
void
LuaHook::restart()
{
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
2021-02-19 06:50:25 +01:00
assert(state == State::STARTED);
2020-12-07 21:30:22 +01:00
2021-02-19 06:50:25 +01:00
if (functions.restart) {
logger->debug("Executing Lua function: restart()");
lua_pushvalue(L, functions.restart);
int ret = lua_pcall(L, 0, 0, 0);
if (ret)
throw LuaError(L, ret);
2021-02-19 06:50:25 +01:00
}
else
Hook::restart();
}
void
LuaHook::periodic()
2021-02-19 06:50:25 +01:00
{
assert(state == State::STARTED);
2021-02-19 06:50:25 +01:00
if (functions.periodic) {
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
2021-02-19 06:50:25 +01:00
logger->debug("Executing Lua function: restart()");
lua_pushvalue(L, functions.periodic);
int ret = lua_pcall(L, 0, 0, 0);
if (ret)
throw LuaError(L, ret);
2021-02-19 06:50:25 +01:00
}
}
2020-12-07 21:30:22 +01:00
Hook::Reason
2021-06-29 10:45:12 -04:00
LuaHook::process(struct sample *smp)
{
if (!functions.process && !hasExpressions)
return Reason::OK;
int rtype;
enum Reason reason;
auto lockScope = needsLocking
? std::unique_lock<std::mutex>(mutex)
: std::unique_lock<std::mutex>();
/* First, run the process() function of the script */
2021-02-19 06:50:25 +01:00
if (functions.process) {
logger->debug("Executing Lua function: process(smp)");
2020-12-07 21:30:22 +01:00
lua_pushsample(L, smp, useNames);
2021-02-19 06:50:25 +01:00
lua_pushvalue(L, functions.process);
lua_pushvalue(L, -2); // Push a copy since lua_pcall() will pop it
int ret = lua_pcall(L, 1, 1, 0);
if (ret)
throw LuaError(L, ret);
rtype = lua_type(L, -1);
if (rtype == LUA_TNUMBER) {
reason = (Reason) lua_tonumber(L, -1);
}
else {
logger->warn("Lua process() did not return a valid number. Assuming Reason::OK");
reason = Reason::OK;
}
lua_pop(L, 1);
lua_tosample(L, smp, &signalsProcessed, useNames);
}
else
reason = Reason::OK;
2021-02-19 06:50:25 +01:00
/* After that evaluate expressions */
if (hasExpressions) {
2021-02-19 06:50:25 +01:00
lua_pushsample(L, smp, useNames);
lua_setglobal(L, "smp");
for (unsigned i = 0; i < expressions.size(); i++) {
auto *sig = (struct signal *) vlist_at(&signalsExpressions, i);
expressions[i].evaluate(&smp->data[i], sig->type);
}
smp->length = expressions.size();
2021-02-19 06:50:25 +01:00
}
return reason;
2021-02-19 06:50:25 +01:00
}
2020-12-07 21:30:22 +01:00
/* Register hook */
static char n[] = "lua";
static char d[] = "Implement hook in Lua";
static HookPlugin<LuaHook, n, d, (int) Hook::Flags::NODE_READ | (int) Hook::Flags::NODE_WRITE | (int) Hook::Flags::PATH, 1> p;
} /* namespace node */
} /* namespace villas */
/** @} */