initial DeviceProxy read/write implementation

This commit is contained in:
Grzegorz Kowalski 2023-02-02 23:21:39 +01:00
parent bc20b25c54
commit d9a4685c1f
20 changed files with 947 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
build/*
lib/*

52
Makefile Normal file
View File

@ -0,0 +1,52 @@
VERSION=$(shell git describe --tags --always --dirty)\:$(shell date +'%Y%m%d')
SRC_DIR := src
BUILD_DIR := build
OUT_DIR := lib
ifeq ($(LUA_VERSION),)
LUA_VERSION := 5.1
endif
ifeq ($(PREFIX),)
PREFIX := /usr/local/lib/lua/$(LUA_VERSION)/lutango
endif
LUT_INCLUDE := $(SRC_DIR)
SRCS := $(shell find . -name '*.cpp')
OBJS := $(SRCS:%=$(BUILD_DIR)/%.o)
SONAME := core.so.$(VERSION)
SOLINK := core.so
CC = g++
CXX = $(CC)
CFLAGS = -shared -fpic -D_REENTRANT -DVERSION=\"$(VERSION)\" -I$(LUT_INCLUDE)
CFLAGS += $(shell pkg-config --cflags tango lua$(LUA_VERSION))
LDFLAGS = $(shell pkg-config --libs tango lua$(LUA_VERSION))
CPPFLAGS += $(CFLAGS)
dir-guard=@mkdir -p $(@D)
.PHONY: default clean install
default: $(OUT_DIR)/$(SONAME)
$(OUT_DIR)/$(SONAME): $(OBJS)
$(dir-guard)
$(CXX) $^ $(CPPFLAGS) $(LDFLAGS) -o $@
$(BUILD_DIR)/%.cpp.o: %.cpp
$(dir-guard)
$(CXX) $(CPPFLAGS) -c $< -o $@
clean:
rm -rf build
rm -rf lib
install:
install -d $(PREFIX)
install -m 644 $(SRC_DIR)/*.lua $(PREFIX)
install -s -m 644 $(OUT_DIR)/$(SONAME) $(PREFIX)
ln -sf $(PREFIX)/$(SONAME) $(PREFIX)/$(SOLINK)

34
src/core/logging/log.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "log.h"
void _log(LogLevel level, const char* fmt, const char* _file, const char* _func, int _line, ...)
{
if(level <= current_log_level)
{
va_list args;
va_start(args, _line);
fprintf(stderr, "[%s]@[CORE:%s:%s:%d]: ", LogLevelLabels[level], _file, _func, _line);
vfprintf(stderr, fmt, args);
va_end(args);
fprintf(stderr, "\n");
}
}
void set_log_level(LogLevel level)
{
current_log_level = level;
}
void lut_lua_register_log(lua_State* L)
{
lua_newtable(L);
luaL_register(L, NULL, lut_log);
lua_setfield(L, -2, "log");
LUT_LOG(INFO, "Registered log table");
}
int lut_log_set_log_level(lua_State* L)
{
int level = luaL_checknumber(L, 1);
set_log_level((LogLevel)level);
return 1;
}

38
src/core/logging/log.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef __LOG_H__
# define __LOG_H__
#include <stdio.h>
#include <stdarg.h>
#include <core/lua.h>
#define LUT_LOG(level, msg, ...) _log(level, msg, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__)
typedef enum _LogLevel
{
CRITICAL = 0,
ERROR,
WARNING,
INFO,
DEBUG,
TRACE
} LogLevel;
static const char* LogLevelLabels[] = {"CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "TRACE"};
static LogLevel current_log_level = WARNING;
// C API
void _log(LogLevel level, const char* fmt, const char* _file, const char* _func, int _line, ...);
void set_log_level(LogLevel level);
// Lua API
void lut_lua_register_log(lua_State* L);
int lut_log_set_log_level(lua_State* L);
static const luaL_reg lut_log[] =
{
{ "set_log_level", lut_log_set_log_level },
{ NULL, NULL }
};
#endif /* __LOG_H__ */

16
src/core/lua.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef __LUA_INCLUDES_H__
# define __LUA_INCLUDES_H__
#ifdef __cplusplus
extern "C"
{
#endif
#include <lua.h>
#include <lauxlib.h>
#ifdef __cplusplus
}
#endif
#endif /* __LUA_INCLUDES_H__ */

