- 1. 1. 存储Ausrüstung标识(M 系列 vs DH 系列)
- 2. 2. 文件 API 总表
- 3. 3. 🔴 内存安全铁律(不看必翻车)
- 4. 4. 读文件:流式Unterteilung块 + 回调
- 5. 4.1 读 txt 逐行显示
- 6. 4.2 读 bin 按 16 Bytes一行
- 7. 5. 写文件
- 8. 6. 遍历 + 拷贝Löschen
- 9. 6.1 遍历 SD 根目录显示zuDaten aufzeichnenKontrollen
- 10. 6.2 Kopieren. + Löschen(带Fortschritt Artikel)
文件读写 API 全套:读 txt/csv/bin、写文件、遍历目录、拷贝/Löschen。核心思想就一条——流式处理,永远不要让内存里同时Existenz超过 2KB 的缓冲区。HMI 内存极小,违反这条看门狗直接复位,Ausrüstung无预警重启。
1. 存储Ausrüstung标识(M 系列 vs DH 系列)
| Ausrüstung | M 系列 | DH 系列 |
|---|---|---|
| SD 卡 | 1:/1.txt |
/sdcard/1.txt |
| U 盘 | 2:/1.txt |
/udisk/1.txt |
| 内部 Flash | 3:/1.txt |
/data/1.txt |
路径Unterteilung隔符必须用 /。
2. 文件 API 总表
| 函数 | Parameter | 说明 |
|---|---|---|
dofile(name) |
lua 文件路径 | Laden执行 .lua 模块,大型工程拆模块 |
list_dir(path) |
目录路径 | Asynchron扫描目录,Ergebnis逐项回调 on_list_dir |
on_list_dir(path,filename,type,fsize) |
type 0=文件夹/1=文件;fsize Bytes | 遍历Ergebnis回调,每发现一个条目调一次 |
file_open(path,mode) |
mode 0=FA_READ 只读/1=FA_WRITE 覆盖写/2=FA_WRITE_ADD 追加写 | Öffnen.文件,返回布尔 |
file_close() |
– | Schließen文件,写入后必须调,防丢Daten |
file_size() |
– | gegenwärtigÖffnen.文件的总Anzahl der Bytes |
file_seek(offset) |
Bytes偏移,0=文件开头 | 定位读写位置 |
file_read(count) |
1 ≤ count ≤ 2048 | 读Anzahl der Bytes组(table,下标从 1),失败返回 nil |
file_write(data) |
Bytes table,1 ≤ #data ≤ 2048 | 写Anzahl der Bytes组,返回布尔 |
file_delete(path) |
vollständig路径 | Löschen文件,不可恢复 |
file_copy(src,dst) |
源/目标vollständig路径 | Kopieren.文件,进度回调 on_copy_file_process |
on_copy_file_process(status,filesize,transfersize) |
status 0失败/1进行中/2成功 | 拷贝进度回调,transfersize 已Kopieren.Bytes |
mkdir(dir) |
vollständig目录路径 | 同步创建文件夹 |
3. 🔴 内存安全铁律(不看必翻车)
严禁把多次Lesen的Daten累积存进全局变Quantität或长生命周期缓冲区。典型Fehler.:Die一次读 2KB 存全局 table,后面每次读再追加——处理大文件时内存持续增长,直接触发看门狗复位。
Richtig.姿势:流式处理——每次 file_read(2048) 后立即Analyse/Prüfungen/写入/显示,处理完就释放,任何时刻内存里只有一块 ≤2KB 的临时缓冲。写文件同理:每次只准备 ≤2048 Bytes的块立即 file_write,不要先拼一个几 MB 的大 table 再Unterteilung段写(Daten准备阶段就内存溢出了)。
4. 读文件:流式Unterteilung块 + 回调
官方封装了 file.read_stream(filepath, on_chunk):Öffnen. → 取大小 → 循环 seek+read(≤2048) → 每块调 on_chunk(chunk, offset, is_last) → 关文件。回调返回 false 可提前终止,大文件循环里调 feed_dog() 喂狗。
4.1 读 txt 逐行显示
if addr == 0x1501 then
local idx = get_uint16(vtype, addr)
if idx == 0 then return end
local path = (idx == 1) and "1:/test1.txt" or "1:/test2.txt"
record_clear(0)
g_line_buffer = "" -- 行缓冲
file.read_stream(path, function(chunk, offset, is_last)
local str = ""
for _, b in ipairs(chunk) do str = str .. string.char(b) end
g_line_buffer = g_line_buffer .. str
local lines = my_split(g_line_buffer, "n") -- 按行Unterteilung割
g_line_buffer = lines[#lines] -- 保留不vollständig行(可能跨块)
for i = 1, #lines - 1 do
if #lines[i] > 0 then record_write_string(0, lines[i]) end
end
return true
end)
if string.len(g_line_buffer) > 0 then -- 最后一行
record_write_string(0, g_line_buffer)
end
end
注意跨块行缓冲:换行符可能正好落在两块之间,所以要维护 g_line_buffer 把不vollständig行留zu下一块。
4.2 读 bin 按 16 Bytes一行
elseif addr == 0x1502 then
local idx = get_uint16(vtype, addr)
if idx == 0 then return end
local path = (idx == 1) and "1:/test1.bin" or "1:/test2.bin"
record_clear(1)
g_byte_buffer = {}
file.read_stream(path, function(chunk, offset, is_last)
for _, b in ipairs(chunk) do
table.insert(g_byte_buffer, b)
if #g_byte_buffer >= 16 then -- 每满 16 Bytes提交一行
local line = {}
for i = 1, 16 do line[i] = g_byte_buffer[i] end
record_write_data(1, line)
for i = 1, 16 do table.remove(g_byte_buffer, 1) end
end
end
return true
end)
if #g_byte_buffer > 0 then -- 末尾不足 16 Bytes补 0
while #g_byte_buffer < 16 do table.insert(g_byte_buffer, 0) end
record_write_data(1, g_byte_buffer)
end
5. 写文件
封装 file.write_chunk(filepath, data, mode),mode 1=覆盖写、2=追加写,data 支持 string 或Bytes table(≤2048)。
function file.write_chunk(filepath, data, mode)
if mode ~= file.mode.create and mode ~= file.mode.append then return false end
local t = type(data)
local len = (t == "string" or t == "table") and #data or -1
if len CHUNK_SIZE then return false end -- 防内存溢出
local ok = file_open(filepath, mode)
if not ok then return false end
if mode == file.mode.append then -- 追加模式必须手动定位!
local size = file_size()
if not file_seek(size) then file_close(); return false end
end
local buf = {}
if t == "string" then
for i = 1, len do buf[i] = string.byte(data, i) end
else
for i = 1, len do buf[i] = data[i] end
end
local ret = file_write(buf)
file_close()
return ret
end
-- Szene1:新建写
local name = get_string(VT_LW, 0x1510)
local msg = get_string(VT_LW, 0x1520)
if not name or #name == 0 or not msg then return end
file.write_chunk("1:/"..name..".txt", msg.."n", 1)
-- Szene2:追加写
file.write_chunk("1:/"..name..".txt", msg.."n", 2)
⚠️ 追加写必须显式 file_seek(file_size())。HMI 的 file_open(mode=2) nicht gleich POSIX 的 O_APPEND,不会自动定位zu末尾——不 seek 就从偏移 0 开始写,把原文件开头覆盖掉。
6. 遍历 + 拷贝Löschen
6.1 遍历 SD 根目录显示zuDaten aufzeichnenKontrollen
function get_extension(filename)
local dot = string.find(filename, ".", 1, true)
if dot then
return string.sub(filename, 1, dot-1), string.sub(filename, dot+1):lower()
end
return filename, "Unbekannt"
end
function on_list_dir(path, filename, type, fsize)
local msg
if type == 0 then
msg = filename..";文件夹;;"
else
local sz
if fsize >= 1024^3 then sz = string.format("%.2fG", fsize/1024^3)
elseif fsize >= 1024^2 then sz = string.format("%.2fM", fsize/1024^2)
elseif fsize >= 1024 then sz = string.format("%.2fKB", fsize/1024)
else sz = fsize.."byte" end
local name, ext = get_extension(filename)
msg = name..";"..ext..";"..sz..";"
end
record_write_string(2, msg)
end
-- ErfrischenKnopf LW1504=1
function on_update(slave, vtype, addr)
if vtype == VT_LW then
if addr == 0x1504 and get_uint16(vtype, addr) == 1 then
record_clear(2)
list_dir("1:") -- 遍历 SD 根目录
end
end
end
6.2 Kopieren. + Löschen(带Fortschritt Artikel)
function on_copy_file_process(status, filesize, transfersize)
if status == 0 then
set_uint16(VT_LW, 0x1507, 3) -- 失败
elseif status == 1 then
local p = math.floor(transfersize * 100 / filesize)
set_uint16(VT_LW, 0x1506, p) -- Fortschritt Artikel
refresh_screen()
elseif status == 2 then
set_uint16(VT_LW, 0x1506, 100)
set_uint16(VT_LW, 0x1507, 2) -- 成功
refresh_screen()
end
end
function on_update(slave, vtype, addr)
if vtype == VT_LW and addr == 0x1505 then
local mode = get_uint16(vtype, addr)
if mode == 1 then -- Kopieren.
if file.exists("1:/1.txt") then
file_copy("1:/1.txt", "1:/1_copy.txt")
else
set_uint16(VT_LW, 0x1507, 1)
end
mkdir("1:/test")
elseif mode == 2 then -- Löschen
file_delete("1:/test")
if file.exists("1:/1_copy.txt") then
file_delete("1:/1_copy.txt")
if not file.exists("1:/1_copy.txt") then
set_uint16(VT_LW, 0x1507, 4)
else
set_uint16(VT_LW, 0x1507, 5)
end
else
set_uint16(VT_LW, 0x1507, 1)
end
end
refresh_screen()
end
end
整理自 广州大彩科技 VisualHMI 开发Dokumentation(hmi-doc.gz-dc.com)LUA Unterricht「文件读写」,版权归大彩科技所有。
Antwort veröffentlichen