simplify attribute info handling

This commit is contained in:
Grzegorz Kowalski 2023-02-09 01:22:59 +01:00
parent 1338eae773
commit 9bd078b221
6 changed files with 342 additions and 306 deletions

View File

@ -20,6 +20,8 @@ namespace lut_Tango
{
static const char* DispLevelNames[] = {"OPERATOR", "EXPERT", "DL_UNKNOWN"};
static const char* AttrDataFormatNames[] = {"SCALAR", "SPECTRUM", "IMAGE", "FMT_UNKNOWN"};
static const char* AttrMemorizedTypeNames[] = {"NOT_KNOWN", "NONE", "MEMORIZED", "MEMORIZED_WRITE_INIT"};
static const char* AttrWriteTypeNames[] = {"READ", "READ_WITH_WRITE", "WRITE", "READ_WRITE", "WT_UNKNOWN"};
}
#endif /* __TANGO_INCLUDES_H__ */

View File

@ -0,0 +1,282 @@
//-----------------------------------------------------------------------------
//
// luTango - Lua binding for Tango
//
// Copyright (C) 2023 Grzegorz Kowalski
// See LICENSE for legal information
//
// file: lut_AttrCmdInfo.cpp
//
// Bindings for AttributeInfoEx and CommandInfo classes
//
//-----------------------------------------------------------------------------
#include "lut_AttrCmdInfo.h"
void lut_AttributeInfo(lua_State* L, Tango::AttributeInfoEx info)
{
lua_createtable(L, 0, 25);
int top = lua_gettop(L);
lut_AttributeAlarmInfo(L, info.alarms);
lua_setfield(L, top, "alarms");
push_string_table(L, info.enum_labels);
lua_setfield(L, top, "enum_labels");
lua_createtable(L, 3, 0);
int events_top = lua_gettop(L);
lut_ArchiveEventInfo(L, info.events.arch_event);
lua_setfield(L, events_top, "archive_event");
lut_ChangeEventInfo(L, info.events.ch_event);
lua_setfield(L, events_top, "change_event");
lut_PeriodicEventInfo(L, info.events.per_event);
lua_setfield(L, events_top, "periodic_event");
lua_setfield(L, top, "events");
lua_pushstring(L, lut_Tango::AttrMemorizedTypeNames[info.memorized]);
lua_setfield(L, top, "memorized");
lua_pushstring(L, info.root_attr_name.c_str());
lua_setfield(L, top, "root_attr_name");
lua_pushstring(L, lut_Tango::DispLevelNames[info.disp_level]);
lua_setfield(L, top, "disp_level");
lua_pushstring(L, lut_Tango::AttrDataFormatNames[info.data_format]);
lua_setfield(L, top, "data_format");
lua_pushstring(L, Tango::CmdArgTypeName[info.data_type]);
lua_setfield(L, top, "tango_type");
lua_pushstring(L, to_lua_type_name(info.data_type).c_str());
lua_setfield(L, top, "data_type");
lua_pushstring(L, info.description.c_str());
lua_setfield(L, top, "description");
lua_pushstring(L, info.display_unit.c_str());
lua_setfield(L, top, "display_unit");
lua_pushstring(L, info.format.c_str());
lua_setfield(L, top, "format");
lua_pushstring(L, info.label.c_str());
lua_setfield(L, top, "label");
lua_pushstring(L, info.max_alarm.c_str());
lua_setfield(L, top, "max_alarm");
lua_pushstring(L, info.min_alarm.c_str());
lua_setfield(L, top, "min_alarm");
lua_pushstring(L, info.max_value.c_str());
lua_setfield(L, top, "max_value");
lua_pushstring(L, info.min_value.c_str());
lua_setfield(L, top, "min_value");
lua_pushnumber(L, info.max_dim_x);
lua_setfield(L, top, "max_dim_x");
lua_pushnumber(L, info.max_dim_y);
lua_setfield(L, top, "max_dim_y");
lua_pushstring(L, info.name.c_str());
lua_setfield(L, top, "name");
lua_pushstring(L, info.standard_unit.c_str());
lua_setfield(L, top, "standard_unit");
lua_pushstring(L, info.unit.c_str());
lua_setfield(L, top, "unit");
lua_pushstring(L, lut_Tango::AttrWriteTypeNames[info.writable]);
lua_setfield(L, top, "writable");
lua_pushstring(L, info.writable_attr_name.c_str());
lua_setfield(L, top, "writable_attr_name");
push_string_table(L, info.extensions);
lua_setfield(L, top, "extensions");
push_string_table(L, info.sys_extensions);
lua_setfield(L, top, "sys_extensions");
}
void lut_CommandInfo(lua_State* L, Tango::CommandInfo info)
{
lua_createtable(L, 0, 9);
int top = lua_gettop(L);
lua_pushstring(L, lut_Tango::DispLevelNames[info.disp_level]);
lua_setfield(L, top, "disp_level");
lua_pushstring(L, info.cmd_name.c_str());
lua_setfield(L, top, "cmd_name");
lua_pushnumber(L, info.cmd_tag);
lua_setfield(L, top, "cmd_tag");
lua_pushstring(L, to_lua_type_name(info.in_type).c_str());
lua_setfield(L, top, "in_type");
lua_pushstring(L, Tango::CmdArgTypeName[info.in_type]);
lua_setfield(L, top, "in_tango_type");
lua_pushstring(L, info.in_type_desc.c_str());
lua_setfield(L, top, "in_type_desc");
lua_pushstring(L, to_lua_type_name(info.out_type).c_str());
lua_setfield(L, top, "out_type");
lua_pushstring(L, Tango::CmdArgTypeName[info.out_type]);
lua_setfield(L, top, "out_tango_type");
lua_pushstring(L, info.out_type_desc.c_str());
lua_setfield(L, top, "out_type_desc");
}
void lut_AttributeAlarmInfo(lua_State* L, Tango::AttributeAlarmInfo info)
{
lua_createtable(L, 0, 7);
int top = lua_gettop(L);
lua_pushstring(L, info.delta_t.c_str());
lua_setfield(L, top, "delta_t");
lua_pushstring(L, info.delta_val.c_str());
lua_setfield(L, top, "delta_val");
lua_pushstring(L, info.max_alarm.c_str());
lua_setfield(L, top, "max_alarm");
lua_pushstring(L, info.max_warning.c_str());
lua_setfield(L, top, "max_warning");
lua_pushstring(L, info.min_alarm.c_str());
lua_setfield(L, top, "min_alarm");
lua_pushstring(L, info.min_warning.c_str());
lua_setfield(L, top, "min_warning");
push_string_table(L, info.extensions);
lua_setfield(L, top, "extensions");
}
void lut_ArchiveEventInfo(lua_State* L, Tango::ArchiveEventInfo info)
{
lua_createtable(L, 0, 4);
int top = lua_gettop(L);
lua_pushstring(L, info.archive_abs_change.c_str());
lua_setfield(L, top, "archive_abs_change");
lua_pushstring(L, info.archive_period.c_str());
lua_setfield(L, top, "archive_period");
lua_pushstring(L, info.archive_rel_change.c_str());
lua_setfield(L, top, "archive_rel_change");
push_string_table(L, info.extensions);
lua_setfield(L, top, "extensions");
}
void lut_ChangeEventInfo(lua_State* L, Tango::ChangeEventInfo info)
{
lua_createtable(L, 0, 3);
int top = lua_gettop(L);
lua_pushstring(L, info.abs_change.c_str());
lua_setfield(L, top, "abs_change");
lua_pushstring(L, info.rel_change.c_str());
lua_setfield(L, top, "rel_change");
push_string_table(L, info.extensions);
lua_setfield(L, top, "extensions");
}
void lut_PeriodicEventInfo(lua_State* L, Tango::PeriodicEventInfo info)
{
lua_createtable(L, 0, 2);
int top = lua_gettop(L);
lua_pushstring(L, info.period.c_str());
lua_setfield(L, top, "period");
push_string_table(L, info.extensions);
lua_setfield(L, top, "extensions");
}
std::string to_lua_type_name(int type)
{
std::string type_name;
switch(type)
{
case Tango::DEV_VOID: // 0 - void
type_name = "nil";
break;
case Tango::DEV_BOOLEAN: // 1 - bool
type_name = "boolean";
break;
case Tango::DEV_SHORT: // 2 - short
case Tango::DEV_LONG: // 3 - long
case Tango::DEV_FLOAT: // 4 - float
case Tango::DEV_DOUBLE: // 5 - double
case Tango::DEV_USHORT: // 6 - ushort
case Tango::DEV_ULONG: // 7 - ulong
case Tango::DEV_LONG64: // 23 - long64
case Tango::DEV_ULONG64: // 24 - ulong64
case Tango::DEV_INT: // 27 - int
type_name = "number";
break;
case Tango::DEV_STRING: // 8 - string
case Tango::DEVVAR_CHARARRAY: // 9 - char[]
case Tango::CONST_DEV_STRING: // 20 - const string
case Tango::DEV_UCHAR: // 22 - uchar
type_name = "string";
break;
case Tango::DEVVAR_SHORTARRAY: // 10 - short[]
case Tango::DEVVAR_LONGARRAY: // 11 - long[]
case Tango::DEVVAR_FLOATARRAY: // 12 - float[]
case Tango::DEVVAR_DOUBLEARRAY: // 13 - double[]
case Tango::DEVVAR_USHORTARRAY: // 14 - ushort[]
case Tango::DEVVAR_ULONGARRAY: // 15 - ulong[]
case Tango::DEVVAR_LONG64ARRAY: // 25 - long64[]
case Tango::DEVVAR_ULONG64ARRAY: // 26 - ulong64[]
type_name = "table(number)";
break;
case Tango::DEVVAR_STRINGARRAY: // 16 - string[]
type_name = "table(string)";
break;
case Tango::DEV_STATE: // 19 - DevState
type_name = "DevState";
break;
case Tango::DEVVAR_BOOLEANARRAY: // 21 - bool[]
type_name = "table(boolean)";
break;
case Tango::DEVVAR_STATEARRAY: // 31 - DevState[]
type_name = "table(DevState)";
break;
case Tango::DEVVAR_LONGSTRINGARRAY: // 17 - longstring[] ???
case Tango::DEVVAR_DOUBLESTRINGARRAY: // 18 - doublestring[] ???
case Tango::DEV_ENCODED: // 28 - DevEncoded
case Tango::DEV_ENUM: // 29 - DevEnum
case Tango::DEV_PIPE_BLOB: // 30 - DevPipeBlob
case Tango::DATA_TYPE_UNKNOWN: // 32 - unknown
default:
type_name = "-not-supported-";
break;
}
return type_name;
}