7
src/core/lua/object.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "object.h"
void *lut_getobj(lua_State *L, int n)
{
LUT_LOG(TRACE, "Extracting object from userdata");
return *(void**)lua_touserdata(L, n);
}

9
src/core/lua/object.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __OBJECT_H__
# define __OBJECT_H__
#include <core/lua.h>
#include <core/logging/log.h>
void *lut_getobj(lua_State *L, int n);
#endif /* __LUT_OBJECT_H__ */

25
src/core/lutango_core.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "lutango_core.h"
#ifdef __cplusplus
extern "C"
{
#endif
int luaopen_lutango_core(lua_State* L)
{
LUT_LOG(TRACE, "luTango core open.");
LUT_LOG(INFO, "luTango core v%s", VERSION);
lua_newtable(L);
lut_lua_register_log(L);
lut_lua_register_sys(L);
lut_lua_register_DeviceProxy(L);
LUT_LOG(TRACE, "Core module table registered.");
return 1;
}
#ifdef __cplusplus
}
#endif

26
src/core/lutango_core.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef __LUTANGO_H__
# define __LUTANGO_H__
//-----------------------------------------------------------------------------
// #define LUTANGO_CORE "lutango_core"
#include <core/lua.h>
#include <core/logging/log.h>
#include <core/sys/sys.h>
#include <core/tango/DeviceProxy/lut_DeviceProxy.h>
#ifdef __cplusplus
extern "C"
{
#endif
// core is loaded as "lutango.core"
int luaopen_lutango_core(lua_State* L);
#ifdef __cplusplus
}
#endif
//-----------------------------------------------------------------------------
#endif /* __LUTANGO_H__ */

21
src/core/sys/sys.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "sys.h"
void lut_lua_register_sys(lua_State* L)
{
lua_newtable(L);
luaL_register(L, NULL, lut_sys);
lua_setfield(L, -2, "sys");
LUT_LOG(INFO, "Registered sys table");
}
int lut_sys_version(lua_State* L)
{
lua_pushstring(L, VERSION);
return 1;
}
int lut_sys_tango_version(lua_State* L)
{
lua_pushstring(L, Tango::TgLibVers);
return 1;
}

20
src/core/sys/sys.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef __SYS_H__
# define __SYS_H__
#include <core/lua.h>
#include <core/tango.h>
#include <core/logging/log.h>
// Lua API
void lut_lua_register_sys(lua_State* L);
int lut_sys_version(lua_State* L);
int lut_sys_tango_version(lua_State* L);
static const luaL_reg lut_sys[] =
{
{ "version", lut_sys_version },
{ "tango_version", lut_sys_tango_version },
{ NULL, NULL }
};
#endif /* __SYS_H__ */

6
src/core/tango.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef __TANGO_INCLUDES_H__
# define __TANGO_INCLUDES_H__
#include <tango/tango.h>
#endif /* __TANGO_INCLUDES_H__ */

View File

