一、任务概述
新增功能:读取主控面板 board 按钮的 BSP 节点,解析按钮是否按下(0/1),并以可读文本打印到日志。
- BSP 节点:
/sys/class/misc‑board//handle_button - 含义:
0 = 未按下(released),1 = 按下(pressed) - 要求:只做解析 + 打印日志(暂不做事件外发)
二、任务分析
1. 仿照已有代码,降低风险
项目里已有类似 "读 BSP 节点‑解析" 的成熟示例(如update_backlight_running_hour读取背光运行时间),仿照其模式,保证代码风格一致、符合项目编码规范。
2. 调用入口(秒级定时)
这个功能需要周期性读取,而update_system_service_info()是每秒被定时调用的函数,专门负责采集各类系统状态。
- 新增一个函数:
update_board_button_status() - 在
update_system_service_info()里面调用该函数
3. 理解事件外发机制
虽然这次只做打印,但需要理解完整事件收发链路:
后端读到值 → 构造 CoreEvent (type+id+payload) → dispatch_event 发出 → CoreEventHub 按 type 自动路由 → 前端 (ClientProxy) 监听对应 id → 前端拿取数据
type:事件发给哪类组件,例如EV_TYPE_LOCAL_CLIENT_PROXY发往前端id:具体什么事件(枚举,例EV_MCP_UPDATE_BACKLIGHT_RUNNING_HOUR)payload:携带的数据,std::any,可以存放任意类型数据- 监听、业务处理逻辑,全部由接收方(前端)负责;发送方只管把事件发出去。
边界:后端只管dispatch_event发出去,后端路由不用管;前端能不能真正拿到,取决于前端是否监听这个 id。如果是全新事件,前端还需要配合增加监听。
4. 新事件需要做两步
如果后续要做事件外发新事件:
- 在
eventdefine.h的CoreEventID枚举里新增 id - 前端配合监听该 id
本次任务只做日志打印,不需要上面两步。
三、代码实现
1. 定义路径常量(规范:不裸写字符串)
仿照背光的BACKLIGHT_BSP_FILE_PATH,把路径抽成常量,不直接裸写字符串,方便后续统一维护。
// 面板按钮BSP文件路径
static const char* const BOARD_BUTTON_BSP_FILE_PATH =
"/sys/class/misc‑board/arcadia/handle_button";
2. 实现函数(核心逻辑)
/**
* @fn void update_board_button_status()
* @brief 面板按钮状态
* @details 读取BSP节点,0=未按下(released),1=按下(pressed)
*/
void SystemManager::update_board_button_status() {
int button_state = -1; // 初始‑1作为"无效值"兜底
std::ifstream file(BOARD_BUTTON_BSP_FILE_PATH); // 打开BSP节点文件
if (!file.is_open()) { // 打开失败
SYSMGR_WARN("open file :{} failed!", BOARD_BUTTON_BSP_FILE_PATH);
return;
}
file >> button_state; // 从文件读取按钮值
if (file.fail()) { // 读取出错(非数字)
SYSMGR_INFO("{} read board button status failed, not number!",
BOARD_BUTTON_BSP_FILE_PATH);
file.close();
return;
}
file.close(); // 关闭文件
if (button_state == -1) { // 值无效
return;
}
// 0/1 转可读文本
const char* button_state_str =
(button_state == 0) ? "released" : "pressed";
SYSMGR_INFO("board button status:{}", button_state_str); // 打印日志
}
3. 头文件 (.h) 声明 + cpp (.cpp) 调用
.h 头文件声明
void update_board_button_status();
.cpp 调用(放在 update_system_service_info 里面)
void SystemManager::update_system_service_info() {
...
update_backlight_running_hour();
update_board_button_status(); // 新增,每秒调用一次
...
}
四、关键知识点
1. 读取 BSP(内核驱动节点)
应用层不需要关心驱动底层实现,但必须清楚文件路径与文件内数据格式。
file >> value;从 sysfs 文件读取一个数值file.is_open()判断文件是否打开成功file.fail()判断读取操作是否失败
2. 命名规范
- 函数名、路径常量名语义保持一致;本次全部使用
board,和 BSP 节点handle_button/misc‑board对应,避免命名前后矛盾。 - 常量名具备可读性:
BOARD_BUTTON_BSP_FILE_PATH一眼看懂含义。 - sysfs 文件路径抽成常量,禁止代码内裸写字符串。
3. 日志可读性
- 不直接打印原始数字 0/1,转换为
released/pressed可读文本再输出。 - 日志统一使用英文,规避中文编码乱码问题。
- 错误日志带上文件路径,方便问题定位。
4. 错误兜底(健壮性)
每一步增加异常保护分支:
- 文件打开失败:输出警告日志 + return 退出
- 文件读取失败(读到非数字):输出日志 + return 退出
- 读到无效值 (-1):直接 return 退出
- 变量初始化为‑1,规避未初始化变量风险
5. Unused variable 编译警告
变量声明之后,如果没有读取、打印、使用,编译器报Unused variable警告。 把button_state用于日志打印,警告就会消失。
五、编译 / 部署 / 调试 / 命令
编译
./build.sh -p <> -m <>
注意使用正确平台参数,避免工具链不一致,出现
error adding symbols: no more archived files链接错误。
部署(NFS 共享方式)
# 设备挂载电脑NFS共享目录
mount -t nfs -o nolock <服务器IP>:/NFS /mnt
# 将编译产物拷贝到设备运行目录
cp /mnt/nxcore‑.../bin/* /home/nx/bin/ -r
重启服务
nxcore 直接执行 restart 经常会在 stop 阶段卡死挂起;推荐 kill 强杀再启动
systemctl kill -s SIGKILL nxcore
systemctl start nxcore
systemctl status nxcore --no-pager | head -5
查看日志
# 实时跟踪日志 + 过滤按钮相关日志
journalctl -u nxcore -f | grep "board button"
journalctl -u nxcore -f:实时查看 nxcore 服务完整日志grep "board button":只过滤按钮相关输出
预期日志输出:
board button status:released # 未按下
board button status:pressed # 按下
board button status:released # 松开
六、Git 提交流程
标准操作流程
#1. 查看状态
git branch
git status
git remote -v
#2. 从主干切功能分支
git checkout -b board-button upstream/dev‑xxx
#3. 暂存业务代码(不要把build产物提交)
git add src/.../systemmanager.h src/.../systemmanager.cpp
#4. 首次配置git身份
git config --global user.email "..."
git config --global user.name "..."
#5. 项目脚本规范提交
./commit.sh # 选 1(feat)/1(需求变更)/模块/机型(Arcadia)
#6. 推送远程分支
git push origin HEAD:board-button
误删代码恢复技巧
git fetch upstream #拉取远程,不会改动本地文件
git show upstream/dev‑xxx:路径 | sed -n 'xx,yyp' #查看远程文件指定行
对照远程版本,只补回被误删的对应函数片段,不要整体 checkout 覆盖整个文件,防止冲掉自己新增代码。 经验:
git fetch只拉取远程代码,不会修改本地工作区;不要直接 checkout 完整文件,会覆盖本地改动。
七、踩坑记录
| 问题 | 原因 | 解决办法 |
|---|---|---|
| CI Pipeline failed | 链接库损坏 / 编译工具链版本不一致 | 查看 CI 日志;重建损坏库,使用项目正确构建命令 |
| 命名不统一(函数 board / 常量 panel) | 编码时命名没有保持语义统一 | 全部统一使用同一语义,本任务统一用 board |
| Unused variable button_state | 变量读取之后没有打印使用 | 增加SYSMGR_INFO打印该变量 |
| 日志输出中文 | 编码、项目日志规范约束 | 全部日志使用英文输出 |
| 服务重启卡住 | nxcore stop 阶段挂起 | kill -s SIGKILL强杀进程,再 start 启动 |
| 误删原有业务逻辑 | 修改代码时误删原有逻辑 | git fetch + git show对照远程版本,局部补回代码 |
八、总结与心得
- 仿照已有成熟代码最稳妥:项目存在同类业务代码,优先直接套用现有实现模式,代码风格、异常健壮性一步到位。
- 命名一致性:函数名、常量名语义要统一,避免自相矛盾。
- 路径抽常量:sysfs 文件路径不要裸写字符串,便于后期维护修改。
- 可读性输出:原始数值转换为可读文本之后再打印日志,不要直接输出 0/1。
- 事件机制边界理解:后端只负责 dispatch_event 发出事件;接收、监听逻辑全部交给前端;新增事件,枚举定义和前端监听两者都要配套。
- 分清职责边界:后端只管发事件,前端自己监听、解析数据,后端不用写前端业务代码。
- 区分环境问题和代码问题:CI 失败、链接库损坏、服务重启卡住,很多属于环境问题,先确认不是自己业务代码错误。
这份笔记覆盖 board 按钮读取任务,附带事件机制、Git、CI、部署排错完整流程,命令均为通用 Linux/Git 命令,仅供学习,无公司业务机密,可以作为个人实习学习笔记,无盈利性。