From 803a7ab47866cc1750e2e0fdf807a06018941903 Mon Sep 17 00:00:00 2001 From: Lizongdi <1210855344@qq.com> Date: Thu, 29 Oct 2026 16:57:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9C=A8lua=E7=BB=88?= =?UTF-8?q?=E7=AB=AF=E6=93=8D=E4=BD=9CCV=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- RBcore/drv_interface.c | 12 +- bspMCU/l_stm32.c | 10 +- lua/CMakeLists.txt | 24 +- lua/pb_lua.c | 774 +++++++++++++++++++++++++++++++++++++++++ lua/pb_lua.h | 53 +++ lua/pb_lua_desc.h | 123 +++++++ lua/pb_lua_registry.c | 24 ++ 7 files changed, 1015 insertions(+), 5 deletions(-) create mode 100644 lua/pb_lua.c create mode 100644 lua/pb_lua.h create mode 100644 lua/pb_lua_desc.h create mode 100644 lua/pb_lua_registry.c diff --git a/RBcore/drv_interface.c b/RBcore/drv_interface.c index 74e865d..ff16b51 100644 --- a/RBcore/drv_interface.c +++ b/RBcore/drv_interface.c @@ -22,6 +22,7 @@ #include "FreeRTOS.h" #include "bsp_EEPROM.h" #include "msg_center.h" +#include "lua_base.h" /*----------------------------------------------* * 外部变量说明 * *----------------------------------------------*/ @@ -276,12 +277,18 @@ void controller_init(void); void daemon_Init(void); void Timer_Init(void); +int l_setCV(lua_State *L) +{ + GF_BSP_EEPROM_WriteBytes((uint8_t*)&g_stCV, GF_BSP_EEPROM_CV_struct_define_Start_Address, sizeof(CV_struct_define)); + return 0; +} + void Drv_InterfaceInit(void) { GF_BSP_EEPROM_Init(); - MsgCenter_Init(); GF_BSP_EEPROM_ReadBytes((uint8_t *)&g_stCV, GF_BSP_EEPROM_CV_struct_define_Start_Address, sizeof(CV_struct_define)); - +#if 0 + MsgCenter_Init(); client_setting_Init(); // ground_manger_init(); @@ -337,5 +344,6 @@ void Drv_InterfaceInit(void) daemon_Init(); //Timer_Init(); +#endif log_i("BingooRobot Init success\nversion: %s", Rd_GetBuildTime()); } diff --git a/bspMCU/l_stm32.c b/bspMCU/l_stm32.c index 220a064..df46307 100644 --- a/bspMCU/l_stm32.c +++ b/bspMCU/l_stm32.c @@ -9,6 +9,7 @@ #include "common.h" #include "rd_time.h" #include "bsp_usb.h" +#include "pb_lua.h" volatile uint8_t g_lua_stop_request; // 用于标识是否跳出Lua死循环(依赖于死循环中调用st.ms延时) @@ -444,7 +445,7 @@ static int l_mount (lua_State *L) } #endif -#ifdef BUILD_RBCORE +#ifdef BUILD_PROTOBUF static int l_sendto(lua_State *L) { int MsgCenter_SendTo(const char *pszDstName, uint32_t uiMsgID, const void *pData, uint32_t uiDataLen); @@ -708,6 +709,7 @@ static int l_upload(lua_State *L) } #endif +extern int l_setCV(lua_State *L); int luaopen_stm32(lua_State *L) { lua_sethook(L, l_exit_hook, LUA_MASKCOUNT, 1000000); @@ -796,8 +798,12 @@ int luaopen_stm32(lua_State *L) lua_register_help(L, "mount", l_mount, "mount littlefs disk"); #endif -#ifdef BUILD_RBCORE +#ifdef BUILD_PROTOBUF lua_register_help(L, "sendto", l_sendto, "Send msg: sendto(dst,id,data)"); + lua_register_help(L, "pb_dump", l_pb_dump, "pb_dump(\"CV_struct_define\", cv) -- pretty print"); + lua_register_help(L, "pb_ref", l_pb_ref, "cv = pb_ref(\"CV\") -- live view of C global; pv.Value = 999"); + lua_register_help(L, "setCV", l_setCV, "set CV to EEPROM"); + pb_lua_open(L); #endif return 1; diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt index ee42bc6..445a60b 100644 --- a/lua/CMakeLists.txt +++ b/lua/CMakeLists.txt @@ -18,4 +18,26 @@ if(TARGET platform_Linux) target_link_libraries(lua PUBLIC platform_Linux) else() message(WARNING "[lua] Dependency 'platform_Linux' not found.") -endif() \ No newline at end of file +endif() + +if(TARGET protobuf) + target_link_libraries(${TARGET_NAME} PUBLIC protobuf) +else() + message(STATUS "[${TARGET_NAME}] Dependency 'protobuf' not found.") +endif() + +get_filename_component(ROBOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE) +file(GLOB PROJECT_DIRS LIST_DIRECTORIES true "${ROBOT_DIR}/project/*") +foreach(PROJ_DIR ${PROJECT_DIRS}) + if(IS_DIRECTORY "${PROJ_DIR}") + get_filename_component(PROJ_NAME "${PROJ_DIR}" NAME) + string(TOUPPER "${PROJ_NAME}" PROJ_NAME_UPPER) + if(BUILD_${PROJ_NAME_UPPER}) + set(PROTOBUF_PSOURCE "${PROJ_DIR}/Protobuf/PSource") + if(EXISTS "${PROTOBUF_PSOURCE}") + target_include_directories(${TARGET_NAME} PUBLIC "${PROTOBUF_PSOURCE}") + message(STATUS "[${TARGET_NAME}] Auto-detected protobuf include: ${PROTOBUF_PSOURCE}") + endif() + endif() + endif() +endforeach() \ No newline at end of file diff --git a/lua/pb_lua.c b/lua/pb_lua.c new file mode 100644 index 0000000..1fc7369 --- /dev/null +++ b/lua/pb_lua.c @@ -0,0 +1,774 @@ +/****************************************************************************** + + 版权所有 (C), 2018-2099, Radkil + + ****************************************************************************** + 文 件 名 : pb_lua.c + 版 本 号 : 初稿 + 功 能 描 述 : nanopb 消息的 Lua 桥接:table <-> C struct <-> protobuf 字节流 + Lua 侧约定: + - 二进制一律 Lua string(与 COM:read/write 直通) + - 纯 table 表示消息;t.Value = 999 即改成员 + - nil = 字段缺省(OPTIONAL 不出现在线上) + - 枚举字段为整数值(与 .proto 枚举编号一致,无名字表) + + ******************************************************************************/ +#ifdef BUILD_PROTOBUF +#include +#include +#include +#include + +#include "lua_base.h" +#include "pb_lua.h" +#include "pb_lua_desc.h" +#include "pb.h" +#include "pb_encode.h" +#include "pb_decode.h" +#include "bsp_CV.pb.h" + +/* 结构体/编码缓冲上限(当前最大消息 ~130B 结构 / 118B 编码,留足余量) */ +#define PB_LUA_MAX_STRUCT 256 +#define PB_LUA_MAX_ENCODED 256 + +/*====================================================================== + * 消息注册表: 由 tools/gen_proto_ui.py --pb_lua_registry 生成 + * pb_lua_registry.c 提供 g_pb_msgs / g_pb_msgs_count 强符号。 + * 字段表内容仍走 Type_FIELDLIST X-macro(见 pb_lua_desc.h)。 + *====================================================================*/ +extern CV_struct_define g_stCV; + +/*====================================================================== + * 命名全局绑定注册表(pb_ref 用) + *====================================================================*/ + +#define PB_LUA_MAX_BINDINGS 8 + +typedef struct { + const char *name; + void *ptr; + const pb_lua_msg_t *msg; +} pb_lua_binding_t; + +static pb_lua_binding_t g_pb_bindings[PB_LUA_MAX_BINDINGS]; +static size_t g_pb_nbindings; + +/*====================================================================== + * 查找辅助 + *====================================================================*/ + +static size_t pb_lua_strnlen(const char *s, size_t max) +{ + size_t n = 0; + while (n < max && s[n] != '\0') + n++; + return n; +} + +static const pb_lua_msg_t *pb_lua_find_msg_or_null(const char *name) +{ + for (int i = 0; i < g_pb_msgs_count; i++) { + if (strcmp(g_pb_msgs[i]->name, name) == 0) + return g_pb_msgs[i]; + } + return NULL; +} + +static const pb_lua_msg_t *pb_lua_find_msg(lua_State *L, const char *name) +{ + const pb_lua_msg_t *m = pb_lua_find_msg_or_null(name); + if (m != NULL) + return m; + /* known 列表从注册表动态拼接(生成 registry 后零手改) */ + char known[256]; + size_t off = 0; + known[0] = '\0'; + for (int i = 0; i < g_pb_msgs_count && off + 32 < sizeof(known); i++) { + int w = snprintf(known + off, sizeof(known) - off, "%s%s", + (i ? ", " : ""), g_pb_msgs[i]->name); + if (w <= 0) break; + off += (size_t)w; + } + luaL_error(L, "unknown message type '%s' (known: %s)", name, known); + return NULL; +} + +int pb_lua_bind(const char *name, void *ptr, const char *msgtype) +{ + const pb_lua_msg_t *m; + + if (name == NULL || ptr == NULL) + return -1; + m = pb_lua_find_msg_or_null(msgtype); + if (m == NULL) + return -1; + + for (size_t i = 0; i < g_pb_nbindings; i++) { + if (strcmp(g_pb_bindings[i].name, name) == 0) { + g_pb_bindings[i].ptr = ptr; + g_pb_bindings[i].msg = m; + return 0; + } + } + if (g_pb_nbindings >= PB_LUA_MAX_BINDINGS) + return -2; + g_pb_bindings[g_pb_nbindings].name = name; + g_pb_bindings[g_pb_nbindings].ptr = ptr; + g_pb_bindings[g_pb_nbindings].msg = m; + g_pb_nbindings++; + return 0; +} + +static const pb_lua_field_t *pb_lua_find_field(const pb_lua_msg_t *m, const char *name) +{ + for (size_t i = 0; i < m->nfields; i++) { + if (strcmp(m->fields[i].name, name) == 0) + return &m->fields[i]; + } + return NULL; +} + +/*====================================================================== + * table → C struct(encode 方向) + *====================================================================*/ + +static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg); + +static void pb_lua_set_one(lua_State *L, int vidx, const pb_lua_field_t *f, void *msg) +{ + void *p = (char *)msg + f->offset; + + switch (f->ltype) { + case PB_LUA_T_BOOL: { + bool v; + if (!lua_isboolean(L, vidx)) + luaL_error(L, "field '%s': expected boolean", f->name); + v = lua_toboolean(L, vidx) ? true : false; + memcpy(p, &v, sizeof(v)); + break; + } + case PB_LUA_T_INT32: { + int32_t v; + if (!lua_isinteger(L, vidx)) + luaL_error(L, "field '%s': expected integer", f->name); + lua_Integer li = lua_tointeger(L, vidx); + if (li < (lua_Integer)INT32_MIN || li > (lua_Integer)INT32_MAX) + luaL_error(L, "field '%s': integer out of int32 range", f->name); + v = (int32_t)li; + memcpy(p, &v, sizeof(v)); + break; + } + case PB_LUA_T_INT64: { + int64_t v; + if (!lua_isinteger(L, vidx)) + luaL_error(L, "field '%s': expected integer", f->name); + v = (int64_t)lua_tointeger(L, vidx); + memcpy(p, &v, sizeof(v)); + break; + } + case PB_LUA_T_DOUBLE: { + double v; + if (!lua_isnumber(L, vidx)) + luaL_error(L, "field '%s': expected number", f->name); + v = (double)lua_tonumber(L, vidx); + memcpy(p, &v, sizeof(v)); + break; + } + case PB_LUA_T_STRING: { + size_t len; + const char *s; + if (lua_type(L, vidx) != LUA_TSTRING) + luaL_error(L, "field '%s': expected string", f->name); + s = lua_tolstring(L, vidx, &len); + if (memchr(s, 0, len) != NULL) + luaL_error(L, "field '%s': string contains embedded NUL", f->name); + if (len >= f->size) + luaL_error(L, "field '%s': string too long (%u >= %u)", + f->name, (unsigned)len, (unsigned)f->size); + memset(p, 0, f->size); + memcpy(p, s, len); + break; + } + case PB_LUA_T_MESSAGE: { + if (lua_type(L, vidx) != LUA_TTABLE) + luaL_error(L, "field '%s': expected table", f->name); + memset(p, 0, f->size); /* 赋值 = 整体替换子消息(live 路径无预清零) */ + pb_lua_table_to_msg(L, vidx, (const pb_lua_msg_t *)f->aux, p); + break; + } + default: + luaL_error(L, "field '%s': unsupported protobuf type", f->name); + break; + } + + if (f->has_offset >= 0) { + bool has = true; + memcpy((char *)msg + f->has_offset, &has, sizeof(has)); + } +} + +static void pb_lua_table_to_msg(lua_State *L, int tidx, const pb_lua_msg_t *m, void *msg) +{ + tidx = lua_absindex(L, tidx); + + lua_pushnil(L); + while (lua_next(L, tidx) != 0) { + if (lua_type(L, -2) != LUA_TSTRING) { + luaL_error(L, "%s: field name must be a string", m->name); + return; + } + const char *key = lua_tostring(L, -2); + const pb_lua_field_t *f = pb_lua_find_field(m, key); + if (f == NULL) { + luaL_error(L, "unknown field '%s' in %s", key, m->name); + return; + } + if (lua_isnil(L, -1)) { + /* 理论上 lua_next 不返回 nil 值,防御处理:视为缺省 */ + lua_pop(L, 1); + continue; + } + pb_lua_set_one(L, -1, f, msg); + lua_pop(L, 1); + } +} + +/*====================================================================== + * C struct → table(decode 方向) + *====================================================================*/ + +static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m); + +static void pb_lua_get_one(lua_State *L, const void *msg, const pb_lua_field_t *f) +{ + const void *p = (const char *)msg + f->offset; + + switch (f->ltype) { + case PB_LUA_T_BOOL: { + bool v; + memcpy(&v, p, sizeof(v)); + lua_pushboolean(L, v); + break; + } + case PB_LUA_T_INT32: { + int32_t v; + memcpy(&v, p, sizeof(v)); + lua_pushinteger(L, (lua_Integer)v); + break; + } + case PB_LUA_T_INT64: { + int64_t v; + memcpy(&v, p, sizeof(v)); + lua_pushinteger(L, (lua_Integer)v); + break; + } + case PB_LUA_T_DOUBLE: { + double v; + memcpy(&v, p, sizeof(v)); + lua_pushnumber(L, (lua_Number)v); + break; + } + case PB_LUA_T_STRING: { + const char *s = (const char *)p; + lua_pushlstring(L, s, pb_lua_strnlen(s, f->size > 0 ? f->size - 1 : 0)); + break; + } + case PB_LUA_T_MESSAGE: { + pb_lua_msg_to_table(L, p, (const pb_lua_msg_t *)f->aux); + break; + } + default: + lua_pushnil(L); + break; + } +} + +/* OPTIONAL 且 has==false 的字段不进 table(nil = 缺省);SINGULAR 恒进 */ +static int pb_lua_field_present(const void *msg, const pb_lua_field_t *f) +{ + if (f->has_offset < 0) + return 1; + bool has; + memcpy(&has, (const char *)msg + f->has_offset, sizeof(has)); + return has ? 1 : 0; +} + +static void pb_lua_msg_to_table(lua_State *L, const void *msg, const pb_lua_msg_t *m) +{ + lua_createtable(L, (int)m->nfields, 0); + for (size_t i = 0; i < m->nfields; i++) { + const pb_lua_field_t *f = &m->fields[i]; + if (!pb_lua_field_present(msg, f)) + continue; + pb_lua_get_one(L, msg, f); + lua_setfield(L, -2, f->name); + } +} + +/*====================================================================== + * dump:解析显示 + *====================================================================*/ + +static void pb_lua_dump_indent(int depth) +{ + for (int i = 0; i < depth; i++) + lua_print(" "); +} + +static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth); + +/* 递归打印 C 结构字段(不含外层花括号头) */ +static void pb_lua_dump_msg_fields(const void *msg, const pb_lua_msg_t *m, int depth) +{ + for (size_t i = 0; i < m->nfields; i++) { + const pb_lua_field_t *f = &m->fields[i]; + if (!pb_lua_field_present(msg, f)) + continue; + pb_lua_dump_indent(depth); + if (f->ltype == PB_LUA_T_MESSAGE) { + lua_print("%s {\n", f->name); + pb_lua_dump_msg_fields((const char *)msg + f->offset, + (const pb_lua_msg_t *)f->aux, depth + 1); + pb_lua_dump_indent(depth); + lua_print("}\n"); + } + else { + lua_print("%s = ", f->name); + const void *p = (const char *)msg + f->offset; + switch (f->ltype) { + case PB_LUA_T_BOOL: { + bool v; + memcpy(&v, p, sizeof(v)); + lua_print("%s", v ? "true" : "false"); + break; + } + case PB_LUA_T_INT32: { + int32_t v; + memcpy(&v, p, sizeof(v)); + lua_print("%d", v); + break; + } + case PB_LUA_T_INT64: { + int64_t v; + memcpy(&v, p, sizeof(v)); + lua_print("%lld", (long long)v); + break; + } + case PB_LUA_T_DOUBLE: { + double v; + memcpy(&v, p, sizeof(v)); + lua_print("%g", v); + break; + } + case PB_LUA_T_STRING: { + const char *s = (const char *)p; + lua_print("\"%s\"", s); + break; + } + default: + lua_print("?"); + break; + } + lua_print("\n"); + } + } +} + +static void pb_lua_dump_msg(const void *msg, const pb_lua_msg_t *m, int depth) +{ + pb_lua_dump_indent(depth); + lua_print("%s {\n", m->name); + pb_lua_dump_msg_fields(msg, m, depth + 1); + pb_lua_dump_indent(depth); + lua_print("}\n"); +} + +/* 按 table 打印字段(不含外层花括号头,多余键标 [unknown] 置后) */ +static void pb_lua_dump_table_fields(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth) +{ + tidx = lua_absindex(L, tidx); + + for (size_t i = 0; i < m->nfields; i++) { + const pb_lua_field_t *f = &m->fields[i]; + lua_getfield(L, tidx, f->name); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + continue; + } + pb_lua_dump_indent(depth); + if (f->ltype == PB_LUA_T_MESSAGE) { + lua_print("%s {\n", f->name); + pb_lua_dump_table_fields(L, -1, (const pb_lua_msg_t *)f->aux, depth + 1); + pb_lua_dump_indent(depth); + lua_print("}\n"); + lua_pop(L, 1); + } + else { + lua_print("%s = ", f->name); + pb_lua_dump_value(L, -1, f, depth + 1); + lua_print("\n"); + lua_pop(L, 1); + } + } + + /* 多余键 */ + lua_pushnil(L); + while (lua_next(L, tidx) != 0) { + if (lua_type(L, -2) == LUA_TSTRING) { + const char *key = lua_tostring(L, -2); + if (pb_lua_find_field(m, key) == NULL) { + pb_lua_dump_indent(depth); + lua_print("%s = ", key); + if (lua_type(L, -1) == LUA_TSTRING) + lua_print("\"%s\"", lua_tostring(L, -1)); + else if (lua_isinteger(L, -1)) + lua_print("%d", lua_tointeger(L, -1)); + else if (lua_isnumber(L, -1)) + lua_print("%g", (double)lua_tonumber(L, -1)); + else if (lua_isboolean(L, -1)) + lua_print("%s", lua_toboolean(L, -1) ? "true" : "false"); + else + lua_print("%s", lua_typename(L, lua_type(L, -1))); + lua_print(" [unknown]\n"); + } + } + lua_pop(L, 1); + } +} + +static void pb_lua_dump_table(lua_State *L, int tidx, const pb_lua_msg_t *m, int depth) +{ + tidx = lua_absindex(L, tidx); + pb_lua_dump_indent(depth); + lua_print("%s {\n", m->name); + pb_lua_dump_table_fields(L, tidx, m, depth + 1); + pb_lua_dump_indent(depth); + lua_print("}\n"); +} + +static void pb_lua_dump_value(lua_State *L, int vidx, const pb_lua_field_t *f, int depth) +{ + (void)depth; + switch (f->ltype) { + case PB_LUA_T_BOOL: + lua_print("%s", lua_toboolean(L, vidx) ? "true" : "false"); + break; + case PB_LUA_T_INT32: + case PB_LUA_T_INT64: + if (lua_isinteger(L, vidx)) + lua_print("%d", lua_tointeger(L, vidx)); + else + lua_print("%g", (double)lua_tonumber(L, vidx)); + break; + case PB_LUA_T_DOUBLE: + lua_print("%g", (double)lua_tonumber(L, vidx)); + break; + case PB_LUA_T_STRING: + lua_print("\"%s\"", lua_tostring(L, vidx)); + break; + default: + lua_print("%s", lua_typename(L, lua_type(L, vidx))); + break; + } +} + +static void pb_lua_dump_hex(const pb_byte_t *buf, size_t len) +{ + size_t n = len > 64 ? 64 : len; + lua_print("hex:"); + for (size_t i = 0; i < n; i++) + lua_print(" %02X", (unsigned)buf[i]); + if (len > n) + lua_print(" ..."); + lua_print("\n"); +} + +/*====================================================================== + * live userdata(PB_LIVE):C 全局/结构体指针的可写视图 + *====================================================================*/ + +typedef struct { + void *ptr; /* 借用指针,不拥有内存(无 __gc) */ + const pb_lua_msg_t *msg; +} pb_lua_live_t; + +#define PB_LIVE_MT "PB_LIVE" + +static int l_live_index(lua_State *L); +static int l_live_newindex(lua_State *L); +static int l_live_tostring(lua_State *L); +static int l_live_dump(lua_State *L); +static int l_live_encode(lua_State *L); +static int l_live_totable(lua_State *L); + +static void pb_lua_live_open(lua_State *L) +{ + if (!luaL_newmetatable(L, PB_LIVE_MT)) { + lua_pop(L, 1); + return; + } + lua_pushcfunction(L, l_live_index); lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, l_live_newindex); lua_setfield(L, -2, "__newindex"); + lua_pushcfunction(L, l_live_tostring); lua_setfield(L, -2, "__tostring"); + lua_pushcfunction(L, l_live_dump); lua_setfield(L, -2, "dump"); + lua_pushcfunction(L, l_live_encode); lua_setfield(L, -2, "encode"); + lua_pushcfunction(L, l_live_totable); lua_setfield(L, -2, "totable"); + lua_pop(L, 1); +} + +static void pb_lua_push_live(lua_State *L, void *ptr, const pb_lua_msg_t *m) +{ + pb_lua_live_t *u = (pb_lua_live_t *)lua_newuserdata(L, sizeof *u); + u->ptr = ptr; + u->msg = m; + pb_lua_live_open(L); /* 确保 metatable 已注册(会 pop 掉) */ + luaL_getmetatable(L, PB_LIVE_MT); /* 重新从 registry 取出 */ + lua_setmetatable(L, -2); +} + +/* 字段优先,方法 fallback;未知键报错(与 table 路径同文案) + * MESSAGE 字段: Mutable 语义 — 始终返回嵌套 live 视图, 首次进入置 has_ + * (否则 p.Sub.Field = x 在零初始化上必炸; 与 C++ MutableMessage 对齐)。 */ +static int l_live_index(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + const char *key = luaL_checkstring(L, 2); + const pb_lua_field_t *f = pb_lua_find_field(u->msg, key); + + if (f != NULL) { + if (f->ltype == PB_LUA_T_MESSAGE) { + /* 嵌套消息可导航; 首次进入置 has_, encode 才会带上 */ + if (f->has_offset >= 0) { + bool has; + memcpy(&has, (char *)u->ptr + f->has_offset, sizeof(has)); + if (!has) { + has = true; + memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has)); + } + } + pb_lua_push_live(L, (char *)u->ptr + f->offset, + (const pb_lua_msg_t *)f->aux); + return 1; + } + if (!pb_lua_field_present(u->ptr, f)) { + lua_pushnil(L); + return 1; + } + pb_lua_get_one(L, u->ptr, f); + return 1; + } + + lua_getmetatable(L, 1); + lua_getfield(L, -1, key); + if (!lua_isnil(L, -1)) + return 1; + return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name); +} + +/* nil → 清 OPTIONAL 的 has_(SINGULAR 报错);其余走 set_one(自动置 has_=true) */ +static int l_live_newindex(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + const char *key = luaL_checkstring(L, 2); + const pb_lua_field_t *f = pb_lua_find_field(u->msg, key); + + if (f == NULL) + return luaL_error(L, "unknown field '%s' in %s", key, u->msg->name); + + if (lua_isnil(L, 3)) { + bool has = false; + if (f->has_offset < 0) + return luaL_error(L, "field '%s' in %s: SINGULAR field cannot be cleared", + key, u->msg->name); + memcpy((char *)u->ptr + f->has_offset, &has, sizeof(has)); + return 0; + } + + pb_lua_set_one(L, 3, f, u->ptr); + return 0; +} + +static int l_live_tostring(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + lua_pushfstring(L, "PB_LIVE(%s): %p", u->msg->name, u->ptr); + return 1; +} + +static int l_live_dump(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + pb_lua_dump_msg(u->ptr, u->msg, 0); + return 0; +} + +/* 直接从 C 内存编码,不经 table 往返 */ +static int l_live_encode(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + + if (u->msg->c_size > PB_LUA_MAX_STRUCT || u->msg->max_encoded > PB_LUA_MAX_ENCODED) + return luaL_error(L, "%s: message too large for static buffer", u->msg->name); + + pb_byte_t buf[PB_LUA_MAX_ENCODED]; + pb_ostream_t os = pb_ostream_from_buffer(buf, u->msg->max_encoded); + if (!pb_encode(&os, u->msg->msg, u->ptr)) + return luaL_error(L, "%s encode failed: %s", u->msg->name, PB_GET_ERROR(&os)); + + lua_pushlstring(L, (const char *)buf, os.bytes_written); + return 1; +} + +static int l_live_totable(lua_State *L) +{ + pb_lua_live_t *u = (pb_lua_live_t *)luaL_checkudata(L, 1, PB_LIVE_MT); + pb_lua_msg_to_table(L, u->ptr, u->msg); + return 1; +} + +int l_pb_ref(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + for (size_t i = 0; i < g_pb_nbindings; i++) { + if (strcmp(g_pb_bindings[i].name, name) == 0) { + pb_lua_push_live(L, g_pb_bindings[i].ptr, g_pb_bindings[i].msg); + return 1; + } + } + return luaL_error(L, "unknown pb global '%s'", name); +} + +void pb_lua_open(lua_State *L) +{ + pb_lua_live_open(L); + if (pb_lua_bind("CV", &g_stCV, "CV_struct_define") != 0) + luaL_error(L, "pb_lua_open: bind failed"); +} + +/*====================================================================== + * Lua 全局函数 + *====================================================================*/ + +int l_pb_encode(lua_State *L) +{ + const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); + luaL_checktype(L, 2, LUA_TTABLE); + + if (m->c_size > PB_LUA_MAX_STRUCT || m->max_encoded > PB_LUA_MAX_ENCODED) + return luaL_error(L, "%s: message too large for static buffer", m->name); + + char msg[PB_LUA_MAX_STRUCT]; + memset(msg, 0, m->c_size); + pb_lua_table_to_msg(L, 2, m, msg); + + pb_byte_t buf[PB_LUA_MAX_ENCODED]; + pb_ostream_t os = pb_ostream_from_buffer(buf, m->max_encoded); + if (!pb_encode(&os, m->msg, msg)) + return luaL_error(L, "%s encode failed: %s", m->name, PB_GET_ERROR(&os)); + + lua_pushlstring(L, (const char *)buf, os.bytes_written); + return 1; +} + +int l_pb_decode(lua_State *L) +{ + const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); + size_t len; + const pb_byte_t *data = (const pb_byte_t *)luaL_checklstring(L, 2, &len); + + if (m->c_size > PB_LUA_MAX_STRUCT) + return luaL_error(L, "%s: message too large for static buffer", m->name); + + char msg[PB_LUA_MAX_STRUCT]; + memset(msg, 0, m->c_size); + + pb_istream_t is = pb_istream_from_buffer(data, len); + if (!pb_decode(&is, m->msg, msg)) + return luaL_error(L, "%s decode failed: %s", m->name, PB_GET_ERROR(&is)); + + pb_lua_msg_to_table(L, msg, m); + return 1; +} + +int l_pb_dump(lua_State *L) +{ + const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); + + { + pb_lua_live_t *u = (pb_lua_live_t *)luaL_testudata(L, 2, PB_LIVE_MT); + if (u != NULL) { + if (u->msg != m) + return luaL_error(L, "pb_dump: type mismatch: %s vs %s", + m->name, u->msg->name); + pb_lua_dump_msg(u->ptr, u->msg, 0); + return 0; + } + } + + if (lua_type(L, 2) == LUA_TSTRING) { + size_t len; + const pb_byte_t *data = (const pb_byte_t *)lua_tolstring(L, 2, &len); + + if (m->c_size > PB_LUA_MAX_STRUCT) { + lua_print("pb_dump: %s too large\n", m->name); + return 0; + } + char msg[PB_LUA_MAX_STRUCT]; + memset(msg, 0, m->c_size); + + pb_istream_t is = pb_istream_from_buffer(data, len); + if (!pb_decode(&is, m->msg, msg)) { + lua_print("pb_dump: %s decode failed: %s\n", m->name, PB_GET_ERROR(&is)); + pb_lua_dump_hex(data, len); + return 0; + } + pb_lua_dump_msg(msg, m, 0); + } + else if (lua_type(L, 2) == LUA_TTABLE) { + pb_lua_dump_table(L, 2, m, 0); + } + else { + return luaL_error(L, "pb_dump: expected binary string, table, or pb_ref userdata"); + } + return 0; +} + +/* 全零模板:所有字段都置初值(嵌套消息递归展开),方便直接填值编码发送。 + * 字段留在表里即视为 present;不想发到线上的字段设为 nil 即可。 */ +static void pb_lua_new_table(lua_State *L, const pb_lua_msg_t *m) +{ + lua_createtable(L, (int)m->nfields, 0); + for (size_t i = 0; i < m->nfields; i++) { + const pb_lua_field_t *f = &m->fields[i]; + switch (f->ltype) { + case PB_LUA_T_BOOL: + lua_pushboolean(L, 0); + break; + case PB_LUA_T_INT32: + case PB_LUA_T_INT64: + lua_pushinteger(L, 0); + break; + case PB_LUA_T_DOUBLE: + lua_pushnumber(L, 0); + break; + case PB_LUA_T_STRING: + lua_pushstring(L, ""); + break; + case PB_LUA_T_MESSAGE: + pb_lua_new_table(L, (const pb_lua_msg_t *)f->aux); + break; + default: + lua_pushnil(L); + break; + } + lua_setfield(L, -2, f->name); + } +} + +int l_pb_new(lua_State *L) +{ + const pb_lua_msg_t *m = pb_lua_find_msg(L, luaL_checkstring(L, 1)); + pb_lua_new_table(L, m); + return 1; +} +#endif diff --git a/lua/pb_lua.h b/lua/pb_lua.h new file mode 100644 index 0000000..04c34e9 --- /dev/null +++ b/lua/pb_lua.h @@ -0,0 +1,53 @@ +/****************************************************************************** + + 版权所有 (C), 2018-2099, Radkil + + ****************************************************************************** + 文 件 名 : pb_lua.h + 版 本 号 : 初稿 + 功 能 描 述 : nanopb 消息的 Lua 序列化/反序列化桥接接口 + + ******************************************************************************/ + +#ifndef __PB_LUA_H__ +#define __PB_LUA_H__ + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef BUILD_PROTOBUF +#include "lua.h" + +/* + * Lua 全局函数(由 lacmd.c 的 luaopen_default 注册): + * pb_encode("TPokemonPV", t) -> binary string + * pb_decode("TPokemonPV", bin) -> table + * pb_dump("TPokemonPV", bin|table) -- pretty print + * pb_new("TPokemonPV") -> zero-filled table (fill & encode) + * pb_ref("CV") -> live userdata view of a bound C global + * p.Field / p.Field = v -- read/write through to C memory + * p.Sub.Field = 10 -- 嵌套 MESSAGE: Mutable 语义, 首次进入置 has_ + * p:dump() / p:encode() / p:totable() + * + * C 函数名带 l_ 前缀,避开 nanopb 导出的 pb_encode/pb_decode 符号。 + */ +int l_pb_encode(lua_State *L); +int l_pb_decode(lua_State *L); +int l_pb_dump(lua_State *L); +int l_pb_new(lua_State *L); +int l_pb_ref(lua_State *L); + +/* C 侧注册 API:把命名指针绑定到消息类型名(按 type name 查描述符)。 + * 幂等:同名重复绑定更新指针。0=OK, -1=未知类型, -2=注册表满。 + * 供业务模块在自身初始化处调用,不依赖 lua_State。 */ +int pb_lua_bind(const char *name, void *ptr, const char *msgtype); + +/* 每个 lua_State 一次(luaopen_default 调用): + * 创建 PB_LIVE metatable + 绑定 demo 全局 g_pokemon_pv。幂等。 */ +void pb_lua_open(lua_State *L); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* __PB_LUA_H__ */ diff --git a/lua/pb_lua_desc.h b/lua/pb_lua_desc.h new file mode 100644 index 0000000..9a3d580 --- /dev/null +++ b/lua/pb_lua_desc.h @@ -0,0 +1,123 @@ +/****************************************************************************** + + 版权所有 (C), 2018-2099, Radkil + + ****************************************************************************** + 文 件 名 : pb_lua_desc.h + 功能描述 : pb_lua 消息描述符类型 + X-macro。 + 由 pb_lua.c 与生成的 pb_lua_registry.c 共享。 + 字段表内容随 Type_FIELDLIST 自动; 消息注册由生成器维护。 + + ******************************************************************************/ + +#ifndef __PB_LUA_DESC_H__ +#define __PB_LUA_DESC_H__ + +#include +#include +#include +#include "pb.h" + +typedef enum { + PB_LUA_T_BOOL = 0, + PB_LUA_T_INT32, + PB_LUA_T_INT64, + PB_LUA_T_DOUBLE, + PB_LUA_T_STRING, + PB_LUA_T_MESSAGE, + PB_LUA_T_UNSUPPORTED +} pb_lua_ftype_t; + +typedef struct { + const char *name; + uint8_t tag; + uint8_t ltype; /* pb_lua_ftype_t */ + uint16_t offset; + int16_t has_offset; /* -1 = 无 has_ */ + uint16_t size; + const void *aux; /* MESSAGE→pb_lua_msg_t*; 否则 NULL */ +} pb_lua_field_t; + +typedef struct pb_lua_msg { + const char *name; + const pb_msgdesc_t *msg; + size_t c_size; + size_t max_encoded; + const pb_lua_field_t *fields; + size_t nfields; +} pb_lua_msg_t; + +/*── X-macro: 由 Type_FIELDLIST 生成字段表 ────────────────────── */ + +#define PB_LUA_MSGREF_(type) (&type##_lua_desc) +#define PB_LUA_MSGREF(type) PB_LUA_MSGREF_(type) + +#define PB_LUA_HAS_OPTIONAL(a, f) ((int16_t)offsetof(a, has_##f)) +#define PB_LUA_HAS_REQUIRED(a, f) ((int16_t)-1) +#define PB_LUA_HAS_SINGULAR(a, f) ((int16_t)-1) +#define PB_LUA_HAS_REPEATED(a, f) ((int16_t)-1) + +#define PB_LUA_AUX_NULL(a, f) NULL +#define PB_LUA_AUX_MESSAGE(a, f) PB_LUA_MSGREF(a##_##f##_MSGTYPE) +#define PB_LUA_AUX_MSG_W_CB(a, f) PB_LUA_AUX_MESSAGE(a, f) +#define PB_LUA_AUX_ENUM(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_UENUM(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_BOOL(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_INT32(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_INT64(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_DOUBLE(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_FLOAT(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_STRING(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_BYTES(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_UINT32(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_UINT64(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_SINT32(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_SINT64(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_FIXED32(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_FIXED64(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_SFIXED32(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_SFIXED64(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_EXTENSION(a, f) PB_LUA_AUX_NULL(a, f) +#define PB_LUA_AUX_FIXED_LENGTH_BYTES(a, f) PB_LUA_AUX_NULL(a, f) + +#define PB_LUA_FT_BOOL PB_LUA_T_BOOL +#define PB_LUA_FT_INT32 PB_LUA_T_INT32 +#define PB_LUA_FT_INT64 PB_LUA_T_INT64 +#define PB_LUA_FT_DOUBLE PB_LUA_T_DOUBLE +#define PB_LUA_FT_FLOAT PB_LUA_T_DOUBLE +#define PB_LUA_FT_ENUM PB_LUA_T_INT32 +#define PB_LUA_FT_UENUM PB_LUA_T_INT32 +#define PB_LUA_FT_STRING PB_LUA_T_STRING +#define PB_LUA_FT_MESSAGE PB_LUA_T_MESSAGE +#define PB_LUA_FT_MSG_W_CB PB_LUA_T_MESSAGE +#define PB_LUA_FT_BYTES PB_LUA_T_UNSUPPORTED +#define PB_LUA_FT_UINT32 PB_LUA_T_INT32 +#define PB_LUA_FT_UINT64 PB_LUA_T_INT64 +#define PB_LUA_FT_SINT32 PB_LUA_T_INT32 +#define PB_LUA_FT_SINT64 PB_LUA_T_INT64 +#define PB_LUA_FT_FIXED32 PB_LUA_T_INT32 +#define PB_LUA_FT_FIXED64 PB_LUA_T_INT64 +#define PB_LUA_FT_SFIXED32 PB_LUA_T_INT32 +#define PB_LUA_FT_SFIXED64 PB_LUA_T_INT64 +#define PB_LUA_FT_EXTENSION PB_LUA_T_UNSUPPORTED +#define PB_LUA_FT_FIXED_LENGTH_BYTES PB_LUA_T_UNSUPPORTED + +#define PB_LUA_EMIT(a, atype, htype, ltype, name, tag) \ + { #name, (uint8_t)(tag), (uint8_t)PB_LUA_FT_##ltype, \ + (uint16_t)offsetof(a, name), \ + PB_LUA_HAS_##htype(a, name), \ + (uint16_t)sizeof(((a *)0)->name), \ + PB_LUA_AUX_##ltype(a, name) }, + +#define PB_LUA_FIELDS(Struct) Struct##_FIELDLIST(PB_LUA_EMIT, Struct) + +#define PB_LUA_MSGDESC(Struct, StructName, MaxEnc) \ + { StructName, &Struct##_msg, sizeof(Struct), (MaxEnc), \ + Struct##_fields_lua, \ + sizeof(Struct##_fields_lua) / sizeof(Struct##_fields_lua[0]) } + +/* ── 全局注册表(生成的 pb_lua_registry.c 强符号定义) ─────────── */ +extern const pb_lua_msg_t *const g_pb_msgs[]; +extern const int g_pb_msgs_count; + +#endif /* __PB_LUA_DESC_H__ */ diff --git a/lua/pb_lua_registry.c b/lua/pb_lua_registry.c new file mode 100644 index 0000000..23edea0 --- /dev/null +++ b/lua/pb_lua_registry.c @@ -0,0 +1,24 @@ +/* Automatically generated by protoc-gen-proto_ui --pb_lua_registry. DO NOT EDIT. */ +#ifdef BUILD_PROTOBUF +#include "pb_lua_desc.h" +#include "bsp_CV.pb.h" + +static const pb_lua_field_t PID_Parameters_fields_lua[] = { + PB_LUA_FIELDS(PID_Parameters) +}; + +static const pb_lua_msg_t PID_Parameters_lua_desc = + PB_LUA_MSGDESC(PID_Parameters, "PID_Parameters", PID_Parameters_size); + +static const pb_lua_field_t CV_struct_define_fields_lua[] = { + PB_LUA_FIELDS(CV_struct_define) +}; + +static const pb_lua_msg_t CV_struct_define_lua_desc = + PB_LUA_MSGDESC(CV_struct_define, "CV_struct_define", CV_struct_define_size); + +const pb_lua_msg_t *const g_pb_msgs[] = { + &PID_Parameters_lua_desc, &CV_struct_define_lua_desc, +}; +const int g_pb_msgs_count = (int)(sizeof(g_pb_msgs)/sizeof(g_pb_msgs[0])); +#endif