@ -0,0 +1,112 @@
#include "lut_DeviceProxy.h"
// WRAPPER CLASS --------------------------------------------------------------
DeviceProxyWrapper::DeviceProxyWrapper(const char* name)
{
dev = new Tango::DeviceProxy(name);
}
DeviceProxyWrapper::~DeviceProxyWrapper()
{
delete dev;
}
// TANGO API ------------------------------------------------------------------
int lut_DeviceProxy_status(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API DeviceProxy:status()");
DeviceProxyWrapper* udata = (DeviceProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->dev->status().c_str());
return 1;
}
int lut_DeviceProxy_get_attribute_list(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API DeviceProxy:get_attribute_list()");
DeviceProxyWrapper* udata = (DeviceProxyWrapper*)lut_getobj(L, 1);
vector<string>* attrlist = udata->dev->get_attribute_list();
lua_newtable(L);
for(int i = 0; i < attrlist->size(); i++)
{
lua_pushstring(L, (*attrlist)[i].c_str());
lua_rawseti(L, -2, i+1);
}
return 1;
}
// LUA API --------------------------------------------------------------------
void lut_lua_register_DeviceProxy(lua_State* L)
{
// create table, register funcs and add it as DeviceProxy to lutango's table
lua_newtable(L);
luaL_register(L, NULL, lut_DeviceProxy);
lua_setfield(L, -2, "DeviceProxy");
LUT_LOG(INFO, "Registered DeviceProxy table");
}
int lut_DeviceProxy_create(lua_State* L)
{
const char* name = luaL_checkstring(L, 1);
LUT_LOG(TRACE, "LUA API DeviceProxy:create(%s)", name);
// push userdata containing the DeviceProxy
DeviceProxyWrapper** udata = (DeviceProxyWrapper**)lua_newuserdata(L, sizeof(DeviceProxyWrapper*));
*udata = new DeviceProxyWrapper(name);
// set metatable, that allows userdata proper garbage collection
luaL_newmetatable(L, LUT_DEVICEPROXY);
luaL_register(L, NULL, lut_DeviceProxy_userdata);
lua_setmetatable(L, -2);
return 1;
}
int lut_DeviceProxy_destroy(lua_State* L)
{
LUT_LOG(TRACE, "LUA API DeviceProxy:destroy()");
DeviceProxyWrapper* udata = (DeviceProxyWrapper*)lut_getobj(L, 1);
delete udata;
return 0;
}
// aka read_attribute
int lut_DeviceProxy_index(lua_State* L)
{
DeviceProxyWrapper* udata = (DeviceProxyWrapper*)lut_getobj(L, 1);
const char* attr_name = luaL_checkstring(L, 2);
LUT_LOG(TRACE, "LUA API DeviceProxy:index(%s)", attr_name);
Tango::DeviceAttribute attr = udata->dev->read_attribute(attr_name);
udata->type_map[attr_name].type = attr.get_type();
udata->type_map[attr_name].format = attr.get_data_format();
extract_and_push(L, attr);
return 1;
}
// aka write_attribute
int lut_DeviceProxy_newindex(lua_State* L)
{
DeviceProxyWrapper* udata = (DeviceProxyWrapper*)lut_getobj(L, 1);
const char* attr_name = luaL_checkstring(L, 2);
LUT_LOG(TRACE, "LUA API DeviceProxy:newindex(%s)", attr_name);
if(udata->type_map.find(attr_name) == udata->type_map.end())
{
LUT_LOG(TRACE, "Type mapping for attribute %s doesn't exist, reading first", attr_name);
Tango::DeviceAttribute attr = udata->dev->read_attribute(attr_name);
udata->type_map[attr_name].type = attr.get_type();
udata->type_map[attr_name].format = attr.get_data_format();
}
Tango::DeviceAttribute v = pop_and_pack(L, udata->type_map[attr_name], attr_name);
udata->dev->write_attribute(v);
return 1;
}

View File

@ -0,0 +1,54 @@
#ifndef __LUT_DEVICEPROXY_H__
# define __LUT_DEVICEPROXY_H__
#include <core/tango.h>
#include <core/lua.h>
#include <core/tango/attrtype.h>
#include <core/lua/object.h>
#include <core/logging/log.h>
#define LUT_DEVICEPROXY "lut_DeviceProxy"
// Wrapper class
class DeviceProxyWrapper
{
public:
DeviceProxyWrapper(const char* name);
~DeviceProxyWrapper();
Tango::DeviceProxy* dev;
AttrTypeMap type_map;
};
// Tango API
int lut_DeviceProxy_status(lua_State* L);
int lut_DeviceProxy_get_attribute_list(lua_State* L);
// Lua API
void lut_lua_register_DeviceProxy(lua_State* L);
int lut_DeviceProxy_create(lua_State* L);
int lut_DeviceProxy_destroy(lua_State* L);
int lut_DeviceProxy_index(lua_State* L);
int lut_DeviceProxy_newindex(lua_State* L);
static const luaL_reg lut_DeviceProxy[] =
{
// Lua API
{ "create", lut_DeviceProxy_create },
{ "index", lut_DeviceProxy_index },
{ "newindex", lut_DeviceProxy_newindex },
// Tango API
{ "status", lut_DeviceProxy_status },
{ "get_attribute_list", lut_DeviceProxy_get_attribute_list},
{ NULL, NULL }
};
static const luaL_reg lut_DeviceProxy_userdata[] =
{
{ "__gc", lut_DeviceProxy_destroy },
{ NULL, NULL }
};
#endif /* __LUT_DEVICEPROXY_H__ */