View File

@ -0,0 +1,31 @@
//-----------------------------------------------------------------------------
//
// luTango - Lua binding for Tango
//
// Copyright (C) 2023 Grzegorz Kowalski
// See LICENSE for legal information
//
// file: lut_AttrCmdInfo.h
//
// Bindings for AttributeInfoEx and CommandInfo classes
//
//-----------------------------------------------------------------------------
#ifndef __LUT_ATTRCMDINFO_H__
# define __LUT_ATTRCMDINFO_H__
#include <core/tango.h>
#include <core/lua.h>
#include <core/logging/log.h>
#include <core/lua/stack.h>
void lut_AttributeInfo(lua_State* L, Tango::AttributeInfoEx info);
void lut_CommandInfo(lua_State* L, Tango::CommandInfo info);
void lut_AttributeAlarmInfo(lua_State* L, Tango::AttributeAlarmInfo info);
void lut_ArchiveEventInfo(lua_State* L, Tango::ArchiveEventInfo info);
void lut_ChangeEventInfo(lua_State* L, Tango::ChangeEventInfo info);
void lut_PeriodicEventInfo(lua_State* L, Tango::PeriodicEventInfo info);
std::string to_lua_type_name(int type);
#endif /* __LUT_ATTRCMDINFO_H__ */

