VisualHMI - 简易数据库 (定制)

1.概述

本系统实现了一种轻量级、无文件系统的嵌入式简易数据库,专为资源受限的 HMI 或工业控制器设计。其核心机制是在 Flash 存储器中划定一块连续物理区域,以固定长度记录的方式实现结构化数据的持久化存储与访问,并支持运行时按需初始化。

⚠️ 简易数据库 Flash 地址规划注意事项

本简易数据库直接操作 Flash 存储区域,不具备文件系统隔离机制,因此其 flashaddr 起始地址必须严格避开系统其他关键功能所占用的存储区块,避免数据覆盖、程序异常或设备变砖。特别需注意以下四类高风险区域:

  1. 工程资源存储区

  2. 资料采样块存储区

  3. 操作记录块存储区

  4. OTA 升级备份区域


适用范围:VisualHMI - HMI&M系列&Dx系列( 定制固件)

下载链接:VisualHMI - 简易数据库(点击下载)

2. API说明

2.1.easydb_open(flashaddr,createIfNotExist,dataRowSize,maxCount)

数据库打开函数easydb_open() 是 HMI 系统提供的嵌入式轻量级数据库接口,用于在内部 Flash 存储器中打开或创建一个结构化数据表。该数据库以固定长度记录方式存储,适用于参数配置、事件日志、运行记录等掉电存数据读写场景。

📊 参数说明

参数 类型 说明
flashaddr number Flash 起始地址(字节对齐)
createIfNotExist number 创建策略标志
0:仅打开已有数据库,若不存在则失败
1:若数据库不存在,则按后续参数创建新库
dataRowSize number 单条记录大小(字节)
• 必须 ≥ 1
maxCount number 最大记录数量
• 决定数据库总占用空间 = dataRowSize × maxCount

2.2.easydb_close(db)

数据库关闭函数,easydb_close(db) 是 HMI 系统提供的嵌入式数据库资源释放接口,用于关闭已打开的 Flash 数据库,释放其占用的内存上下文或驱动句柄

📊 参数说明

参数 类型 说明
db number 数据库句柄
• 由 easydb_open() 成功调用时返回数据

2.3.easydb_get_count(db)

获取数据库当前有效记录数,easydb_get_count(db) 是 HMI 系统提供的轻量级 Flash 数据库查询接口,用于获取指定数据库中当前已写入的有效记录数量

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 必须是由 easydb_open() 成功返回的句柄

2.4.easydb_get(db,idx)

读取数据库指定记录,easydb_get(db, idx) 是 HMI 系统提供的轻量级 Flash 数据库读取接口,用于从已打开的数据库中读取指定索引位置的完整数据记录

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 由 easydb_open() 成功返回的句柄
idx number 记录索引(行号)
• 从 0 开始计数 • 有效范围:0 ≤ idx < 当前记录总数

📊 返回值说明

项目 说明
成功返回 字符串
失败返回 nil

2.5.easydb_add(db,dataset)

向数据库追加新记录easydb_add(db, dataset) 是 HMI 系统提供的轻量级 Flash 数据库写入接口,用于在指定数据库末尾追加一条新记录

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 由 easydb_open() 成功返回的句柄
dataset string 要写入的数据内容

2.6.easydb_update(db,idx,dataset)

更新数据库指定记录,easydb_update(db, idx, dataset) 是 HMI 系统提供的轻量级 Flash 数据库更新接口,用于修改已存在记录的指定索引位置的数据内容

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 由 easydb_open() 成功返回的句柄
idx number 要更新的记录索引(行号)
• 从 0 开始计数 • 有效范围:0 ≤ idx < easydb_get_count(db)
dataset string 新的数据内容
• 长度必须严格等于创建时指定的 dataRowSize

2.7.easydb_del(db,idx)

删除数据库指定记录,easydb_del(db, idx) 是 HMI 系统提供的轻量级 Flash 数据库删除接口,用于删除指定索引位置的数据记录

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 由 easydb_open() 成功返回的句柄
idx number 要删除的记录索引(行号)
• 从 0 开始计数 • 有效范围:0 ≤ idx < easydb_get_count(db)

