内容目录
本文目录
- 1. 1. ⚠️ Flash 地址规划(不看会变砖)
- 2. 2. API 总表
- 3. 3. 数据记录控件「自定义」模式
- 4. 4. 应用:db.lua 句柄封装 + 增删改查
- 5. 4.1 初始化(on_init)
- 6. 4.2 增删改(LW5000 按键分发)
简易数据库(定制固件专属)是一个轻量级、无文件系统的嵌入式数据库:在 Flash 里划一块连续区域,按固定长度记录存结构化数据(参数配置、事件日志、运行记录),掉电不丢,支持运行时按需初始化。数据记录控件用「自定义」数据源模式对接,实现增删改查全流程。
1. ⚠️ Flash 地址规划(不看会变砖)
简易数据库直接操作 Flash,没有文件系统隔离机制,flashaddr 起始地址必须严格避开四类高风险区域:
- 工程资源存储区
- 资料采样块存储区
- 操作记录块存储区
- OTA 升级备份区域
总占用空间 = dataRowSize × maxCount,规划地址时把这段空间算进去,别和别的功能打架。
2. API 总表
| 函数 | 参数 | 说明 |
|---|---|---|
easydb_open(flashaddr, createIfNotExist, dataRowSize, maxCount) |
flashaddr Flash 起始地址;createIfNotExist 0=仅打开/1=不存在则创建;dataRowSize ≥1;maxCount 最大条数 | 打开或创建数据库,返回句柄(非 0 成功,0 失败) |
easydb_close(db) |
句柄 | 关闭数据库,释放资源 |
easydb_get_count(db) |
句柄 | 当前有效记录数 |
easydb_get(db, idx) |
idx 从 0 开始 | 读指定索引记录,成功返回字符串,失败 nil |
easydb_add(db, dataset) |
字符串数据 | 末尾追加一条记录 |
easydb_update(db, idx, dataset) |
idx + 新内容 | 更新指定记录,dataset 长度必须严格等于 dataRowSize |
easydb_del(db, idx) |
idx | 删除指定记录 |
easydb_clear(db) |
句柄 | 清空所有记录 |
3. 数据记录控件「自定义」模式
数据来源设为自定义时,系统不知道有多少行数据,必须由两个回调提供:
- 记录数量回调
on_get_record_count(screen_id, control_id):返回总行数,内部调 easydb_get_count(g_db_handle) - 记录内容回调
on_get_record_row(screen_id, control_id, row):返回第 row 行的字符串内容
function on_get_record_count(screen_id, control_id)
if screen_id == 0 and control_id == 1 then
return db.getCnt(g_db_handle)
end
end
function on_get_record_row(screen_id, control_id, row)
local ret, msg = db.get(g_db_handle, row)
if ret == 0 then
return msg
end
end
控件配置:数据来源=自定义、字符编码=UTF8、翻页控制√、控制地址 LW4000、选中行通知√(LW4004,UINT32)——选中某行时索引写进 LW4004,增删改都靠它定位。
4. 应用:db.lua 句柄封装 + 增删改查
4.1 初始化(on_init)
function on_init()
dofile("db.lua")
_EN_ON_UPDATA_API_ = 0
-- 10MB 起始、不存在则创建、单条1000字节、最多1000条
g_db_handle = db.open(10*1024*1024, 1, 1000, 1000)
if g_db_handle == 0 then
print("ERROR: 无法打开数据库!")
return
end
db.clear(g_db_handle) -- 清空旧数据(可选)
db.add(g_db_handle, "Alice;Engineer;30")
db.add(g_db_handle, "Bob;Technician;25")
db.add(g_db_handle, "Charlie;Manager;40")
db.add(g_db_handle, "Harden;Basketball players;36")
_EN_ON_UPDATA_API_ = 1
end
记录格式「姓名;职业;年龄」,英文分号分隔,和表格控件解析规则一致,直接映射多列。
4.2 增删改(LW5000 按键分发)
function on_update(slave, vtype, addr)
if addr == 0x4004 then -- 选中行通知
local idx = get_uint32(VT_LW, addr)
local ret, msg = db.get(g_db_handle, idx)
if ret == 0 then
local parts = my_split(msg, ";")
if parts ~= nil then
set_uint16(VT_LW, 0x4530, idx) -- 记录当前选中索引
set_string(VT_LW, 0x4500, (parts[1] == nil) and "--" or parts[1])
set_string(VT_LW, 0x4510, (parts[2] == nil) and "--" or parts[2])
set_string(VT_LW, 0x4520, (parts[3] == nil) and "--" or parts[3])
end
show_dialog(1, 232, 33, 50) -- 弹窗显示详情
end
elseif addr == 0x5000 then
local key = get_uint16(VT_LW, addr)
if key == 0 then -- 添加
local name = get_string(VT_LW, 0x5010)
local position = get_string(VT_LW, 0x5020)
local age = get_string(VT_LW, 0x5030)
db.add(g_db_handle, name..";"..position..";"..age..";")
elseif key == 1 then -- 修改(改选中行)
local name = get_string(VT_LW, 0x4500)
local position = get_string(VT_LW, 0x4510)
local age = get_string(VT_LW, 0x4520)
local idx = get_uint16(VT_LW, 0x4530)
db.modify(g_db_handle, idx, name..";"..position..";"..age..";")
elseif key == 2 then -- 删除(删选中行)
local idx = get_uint16(VT_LW, 0x4530)
db.del(g_db_handle, idx)
end
end
end
db.lua 封装层每个函数都做句柄校验 + 索引越界检查(idx = count 返回 -2),这是句柄驱动设计的标准姿势:不合法就直接返回错误码,绝不往下传。
四个坑:①flashaddr 必须避开工程资源/资料采样/操作记录/OTA 备份四区,直接操作 Flash 无隔离,选错地址数据互相覆盖甚至变砖;②easydb_update 的 dataset 长度必须严格等于创建时的 dataRowSize,长了短了都会失败;③记录用英文分号 ; 分隔,和表格控件解析规则对齐,别用中文分号;④句柄是全局生命周期资源,on_init 里开一次全程持有,操作前先校验句柄非 0、索引在范围内。定制固件才有这套 API,普通固件没有。
整理自 广州大彩科技 VisualHMI 开发文档(hmi-doc.gz-dc.com)LUA 教程「简易数据库」,版权归大彩科技所有。
发表回复