366
src/core/tango/attrtype.cpp Normal file
View File

@ -0,0 +1,366 @@
#include "attrtype.h"
void extract_and_push(lua_State* L, Tango::DeviceAttribute attr)
{
switch(attr.get_type())
{
case 0: // void
lua_pushnil(L);
break;
case 1: // bool
push_bool(L, attr);
break;
case 2: // short
push_number<short>(L, attr);
break;
case 3: // long
push_number<long>(L, attr);
break;
case 4: // float
push_number<float>(L, attr);
break;
case 5: // double
push_number<double>(L, attr);
break;
case 6: // ushort
push_number<unsigned short>(L, attr);
break;
case 7: // ulong
push_number<unsigned long>(L, attr);
break;
case 8: // string
case 9: // char[]
case 20: // const string
push_string(L, attr);
break;
case 10: // short[]
{
std::vector<short> v;
attr >> v;
push_number_table(L, v);
break;
}
case 11: // long[]
{
std::vector<long> v;
attr >> v;
push_number_table(L, v);
break;
}
case 12: // float[]
{
std::vector<float> v;
attr >> v;
push_number_table(L, v);
break;
}
case 13: // double[]
{
std::vector<double> v;
attr >> v;
push_number_table(L, v);
break;
}
case 14: // ushort[]
{
std::vector<unsigned short> v;
attr >> v;
push_number_table(L, v);
break;
}
case 15: // ulong[]
{
std::vector<unsigned long> v;
attr >> v;
push_number_table(L, v);
break;
}
case 16: // string[]
{
std::vector<std::string> v;
attr >> v;
push_string_table(L, v);
break;
}
case 21: // bool[]
{
std::vector<bool> v;
attr >> v;
push_bool_table(L, v);
break;
}
case 22: // uchar
{
unsigned char v;
attr >> v;
lua_pushstring(L, (const char *)&v);
break;
}
case 23: // long64
push_number<int64_t>(L, attr);
break;
case 24: // ulong64
push_number<uint64_t>(L, attr);
break;
case 25: // long64[]
{
std::vector<int64_t> v;
attr >> v;
push_number_table(L, v);
break;
}
case 26: // ulong64[]
{
std::vector<uint64_t> v;
attr >> v;
push_number_table(L, v);
break;
}
case 27: // int
push_number<int>(L, attr);
break;
case 17: // longstring[] ???
case 18: // doublestring[] ???
case 19: // DevState
case 28: // DevEncoded
LUT_LOG(WARNING, "%s: Attribute type not implemented for reading yet: %d", attr.name.c_str(), attr.get_type());
lua_pushnil(L);
break;
default:
LUT_LOG(ERROR, "%s: Device returned unknown type: %d", attr.name.c_str(), attr.get_type());
lua_pushnil(L);
//return luaL_error(L, "Device returned type %d, that is unknown or unsupported.", attr.get_type());
}
}
template<class T>
void push_number(lua_State* L, Tango::DeviceAttribute attr)
{
Tango::AttrDataFormat fmt = attr.get_data_format();
if(fmt == Tango::SCALAR)
{
T v;
attr >> v;
lua_pushnumber(L, v);
}
else if(fmt == Tango::SPECTRUM || fmt == Tango::IMAGE)
{
std::vector<T> v;
attr >> v;
push_number_table(L, v);
}
else if(fmt == Tango::IMAGE)
{
// std::vector<std::vector<T>> v;
// //attr >> v;
// // push_number_2d(L, v);
LUT_LOG(WARNING, "%s: IMAGE data format is not supported yet", attr.name.c_str());
lua_pushnil(L);
}
else
{
LUT_LOG(ERROR, "%s: Invalid data format", attr.name.c_str());
lua_pushnil(L);
}
}
void push_bool(lua_State* L, Tango::DeviceAttribute attr)
{
Tango::AttrDataFormat fmt = attr.get_data_format();
if(fmt == Tango::SCALAR)
{
bool v;
attr >> v;
lua_pushboolean(L, v);
}
else if(fmt == Tango::SPECTRUM)
{
std::vector<bool> v;
attr >> v;
push_bool_table(L, v);
}
else if(fmt == Tango::IMAGE)
{
LUT_LOG(WARNING, "%s: IMAGE data format is not supported yet", attr.name.c_str());
lua_pushnil(L);
}
else
{
LUT_LOG(ERROR, "%s: Invalid data format", attr.name.c_str());
lua_pushnil(L);
}
}
void push_string(lua_State* L, Tango::DeviceAttribute attr)
{
Tango::AttrDataFormat fmt = attr.get_data_format();
if(fmt == Tango::SCALAR)
{
std::string v;
attr >> v;
lua_pushstring(L, v.c_str());
}
else if(fmt == Tango::SPECTRUM)
{
std::vector<std::string> v;
attr >> v;
push_string_table(L, v);
}
else if(fmt == Tango::IMAGE)
{
LUT_LOG(WARNING, "%s: IMAGE data format is not supported yet", attr.name.c_str());
lua_pushnil(L);
}
else
{
LUT_LOG(ERROR, "%s: Invalid data format", attr.name.c_str());
lua_pushnil(L);
}
}
template<class T>
void push_number_table(lua_State* L, std::vector<T> v)
{
lua_newtable(L);
for(int i = 0; i < v.size(); i++)
{
lua_pushnumber(L, v[i]);
lua_rawseti(L, -2, i+1);
}
}
void push_bool_table(lua_State* L, std::vector<bool> v)
{
lua_newtable(L);
for(int i = 0; i < v.size(); i++)
{
lua_pushboolean(L, v[i]);
lua_rawseti(L, -2, i+1);
}
}
void push_string_table(lua_State* L, std::vector<std::string> v)
{
lua_newtable(L);
for(int i = 0; i < v.size(); i++)
{
lua_pushstring(L, v[i].c_str());
lua_rawseti(L, -2, i+1);
}
}
Tango::DeviceAttribute pop_and_pack(lua_State* L, AttrTypeDescription d, const char* attr_name)
{
switch(d.type)
{
case 0: // void
LUT_LOG(ERROR, "%s: Void is not writeable type!", attr_name);
break;
case 1: // bool
// Lua 5.1 is missing the luaL_checkboolean function
return pack_value(attr_name, (bool)lua_toboolean(L, 3));
break;
case 2: // short
return pack_value(attr_name, pop_number<short>(L, 3));
break;
case 3: // long
return pack_value(attr_name, pop_number<long>(L, 3));
break;
case 4: // float
return pack_value(attr_name, pop_number<float>(L, 3));
break;
case 5: // double
return pack_value(attr_name, pop_number<double>(L, 3));
break;
case 6: // ushort
return pack_value(attr_name, pop_number<unsigned short>(L, 3));
break;
case 7: // ulong
return pack_value(attr_name, pop_number<unsigned long>(L, 3));
break;
case 8: // string
case 9: // char[]
case 20: // const string
return pack_value(attr_name, luaL_checkstring(L, 3));
break;
case 23: // long64
return pack_value(attr_name, pop_number<int64_t>(L, 3));
break;
case 24: // ulong64
return pack_value(attr_name, pop_number<uint64_t>(L, 3));
break;
case 27: // int
return pack_value(attr_name, pop_number<int>(L, 3));
break;
case 10: // short[]
case 11: // long[]
case 12: // float[]
case 13: // double[]
case 14: // ushort[]
case 15: // ulong[]
case 16: // string[]
case 17: // longstring[] ???
case 18: // doublestring[] ???
case 19: // DevState
case 21: // bool[]
case 22: // uchar
case 25: // long64[]
case 26: // ulong64[]
case 28: // DevEncoded
LUT_LOG(WARNING, "%s: Attribute type not implemented for writing yet: %d", attr_name, d.type);
break;
default:
LUT_LOG(ERROR, "%s: Attribute reports unknown type: %d", attr_name, d.type);
}
}
template<class T>
T pop_number(lua_State* L, int idx)
{
return luaL_checknumber(L, idx);
}
template<class T>
Tango::DeviceAttribute pack_value(const char* name, T value)
{
Tango::DeviceAttribute v(name, value);
return v;
}

