第一步:把 PDF 或 Excel 寄存器表转成CSV
先人工用 Python 脚本(或者 WPS 另存为)把寄存器表转成统一 CSV,列名固定:`register_addr, register_name, register_type, enum_values, description`保持数据干净。
cpp
# 假设你已经从 PDF 复制粘贴到了 excel.csv
import pandas as pd
df = pd.read_excel("modbus_table.xlsx")
# 清洗:去空行、去注释、统一列名
df = df.dropna(subset=["register_addr"])
df.to_csv("modbus_clean.csv", index=False)
print(f"清洗完成,共 {len(df)} 个寄存器")
注意:现场表格里经常有合并单元格,地址列是空的,但下面几行都引用。清洗时务必 `fillna(method='ffill')`,否则 AI 拿到错地址,生成的代码会出错。
第二步:写个 Prompt,让 DeepSeek产生Qt 头文件
把 prompt 写成「合同」,清晰列出输入 CSV 路径、输出头文件格式、枚举命名规则、结构体对齐方式。以下是稳定的 prompt 模板:
cpp
// 你的项目里放个 prompt_template.txt
// 内容如下(用你实际 CSV 路径替换):
/*
你是 Qt 嵌入式专家。读取 modbus_clean.csv,生成 modbus_regs.h。
要求:
1. 每个 register_name 生成一个枚举常量,按地址升序排列。
2. 枚举名用驼峰,前缀 MODBUS_REG_。
3. 每个寄存器生成一个结构体成员,类型 uint16_t,注释写 description。
4. 枚举值与寄存器地址一一对应,禁止跳变。
5. 生成一个寄存表数组,按地址顺序排列。
6. 代码风格:Qt 5.15+,C++17,带 #pragma once。
7. 如果 enum_values 列非空,额外生成一个子枚举,名字为 寄存器名_Value。
*/
把 prompt 和 CSV 一起发给 DeepSeek API,拿到返回的 .h 文件内容。我通常直接存成 `modbus_regs.h`。
第三步:CMake 编译验证脚本
cpp
# CMakeLists.txt 放在同目录下
cmake_minimum_required(VERSION 3.16)
project(modbus_gen_check)
set(CMAKE_CXX_STANDARD 17)
find_package(Qt5 REQUIRED COMPONENTS Core)
add_executable(check_main main.cpp)
target_include_directories(check_main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(check_main Qt5::Core)
cpp
// main.cpp ------ 只包含头文件,静态断言检查枚举连续性
#include "modbus_regs.h"
#include <type_traits>
// 编译期检查:枚举按地址升序,且无重复
static_assert(MODBUS_REG_OFFSET < MODBUS_REG_MODE, "地址乱序!");
static_assert(MODBUS_REG_MODE < MODBUS_REG_STATUS, "地址乱序!");
int main() {
// 故意引用一次,防止编译器优化掉
volatile auto x = sizeof(modbus_reg_table);
return x == 0; // 返回1表示表为空,会报错
}
如果编译出错,把错误信息返 DeepSeek,修改两轮后基本稳定。
注意:如果 AI 生成了大括号初始化数组,检查 C++11 的列表初始化是否满足,别用 C++14 特性,老编译器编译不过。
第四步:集成到 Qt 工程,加互斥锁和缓存,别让 UI 卡死
代码生成验证通过后,别直接裸奔。工业现场,寄存器读得太勤,UI 线程会卡。咱们包一层单例,带 `QReadWriteLock` 和 100ms 缓存,保证 7x24 稳定。
cpp
// ModbusRegCache.h
class ModbusRegCache {
public:
static ModbusRegCache& instance() {
static ModbusRegCache c;
return c;
}
uint16_t read(uint16_t addr) {
QReadLocker locker(&m_lock);
auto it = m_cache.find(addr);
if (it != m_cache.end() && m_lastUpdate.elapsed() < 100) {
return it.value();
}
// 实际走 Modbus 请求,这里省略
uint16_t val = m_modbus->readRegister(addr);
m_cache[addr] = val;
m_lastUpdate.restart();
return val;
}
private:
ModbusRegCache() : m_modbus(nullptr) {}
QModbusClient* m_modbus; // 你自己的通信对象
QHash<uint16_t, uint16_t> m_cache;
QReadWriteLock m_lock;
QElapsedTimer m_lastUpdate;
};
第五步:整个流程挂到 Git Hook 上,以后改文档自动触发
cpp
#!/bin/bash
# auto_gen.sh ------ 一键生成+验证
python3 clean.py && \
python3 call_deepseek.py && \
cd build && cmake .. && make -j4
if [ $? -ne 0 ]; then
echo "🚨 Modbus 头文件生成或编译失败,禁止提交!"
exit 1
fi