Browse Source

增加在lua终端操作CV的功能

master
Lizongdi 2 weeks ago
parent
commit
803a7ab478
  1. 12
      RBcore/drv_interface.c
  2. 10
      bspMCU/l_stm32.c
  3. 22
      lua/CMakeLists.txt
  4. 774
      lua/pb_lua.c
  5. 53
      lua/pb_lua.h
  6. 123
      lua/pb_lua_desc.h
  7. 24
      lua/pb_lua_registry.c

12
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());
}

10
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;

22
lua/CMakeLists.txt

@ -19,3 +19,25 @@ if(TARGET platform_Linux)
else()
message(WARNING "[lua] Dependency 'platform_Linux' not found.")
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()

774
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 <string.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#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

53
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__ */

123
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 <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#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__ */

24
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
Loading…
Cancel
Save