2.8.easydb_clear(db)

清空数据库所有记录,easydb_clear(db) 是 HMI 系统提供的轻量级 Flash 数据库清空接口,用于一次性删除数据库中的所有有效记录,将数据库恢复至初始空状态。

📊 参数说明

参数 类型 说明
db number 有效的数据库句柄
• 由 easydb_open() 成功返回的句

3. 应用

本示例采用嵌入式简易数据库(由 db.lua 封装),以 “姓名;职业;年龄” 作为单条记录的存储格式(例如:"张三;工程师;32")。所有字段之间使用英文分号 ; 作为分隔符,确保与 HMI 表格控件的数据解析规则一致,便于直接映射至多列表格进行可视化展示。,完整演示数据库的读取、新增、修改、删除等核心操作流程。

3.1. 添加数据记录控件

  1. 数据来源:自定义
  2. 字符编码:UTF8
  3. 翻页控制:✔
  4. 控制地址:LW4000
  5. 选中行通知:✔(LW4004,UINT32),目的选中对应数据,修改删除简易数据库

image-20260123163857278

3.2. 编辑Lua

本DEMO,设计的数据库操作段均采用句柄驱动(Handle-Based)的设计范式,并封装在db.lua中。调用 db.open 成功后,将返回一个非零整数作为数据库操作的唯一有效句柄。该句柄应被声明为全局变量(如 g_db_handle),并在整个应用生命周期内由主逻辑脚本统一持有。

所有对简易数据库的读写操作(包括添加、查询、修改、删除、清空等)必须显式地将该句柄作为首个关键形参传入,模块内部不维护任何全局状态或隐式上下文。此设计确保了接口的无状态性、线程安全性及多实例兼容性。

3.2.1. 数据记录控件设置

在数据记录控件的数据来源设置为“自定义”的模式下,需要脚本处数量获取内容填充两个阶段。

1. 记录数量回调:on_get_record_count

此函数作为数据记录控件的行数提供者。当控件的数据来源设置为“自定义”时,系统无法自动获知数据量,必须通过该回调函数来确定表格的逻辑尺寸(即总行数)。

  • 动态同步:函数内部调用 easydb_get_count(g_db_handle),向底层简易数据库驱动查询当前的有效记录总数。
  • 作用:实时返回数据库的物理记录数,驱动表格控件创建相应数量的行模板,确保 UI 展示范围与数据库数据量严格一致。
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
2. 记录内容回调:on_get_record_row

此函数作为数据记录控件的内容提供者。在表格确定行数后,每当某一行需要显示或刷新时,系统会调用此函数来获取该行的具体数据内容。

  • 参数映射:接收系统传入的 row 参数(代表当前请求的行索引)。
  • 数据抓取:根据传入的行索引 row,调用封装好的 db.get 接口,从全局数据库句柄 g_db_handle 中提取对应索引的记录数据。
  • 作用:将数据库中第 row 条记录的内容实时映射并返回给控件,完成表格单元格的动态数据填充。
function on_get_record_row(screen_id,control_id,row)

    local ret, msg = db.get(g_db_handle, row)
    print('on_get_record_row('..screen_id..','..control_id..','..row..') : '..((ret == 0) and msg or 'is nil'))
    if ret == 0 then return msg end

end

3.2.2. 初始化数据库

在 main.lua的 on_init() 回调中完成数据库初始化:

  • 调用 db.open(flashaddr, createIfNotExist, dataRowSize, maxCount) 打开数据库;
  • 返回值为非零整数句柄,表示数据库操作上下文;返回 0 表示打开失败。
--db.lua
--- 打开数据库,返回操作句柄。
-- @param flashaddr        (number) Flash 存储起始地址(单位:字节),例如 10*1024*1024
-- @param createIfNotExist (number) 是否在数据库不存在时自动创建(0: 否, 1: 是)
-- @param dataRowSize      (number) 单条记录最大字节数(建议 ≤ 1024)
-- @param maxCount         (number) 数据库最多可存储的记录条数(建议 ≤ 10000)
-- @return handle          (number) 数据库句柄;非0表示成功,0表示失败
function db.open(flashaddr, createIfNotExist, dataRowSize, maxCount)
    local handle = easydb_open(flashaddr, createIfNotExist, dataRowSize, maxCount)
    return handle