42
src/core/tango/attrtype.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef __ATTRTYPE_H__
# define __ATTRTYPE_H__
#include <vector>
#include <map>
#include <inttypes.h>
#include <core/lua.h>
#include <core/tango.h>
#include <core/logging/log.h>
typedef struct
{
int type;
Tango::AttrDataFormat format;
} AttrTypeDescription;
typedef std::map<const char*, AttrTypeDescription> AttrTypeMap;
// READ ATTRIBUTE -------------------------------------------------------------
void extract_and_push(lua_State* L, Tango::DeviceAttribute attr);
template<class T>
void push_number(lua_State* L, Tango::DeviceAttribute attr);
void push_bool(lua_State* L, Tango::DeviceAttribute attr);
void push_string(lua_State* L, Tango::DeviceAttribute attr);
template<class T>
void push_number_table(lua_State* L, std::vector<T> value);
void push_bool_table(lua_State* L, std::vector<bool> value);
void push_string_table(lua_State* L, std::vector<std::string> value);
// WRITE ATTRIBUTE ------------------------------------------------------------
Tango::DeviceAttribute pop_and_pack(lua_State* L, AttrTypeDescription d, const char* attr_name);
template<class T>
T pop_number(lua_State* L, int idx);
template<class T>
Tango::DeviceAttribute pack_value(const char* name, T value);
#endif /* __ATTRTYPE_H__ */