View File

@ -17,7 +17,7 @@
AttributeProxyWrapper::AttributeProxyWrapper(const char* name)
{
attr = new Tango::AttributeProxy(name);
cache = attr->get_config();
info = attr->get_config();
}
AttributeProxyWrapper::~AttributeProxyWrapper()
@ -25,66 +25,6 @@ AttributeProxyWrapper::~AttributeProxyWrapper()
delete attr;
}
int AttributeProxyWrapper::type()
{
return cache.data_type;
}
const char* AttributeProxyWrapper::type_name()
{
return Tango::CmdArgTypeName[cache.data_type];
}
Tango::AttrDataFormat AttributeProxyWrapper::format()
{
return cache.data_format;
}
Tango::DispLevel AttributeProxyWrapper::disp_level()
{
return cache.disp_level;
}
std::string AttributeProxyWrapper::label()
{
return cache.label;
}
std::string AttributeProxyWrapper::description()
{
return cache.description;
}
std::string AttributeProxyWrapper::disp_format()
{
return cache.format;
}
std::string AttributeProxyWrapper::unit()
{
return cache.unit;
}
std::vector<std::string> AttributeProxyWrapper::enum_labels()
{
return cache.enum_labels;
}
int AttributeProxyWrapper::max_dim_x()
{
return cache.max_dim_x;
}
int AttributeProxyWrapper::max_dim_y()
{
return cache.max_dim_y;
}
bool AttributeProxyWrapper::is_read_only()
{
return (cache.writable == Tango::READ);
}
// TANGO API ------------------------------------------------------------------
int lut_AttributeProxy_state(lua_State* L)
{
@ -122,196 +62,15 @@ int lut_AttributeProxy_status(lua_State* L)
return 1;
}
int lut_AttributeProxy_type(lua_State* L)
int lut_AttributeProxy_get_config(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:type()");
LUT_LOG(TRACE, "TANGO API AttributeProxy:get_config()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
std::string type;
switch(udata->type())
{
case Tango::DEV_VOID: // 0 - void
type = "nil";
break;
case Tango::DEV_BOOLEAN: // 1 - bool
type = "boolean";
break;
case Tango::DEV_SHORT: // 2 - short
case Tango::DEV_LONG: // 3 - long
case Tango::DEV_FLOAT: // 4 - float
case Tango::DEV_DOUBLE: // 5 - double
case Tango::DEV_USHORT: // 6 - ushort
case Tango::DEV_ULONG: // 7 - ulong
case Tango::DEV_LONG64: // 23 - long64
case Tango::DEV_ULONG64: // 24 - ulong64
case Tango::DEV_INT: // 27 - int
type = "number";
break;
case Tango::DEV_STRING: // 8 - string
case Tango::DEVVAR_CHARARRAY: // 9 - char[]
case Tango::CONST_DEV_STRING: // 20 - const string
case Tango::DEV_UCHAR: // 22 - uchar
type = "string";
break;
case Tango::DEVVAR_SHORTARRAY: // 10 - short[]
case Tango::DEVVAR_LONGARRAY: // 11 - long[]
case Tango::DEVVAR_FLOATARRAY: // 12 - float[]
case Tango::DEVVAR_DOUBLEARRAY: // 13 - double[]
case Tango::DEVVAR_USHORTARRAY: // 14 - ushort[]
case Tango::DEVVAR_ULONGARRAY: // 15 - ulong[]
case Tango::DEVVAR_LONG64ARRAY: // 25 - long64[]
case Tango::DEVVAR_ULONG64ARRAY: // 26 - ulong64[]
type = "table(number)";
break;
case Tango::DEVVAR_STRINGARRAY: // 16 - string[]
type = "table(string)";
break;
case Tango::DEV_STATE: // 19 - DevState
type = "DevState";
break;
case Tango::DEVVAR_BOOLEANARRAY: // 21 - bool[]
type = "table(boolean)";
break;
case Tango::DEVVAR_STATEARRAY: // 31 - DevState[]
type = "table(DevState)";
break;
case Tango::DEVVAR_LONGSTRINGARRAY: // 17 - longstring[] ???
case Tango::DEVVAR_DOUBLESTRINGARRAY: // 18 - doublestring[] ???
case Tango::DEV_ENCODED: // 28 - DevEncoded
case Tango::DEV_ENUM: // 29 - DevEnum
case Tango::DEV_PIPE_BLOB: // 30 - DevPipeBlob
case Tango::DATA_TYPE_UNKNOWN: // 32 - unknown
default:
type = "-not-supported-";
break;
}
Tango::AttrDataFormat format = udata->format();
if(format == Tango::SCALAR)
;
else if(format == Tango::SPECTRUM)
type = "table(" + type + ")";
else
type = "-not-supported-";
lua_pushstring(L, type.c_str());
lut_AttributeInfo(L, udata->info);
return 1;
}
int lut_AttributeProxy_tango_type(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:tango_type()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->type_name());
return 1;
}
int lut_AttributeProxy_format(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:format()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, lut_Tango::AttrDataFormatNames[udata->format()]);
return 1;
}
int lut_AttributeProxy_disp_level(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:disp_level()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, lut_Tango::DispLevelNames[udata->disp_level()]);
return 1;
}
int lut_AttributeProxy_label(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:label()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->label().c_str());
return 1;
}
int lut_AttributeProxy_description(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:description()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->description().c_str());
return 1;
}
int lut_AttributeProxy_disp_format(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:disp_format()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->disp_format().c_str());
return 1;
}
int lut_AttributeProxy_unit(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:unit()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushstring(L, udata->unit().c_str());
return 1;
}
int lut_AttributeProxy_enum_labels(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:enum_labels()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
push_string_table(L, udata->enum_labels());
return 1;
}
int lut_AttributeProxy_max_dim_x(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:max_dim_x()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushnumber(L, udata->max_dim_x());
return 1;
}
int lut_AttributeProxy_max_dim_y(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:max_dim_y()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushnumber(L, udata->max_dim_y());
return 1;
}
int lut_AttributeProxy_is_read_only(lua_State* L)
{
LUT_LOG(TRACE, "TANGO API AttributeProxy:is_read_only()");
AttributeProxyWrapper* udata = (AttributeProxyWrapper*)lut_getobj(L, 1);
lua_pushboolean(L, udata->is_read_only());
return 1;
}
// LUA API --------------------------------------------------------------------
void lut_lua_register_AttributeProxy(lua_State* L)
{
@ -370,12 +129,12 @@ int lut_AttributeProxy_call(lua_State* L)
{
// called without arguments - read attribute
LUT_LOG(TRACE, "Reading attribute %s", attr_name.c_str());
lut_fromTangoType(L, udata->attr->read(), udata->type(), udata->format());
lut_fromTangoType(L, udata->attr->read(), udata->info.data_type, udata->info.data_format);
return 1;
}
else
{
Tango::DeviceAttribute v = lut_toTangoType<Tango::DeviceAttribute>(L, 2, udata->type(), udata->format());
Tango::DeviceAttribute v = lut_toTangoType<Tango::DeviceAttribute>(L, 2, udata->info.data_type, udata->info.data_format);
v.set_name(attr_name);
udata->attr->write(v);
return 0;

View File

@ -27,6 +27,7 @@
#include <core/logging/log.h>
#include <core/tango/types.h>
#include <core/tango/AttrCmdInfo/lut_AttrCmdInfo.h>
#include <core/tango/DevState/lut_DevState.h>
#include <core/tango/DevFailed/lut_DevFailed.h>
@ -37,42 +38,16 @@ class AttributeProxyWrapper
{
public:
Tango::AttributeProxy* attr;
Tango::AttributeInfoEx info;
AttributeProxyWrapper(const char* name);
~AttributeProxyWrapper();
int type();
const char* type_name();
Tango::AttrDataFormat format();
Tango::DispLevel disp_level();
std::string label();
std::string description();
std::string disp_format();
std::string unit();
std::vector<std::string> enum_labels();
int max_dim_x();
int max_dim_y();
bool is_read_only();
private:
Tango::AttributeInfoEx cache;
};
// Tango API
int lut_AttributeProxy_state(lua_State* L);
int lut_AttributeProxy_status(lua_State* L);
int lut_AttributeProxy_type(lua_State* L);
int lut_AttributeProxy_tango_type(lua_State* L);
int lut_AttributeProxy_format(lua_State* L);
int lut_AttributeProxy_disp_level(lua_State* L);
int lut_AttributeProxy_label(lua_State* L);
int lut_AttributeProxy_description(lua_State* L);
int lut_AttributeProxy_disp_format(lua_State* L);
int lut_AttributeProxy_unit(lua_State* L);
int lut_AttributeProxy_enum_labels(lua_State* L);
int lut_AttributeProxy_max_dim_x(lua_State* L);
int lut_AttributeProxy_max_dim_y(lua_State* L);
int lut_AttributeProxy_is_read_only(lua_State* L);
int lut_AttributeProxy_get_config(lua_State* L);
// Lua API
void lut_lua_register_AttributeProxy(lua_State* L);
@ -89,18 +64,7 @@ static const luaL_Reg lut_AttributeProxy[] =
// Tango API
{ "state", lut_AttributeProxy_state },
{ "status", lut_AttributeProxy_status },
{ "type", lut_AttributeProxy_type },
{ "tango_type", lut_AttributeProxy_tango_type },
{ "format", lut_AttributeProxy_format },
{ "disp_level", lut_AttributeProxy_disp_level },
{ "label", lut_AttributeProxy_label },
{ "description", lut_AttributeProxy_description },
{ "disp_format", lut_AttributeProxy_disp_format },
{ "unit", lut_AttributeProxy_unit },
{ "enum_labels", lut_AttributeProxy_enum_labels },
{ "max_dim_x", lut_AttributeProxy_max_dim_x },
{ "max_dim_y", lut_AttributeProxy_max_dim_y },
{ "is_read_only", lut_AttributeProxy_is_read_only },
{ "get_config", lut_AttributeProxy_get_config},
{ NULL, NULL }
};

View File

@ -14,6 +14,19 @@
local lut = require "lutango"
lut.log:set_log_level(lut.log.level.WARNING)
local function print_kv(k, v, prefix)
prefix = prefix or ""
print(prefix..k..": "..tostring(v))
if type(v) == "table" then
for kk,vv in ipairs(v) do
print_kv(kk, vv, prefix.."\t")
end
for kk,vv in pairs(v) do
print_kv(kk, vv, prefix.."\t")
end
end
end
local ap = lut.AttributeProxy("sys/tg_test/1/ampli")
print("Reading state and status")
@ -30,23 +43,8 @@ ap(v+1)
print("Reading again")
print("ampli = "..ap())
print("Attribute info:")
print("type: "..tostring(ap:type()))
print("tango_type: "..tostring(ap:tango_type()))
print("format: "..tostring(ap:format()))
print("displ_level: "..tostring(ap:disp_level()))
print("label: "..tostring(ap:label()))
print("description: "..tostring(ap:description()))
print("disp_format: "..tostring(ap:disp_format()))
print("unit: "..tostring(ap:unit()))
local enum_labels = ap:enum_labels()
io.write("enum_labels: "..tostring(enum_labels))
io.write(": {")
for _,l in ipairs(enum_labels) do
io.write(tostring(l)..", ")
local info = ap:get_config()
print("\nAttribute info: "..tostring(info))
for k,v in pairs(info) do
print_kv(k, v)
end
print("}")
print("max_dim_x: "..tostring(ap:max_dim_x()))
print("max_dim_y: "..tostring(ap:max_dim_y()))
print("read_only?: "..tostring(ap:is_read_only()))