end

--main.lua
function on_init()

    dofile('db.lua')

    _EN_ON_UPDATA_API_ = 0

    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

3.2.3. 获取数据库记录条数

🛡️ 系统支持通过数据库句柄实时查询当前的有效记录总数。为了确保调用数据操作的安全性,执行具体的增、删、改、查逻辑之前,增加一个边界检查的前置步骤。这个步骤用于验证用户选中的索引(idx)是否在数据库当前的有效范围内。

--- 获取当前数据库中的记录总数。
-- @param handle (number) 数据库句柄
-- @return count (number) 记录数量;若句柄无效,返回 -1
function db.getCnt(handle)
    if not handle or handle == 0 then return -1 end
    return easydb_get_count(handle)
end

3.2.4. 数据库添加一条记录

在 HMI 主画面中,用户通过三个独立输入控件分别填写 姓名职业年龄,系统将这些字段绑定至指定的内部寄存器地址。当用户点击“确认”按钮时,触发写入逻辑,将结构化数据以分号分隔格式追加至简易数据库。

字段 寄存器地址(LW) 说明
姓名 LW5010 文本输入框,存储用户姓名
职业 LW5020 文本输入框,存储用户职业
年龄 LW5030 数值输入框或文本框,存储年龄(整数)
确认信号 LW5000 按钮触发标志:写入后置 0,或由按钮事件直接调用
--- 向数据库末尾追加一条新记录。
-- @param handle (number) 数据库句柄
-- @param msg    (string) 要写入的字符串数据(长度 ≤ dataRowSize)
-- @return ret   (number) 0 表示成功,-1 表示失败(如句柄无效或空间不足)
function db.add(handle, msg)
    if not handle or handle == 0 then return -1 end
    return easydb_add(handle, msg)
end

function on_update(slave,vtype,addr)
    ......
    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..';') --添加记录

        ......
        end
    end
end

3.2.5. 数据库读取一条记录

在 HMI 主画面的表格中,当用户选中某一条记录时,系统将触发数据记录选中通知,并将选中行的索引写入寄存器 LW4004。随后,系统自动读取该索引对应的数据库记录,并按照分号 ; 作为分隔符对记录内容进行解析。解析后的字段依次回填至指定寄存器:姓名(LW4500) 职业(LW4510)年龄 (LW4520),并在界面上弹出对话框,实时显示所选记录的详细信息。

--db.lua
--- 根据索引读取一条记录(索引从 0 开始)。
-- @param handle (number) 数据库句柄
-- @param idx    (number) 记录索引(0 ~ getCnt()-1)
-- @return ret   (number) 0 表示成功,-1 表示句柄无效,-2 表示索引越界
function db.get(handle, idx)

    if not handle or handle == 0 then return nil end

    local count = easydb_get_count(handle)
    if idx < 0 or idx >= count then return -2 end -- 索引越界

    return 0, easydb_get(handle, idx)  
end

--main.lua
function on_update(slave,vtype,addr)
    if addr == 0x4000+4
    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
    ......
    end
end

3.2.6. 数据库修改一条记录

用户在 HMI 界面选中表格中的某一行记录时,记录对应的易数据库(EasyDB)索引 idx 。当用户点击 修改按钮(LW5000 Set 1) 时,系统将 姓名(LW4500)职业(LW4510)年龄(LW4520) 输入框中的最新内容,拼接为一条完整的记录字符串,完成对易数据库中idx记录的修改操作。