14
src/init.lua Normal file
View File

@ -0,0 +1,14 @@
local log = require "lutango.lutLog"
local core = require "lutango.core"
local lutObject = require "lutango.lutObject"
local lutango = {
__prefix = prefix,
log = log,
sys = core.sys,
lutObject = lutObject,
DeviceProxy = lutObject("DeviceProxy")
}
log(log.level.TRACE, "luTango module ready")
return lutango

35
src/lutLog.lua Normal file
View File

@ -0,0 +1,35 @@
local core = require "lutango.core"
local utils = require "lutango.utils"
local _log_level = {
CRITICAL = 0,
ERROR = 1,
WARNING = 2,
INFO = 3,
DEBUG = 4,
TRACE = 5
}
local log = {
level = _log_level,
current_log_level = _log_level.WARNING
}
function log:__call(level, msg)
if level <= self.current_log_level then
level = utils.get_key_by_value(self.level, level)
local caller = debug.getinfo(2)
local file = utils.get_file_name(caller.source)
local func = caller.name or "<anonymous>"
local line = caller.linedefined
io.stderr:write("["..level.."]@[API:"..file..":"..func..":"..line.."]: "..msg.."\n")
end
end
function log:set_log_level(level)
self.current_log_level = level
core.log.set_log_level(level)
end
setmetatable(log, log)
return log

53
src/lutObject.lua Normal file
View File

@ -0,0 +1,53 @@
local core = require "lutango.core"
local log = require "lutango.lutLog"
local lutObject = {}
function create_lutObject(type)
log(log.level.TRACE, "New lutObject for "..type)
local o = {
__type = type
}
setmetatable(o, lutObject)
return o
end
function lutObject:__call(...)
log(log.level.TRACE, "New "..self.__type.."("..table.concat({...}, ", ")..")")
local o = {
__type = self.__type
}
if self.__type then
o.__obj = core[self.__type].create(...)
end
setmetatable(o, lutObject)
return o
end
function lutObject:__index(key)
if self.__type then
local from_core = core[self.__type][key]
if from_core then
return function(...)
return from_core(self.__obj, ...)
end
end
return core[self.__type].index(self.__obj, key)
else return nil end
end
function lutObject:__newindex(key, value)
if self.__type then
local from_core = core[self.__type][key]
if from_core then
log(log.level.ERROR, "Cannot write non-attribute: "..key)
return nil
end
core[self.__type].newindex(self.__obj, key, value)
else return nil end
end
return create_lutObject

15
src/utils.lua Normal file
View File

@ -0,0 +1,15 @@
local utils = {}
function utils.get_key_by_value(t, value)
for k,v in pairs(t) do
if v == value then return k end
end
return nil
end
function utils.get_file_name(path)
return path:match("^.+[/\\](.+)$")
end
return utils