增加网络配置界面
This commit is contained in:
+43
-12
@@ -3,13 +3,16 @@
|
||||
"""生成菜单节点表 ui_menu_tab.c / ui_menu_tab.h(扁平"父子索引表",输出到 Drivers/BSP/UI/)。
|
||||
|
||||
把"菜单长什么样"声明在缩进文本源 ui_menu_tree.txt(缩进即层级,每级 2 空格,
|
||||
`#` 开头为注释)。本脚本读源、推导每个节点的 parent 下标、产出 C 表,供 ui_menu.c 消费。
|
||||
`#` 开头为注释;行尾可选 ` #P` = 该叶子需密码(密码页按所输密码授权:查看/修改);
|
||||
密码值在 Drivers/BSP/UI/ui_pwd_def.h 编译期配置)。
|
||||
本脚本读源、推导每个节点的 parent 下标、产出 C 表,供 ui_menu.c 消费。
|
||||
改菜单只需编辑 ui_menu_tree.txt 后重跑本脚本(与字体 ui_cn_chars.txt -> make_font.py 同模式)。
|
||||
|
||||
用法(仓库根目录或任意目录均可,脚本自动定位仓库根):
|
||||
python tools/menu_gen/gen_menu.py
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
@@ -36,20 +39,27 @@ def parse_tree(text):
|
||||
if '\t' in line:
|
||||
sys.exit('ERROR: 请用空格缩进(勿用 Tab): ' + repr(raw))
|
||||
indent = len(line) - len(line.lstrip(' '))
|
||||
lines.append((indent, stripped))
|
||||
name, pwd = stripped, None
|
||||
m = re.match(r'^(.*?)[ \t]+#([A-Za-z0-9]+)$', stripped)
|
||||
if m:
|
||||
if m.group(2) != 'P':
|
||||
sys.exit('ERROR: 行尾注记仅支持 #P(需密码): %r(出现 #%s;旧 #V/#M 类别注记已废弃,'
|
||||
'密码值改在 ui_pwd_def.h 配置)' % (raw, m.group(2)))
|
||||
name, pwd = m.group(1).strip(), True
|
||||
lines.append((indent, name, pwd))
|
||||
|
||||
if not lines:
|
||||
sys.exit('ERROR: ui_menu_tree.txt 无有效节点')
|
||||
|
||||
indents = sorted(set(i for i, _ in lines if i > 0))
|
||||
indents = sorted(set(i for i, _, _ in lines if i > 0))
|
||||
unit = indents[0] if indents else 1
|
||||
for i, name in lines:
|
||||
for i, name, _ in lines:
|
||||
if i % unit != 0:
|
||||
sys.exit('ERROR: 缩进不是 %d 的整数倍: %r (indent=%d)' % (unit, name, i))
|
||||
|
||||
nodes = []
|
||||
stack = [] # [(depth, index), ...] 当前各层的父节点下标
|
||||
for indent, name in lines:
|
||||
for indent, name, pwd in lines:
|
||||
depth = indent // unit
|
||||
if depth > MAX_DEPTH:
|
||||
sys.exit('ERROR: 层级过深(>%d): %r' % (MAX_DEPTH, name))
|
||||
@@ -57,10 +67,10 @@ def parse_tree(text):
|
||||
stack.pop()
|
||||
parent = stack[-1][1] if stack else ROOT_ID
|
||||
idx = len(nodes)
|
||||
nodes.append((parent, name, depth))
|
||||
nodes.append((parent, name, depth, pwd))
|
||||
stack.append((depth, idx))
|
||||
|
||||
roots = [i for i, (_, _, d) in enumerate(nodes) if d == 0]
|
||||
roots = [i for i, (_, _, d, _) in enumerate(nodes) if d == 0]
|
||||
if len(roots) != 1:
|
||||
sys.exit('ERROR: 必须有且仅有一个根节点(depth 0),当前 %d 个: %s'
|
||||
% (len(roots), [nodes[i][1] for i in roots]))
|
||||
@@ -70,7 +80,7 @@ def parse_tree(text):
|
||||
def emit(nodes):
|
||||
n = len(nodes)
|
||||
menus = sum(1 for i in range(n) if any(nodes[j][0] == i for j in range(n)))
|
||||
max_depth = max(d for _, _, d in nodes)
|
||||
max_depth = max(d for _, _, d, _ in nodes)
|
||||
|
||||
h = [
|
||||
'/* 本文件由 tools/menu_gen/gen_menu.py 从 ui_menu_tree.txt 自动生成,请勿手改。',
|
||||
@@ -85,8 +95,10 @@ def emit(nodes):
|
||||
'#define MENU_ROOT 0xFFu /* 根哨兵:仅根节点(主界面)的 parent 取此值;8 位,上限 255 项 */',
|
||||
'',
|
||||
'/* 菜单节点:子项 = parent 等于本下标的所有行(按表序);有子项=菜单,无=叶子 */',
|
||||
'/* 字段序 parent/pwd 在前:32 位下两项 8 位合并 + 4 位指针 = 8 B/项 */',
|
||||
'typedef struct {',
|
||||
' uint8_t parent; /* 父节点下标(0..254);根为 MENU_ROOT(0xFF) */',
|
||||
' uint8_t pwd; /* 1 = 需密码(密码页按所输密码授权);0 = 直接进(只读) */',
|
||||
' const char *name; /* 显示文本(字面量,存 Flash) */',
|
||||
'} ui_menu_node_t;',
|
||||
'',
|
||||
@@ -104,7 +116,8 @@ def emit(nodes):
|
||||
'uint8_t ui_menu_tab_children(uint8_t node, uint8_t out[], uint8_t max); /* 取 node 直接子项(按表序),最多 max 个;返回实际个数 */',
|
||||
'bool ui_menu_tab_has_children(uint8_t node); /* 有子项=菜单(带▶);无=叶子 */',
|
||||
'const char *ui_menu_tab_name(uint8_t node); /* 显示名 */',
|
||||
'bool ui_menu_tab_valid(void); /* 启动自检:非根 parent 下标均有效 */',
|
||||
'bool ui_menu_tab_has_pwd(uint8_t node); /* 需密码(密码页按所输密码授权);false = 直接进(只读) */',
|
||||
'bool ui_menu_tab_valid(void); /* 启动自检:非根 parent 下标均有效、pwd 为 0/1 */',
|
||||
'',
|
||||
'#endif',
|
||||
'',
|
||||
@@ -118,9 +131,10 @@ def emit(nodes):
|
||||
'',
|
||||
'const ui_menu_node_t g_menu_tab[UI_MENU_TAB_COUNT] = {',
|
||||
]
|
||||
for i, (parent, name, depth) in enumerate(nodes):
|
||||
for i, (parent, name, depth, pwd) in enumerate(nodes):
|
||||
pstr = 'MENU_ROOT' if parent == ROOT_ID else '%d' % parent
|
||||
c.append(' { %8s, "%s" }, /* %2d L%d */' % (pstr, name, i, depth))
|
||||
cstr = '1u' if pwd else '0u'
|
||||
c.append(' { %8s, %2s, "%s" }, /* %2d L%d */' % (pstr, cstr, name, i, depth))
|
||||
c.append('};')
|
||||
c.append('')
|
||||
c += [
|
||||
@@ -174,6 +188,18 @@ def emit(nodes):
|
||||
'}',
|
||||
'',
|
||||
'/*',
|
||||
' * 函数功能:判断节点是否需密码(叶子密码保护)',
|
||||
' * 入口参数:node - 节点下标 uint8_t',
|
||||
' * 返回值:true - 需密码页(按所输密码授权查看/修改权限);false - 直接进(只读)',
|
||||
' * 限定条件:无',
|
||||
' * 说明:表 pwd 列取自源 ui_menu_tree.txt 行尾 #P 注记;导航逻辑层据此决定是否先经密码页',
|
||||
' */',
|
||||
'bool ui_menu_tab_has_pwd(uint8_t node)',
|
||||
'{',
|
||||
' return (g_menu_tab[node].pwd != 0u);',
|
||||
'}',
|
||||
'',
|
||||
'/*',
|
||||
' * 函数功能:菜单表启动自检',
|
||||
' * 入口参数:无',
|
||||
' * 返回值:true - 表有效;false - 存在越界 parent(表内笔误)',
|
||||
@@ -190,6 +216,9 @@ def emit(nodes):
|
||||
' if (p != MENU_ROOT && p >= UI_MENU_TAB_COUNT) {',
|
||||
' return false;',
|
||||
' }',
|
||||
' if (g_menu_tab[i].pwd > 1u) {',
|
||||
' return false; /* pwd 只取 0/1(行尾注记笔误捕获) */',
|
||||
' }',
|
||||
' }',
|
||||
' return true;',
|
||||
'}',
|
||||
@@ -213,7 +242,9 @@ def main():
|
||||
fp.write(header)
|
||||
with open(OUT_C, 'w', encoding='utf-8-sig', newline='') as fp:
|
||||
fp.write(body)
|
||||
print('nodes=%d menu=%d leaf=%d max_depth=%d' % (len(nodes), menus, len(nodes) - menus, max_depth))
|
||||
protected = sum(1 for _, _, _, p in nodes if p)
|
||||
print('nodes=%d menu=%d leaf=%d protected=%d max_depth=%d'
|
||||
% (len(nodes), menus, len(nodes) - menus, protected, max_depth))
|
||||
print('->', os.path.relpath(OUT_H, ROOT))
|
||||
print('->', os.path.relpath(OUT_C, ROOT))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user