--db.lua
--- 修改指定索引的记录内容(索引从 0 开始)。
-- @param handle (number) 数据库句柄
-- @param idx    (number) 要修改的记录索引(0 ~ getCnt()-1)
-- @param msg    (string) 新的数据内容
-- @return ret   (number) 0 表示成功,-1 表示句柄无效,-2 表示索引越界
function db.modify(handle, idx, msg)

    if not handle or handle == 0 then return -1 end

    local count = easydb_get_count(handle)
    if idx < 0 or idx >= count then return -2 end -- 索引越界

    print(string.format("db.modify(%d, '%s')......", idx, msg))
    return 0, easydb_update(handle, idx, msg)
end

--main.lua
function on_update(slave,vtype,addr)
    ......
    elseif addr == 0x5000
    then
        local key = get_uint16(VT_LW, addr)
        ......
        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..';')

      ......
        end
    end
end

3.2.7. 数据库删除一条记录

当用户在 HMI 界面选中表格中的某一行记录时,记录对应的易数据库(EasyDB)索引 idx ,当用户点击 删除按钮(LW5000 Set 2) 时,执行删除操作对于数据库的idx条记录,从而实现数据的移除。

--db.lua

--- 删除指定索引的记录(索引从 0 开始)。
-- @param handle (number) 数据库句柄
-- @param idx    (number) 要删除的记录索引(0 ~ getCnt()-1)
-- @return ret   (number) 0 表示成功,-1 表示句柄无效,-2 表示索引越界
function db.del(handle, idx)

    if not handle or handle == 0 then return -1 end

    local count = easydb_get_count(handle)
    if idx < 0 or idx >= count then return -2 end -- 索引越界

    return 0, easydb_delete(handle, idx)
end

--main.lua
function on_update(slave,vtype,addr)
    ......
    elseif addr == 0x5000
    then
        ......
        elseif key == 2--删除
        then
            local idx = get_uint16(VT_LW, 0x4530)
            db.del(g_db_handle, idx)
        end

    end
end

3.2.8. 关闭数据库

当用户完成数据操作(添加、删除、修改)后,可以关闭数据库,将释放易数据库(EasyDB)占用的文件句柄与内存资源。

--- 关闭数据库,释放资源。
-- @param handle (number) 由 db.open() 返回的数据库句柄
function db.close(handle)
    if handle and handle ~= 0 then
        easydb_close(handle)
    end
end

3.2.9. 清除数据库

在获取数据库连接句柄(Handle)后,通过该句柄执行数据库清除操作。

--- 清空数据库中所有记录。
-- @param handle (number) 数据库句柄
-- @return ret   (number) 0 表示成功,-1 表示失败
function db.clear(handle)
    if not handle or handle == 0 then return -1 end
    return easydb_clear(handle)
end

4. 运行预览

系统启动时,执行数据库(Easy DB)初始化指令。通过 db.open 接口建立连接并获取句柄 g_db_handle。随后,向数据库追加四条预设记录("Alice;Engineer;30"、"Bob;Technician;25"、"Charlie;Manager;40"、"Harden;Basketball players;36"),确保数据记录控件在启动后可实时呈现该初始数据集。

  • 添加操作:系统通过 db.add 指令向数据库末尾追加新记录 "张三;FAE;22"。数据记录控件通过 on_get_record_count 回调感知数据量变化,自动刷新显示,将新记录纳入可见列表。

  • 读取操作:用户选中 "张三"(内部索引 4)、"Harden"(内部索引 3)、"Charlie"(内部索引 2)时,系统调用 db.get(g_db_handle, idx) 接口读取对应记录,并通过弹窗界面展示详情。

  • 修改操作:在 "Harden" 的编辑对话框中,用户将年龄字段更新为 "35"。调用 db.modify(g_db_handle, 3, "Harden;Basketball players;35"),并更新保存到数据库。
  • 删除操作:用户确认删除 "Charlie"(内部索引 2)。系统执行 db.del(g_db_handle, 2) 指令,移除该记录。数据记录控件随之动态调整行数与内容。

Video_2026-01-26_104957 00_00_00-00_00_30

Copyright ©Dacai all right reserved,powered by Gitbook该文件修订时间: 2026-02-05 15:49:52

results matching ""

    No results matching ""