22 changed files with 1128 additions and 17 deletions
@ -0,0 +1,15 @@ |
|||||
|
cmake_minimum_required(VERSION 3.10) |
||||
|
|
||||
|
set(COMMON_CMAKE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../CMakeLists.txt") |
||||
|
|
||||
|
if(EXISTS ${COMMON_CMAKE_PATH}) |
||||
|
include(${COMMON_CMAKE_PATH}) |
||||
|
else() |
||||
|
message(FATAL_ERROR "Cannot find common build logic at ${COMMON_CMAKE_PATH}") |
||||
|
endif() |
||||
|
|
||||
|
if(TARGET common) |
||||
|
target_link_libraries(${TARGET_NAME} PUBLIC common) |
||||
|
else() |
||||
|
message(FATAL_ERROR "[${TARGET_NAME}] Dependency 'common' not found.") |
||||
|
endif() |
||||
@ -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)); |
||||
|
print_number_fixed3(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)) |
||||
|
print_number_fixed3((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 |
||||
|
print_number_fixed3((double)lua_tonumber(L, vidx)); |
||||
|
break; |
||||
|
case PB_LUA_T_DOUBLE: |
||||
|
print_number_fixed3((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 |
||||
@ -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__ */ |
||||
@ -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__ */ |
||||
@ -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…
Reference in new issue