VisualHMI-Datei IO: LUA Lesen und Schreiben SD-Karte / USB-Stick / Flash und Verzeichnislisten

kostenlosKostenloses technisches Material

Dieser Inhalt ist direkt lesbar und eignet sich für das Grundlagenlernen und die Suche.

Vollständige API-Lese - Schreib-Dateien: Lesen von txt / csv / bin, Schreiben von Dateien, Durchreisen von Verzeichnissen, Kopieren / Löschen. Die Kernidee ist eine -Streaming, niemals mehr als 2 KB gleichzeitig im Speicher。Der HMI-Speicher ist sehr klein, und die Verletzung dieses Watchdogs wird direkt zurückgesetzt, und das Gerät wird ohne Vorwarnung neu gestartet.

1. Identifikation des Speichergeräts (M-Serie vs DH-Serie)

GerätM-SerieDH-Serie
SD-Karte1:/1.txt/sdcard/1.txt
USB-Laufwerk2:/1.txt/udisk/1.txt
Der interne Flash3:/1.txt/data/1.txt

Der Pfadtrennzeichen muss / verwendet werden.

2. API-Dokumentationsübersicht

FunktionParameterBeschreibung
dofile(name)lua DateipfadeLade ausführende. lua-Module, große Entfernung von Modulen
list_dir(path)Verzeichnispfadasynchron durchsucht das Verzeichnis, das Ergebnis ruft auf_list_dir
on_list_dir(path,filename,type,fsize)type 0 = Ordner / 1 = Datei; fsize ByteEinmal wiederholt, wenn man einen Eintrag findet.
file_open(path,mode)mode 0 = FA_READ read only / 1 = FA_WRITE overwrite write / 2 = FA_WRITE _ ADD Add-on writeÖffnen Sie die Datei, zurückgeben Boolean
file_close()-Schließen Sie die Datei, nach dem Schreiben muss wiederholen, Datenverlust-Schutz
file_size()-Gesamtzahl der Bytes der aktuell geöffneten Datei
file_seek(offset)Byte Offset, 0 = DateianfangLese - und Schreibort
file_read(count)1 ≤ count ≤ 2048Lesen von Bytearray (table, Unterzeichnis von 1), fehlgeschlagen zurückgegeben nil
file_write(data)Bytetabelle, 1 ≤ #data ≤ 2048Schreiben von Bytearray, zurückgegeben Boolean
file_delete(path)Vollständiger PfadDateien gelöscht, nicht wiederhergestellt
file_copy(src,dst)Quell / Ziel Vollständiger PfadDatei repliziert, Fortschrittsrufback on_copy_file_process
on_copy_file_process(status,filesize,transfersize)status 0 fehlgeschlagen / 1 in Gang / 2 erfolgreichKopieren Fortschrittsrufback, transfersize kopiert Bytes
mkdir(dir)Vollständiger VerzeichnispfadOrdner synchronisiert

3.🔴 Sicherheitseinsatz (nicht zu sehen)

Es ist streng verboten, mehrfach gelesenen Daten in globalen Variablen oder in Puffern mit langem Lebenszyklus zu sammeln.Typischer Fehler: Das erste Lesen von 2 KB Speicherung der globalen Tabelle, später jedes Mal Lesen und hinzufügen - Speicher wächst kontinuierlich bei der Verarbeitung großer Dateien, direkt auslösen die Reset-Wachhund.

Die richtige Haltung:StreamingJedes Mal, wenn file_read (2048) sofort analysiert / verifiziert / schreibt / zeigt, und die Verarbeitung ist abgeschlossen, freigegeben, nur ein temporärer Puffer von ≤ 2 KB im Speicher zu jedem Zeitpunkt. Das gleiche gilt für das Schreiben von Dateien: Vorbereiten Sie nur einen Block von ≤ 2048 Bytes sofort file_write, nicht zuerst eine große Tabelle von mehreren MB schreiben und dann in Segmenten schreiben (Datenvorbereitungsphase ist der Speicher überflutet).

4. Lesen von Dateien: Streaming-Blocking + Rückruf

Offizielle Kapselungfile.read_stream(filepath, on_chunk): Öffnen → Größe nehmen → Schleife seek + read (≤ 2048) → auf _ chunk (chunk, offset, is _ last) pro Block → Schließen der Datei. Der Rückruf gibt false zurück, um vorzeitig zu beenden, und die große Datei-Schleife nutzt feed _ dog (), um den Hund zu füttern.

4.1 Lesen Sie das txt Zeile für Zeile

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")  -- 按行分割
    g_line_buffer = lines[#lines]          -- 保留不完整行(可能跨块)
    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

Beachten Sie, dass die Zeilenpuffer zwischen den Blöcken liegen: Zeilenwechsel können genau zwischen zwei Blöcken liegen, also halten Sie g _ line _ buffer auf, um unvollständige Zeilen für den nächsten Block zu überlassen.

4.2 Lesen Sie Bin in 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 字节提交一行
        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 字节补 0
    while #g_byte_buffer < 16 do table.insert(g_byte_buffer, 0) end
    record_write_data(1, g_byte_buffer)
  end

5. Schreiben von Dateien

Kapselfile.write_chunk(filepath, data, mode)Modus 1 = Überschreiben, 2 = Zusatzschreiben, data unterstützt String oder Byte 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

-- 场景1:新建写
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)

-- 场景2:追加写
file.write_chunk("1:/"..name..".txt", msg.."n", 2)

und️ Das Nachschreiben muss explizit file_seek (file_size()) sein.HMI-file _ open (mode = 2) ist nicht gleich POSIX-O _ APPEND und wird nicht automatisch am Ende gezeigt - ohne seek schreiben Sie mit Offset 0 und überschreiben Sie den Anfang der ursprünglichen Datei.

6.Überholen + Kopieren löschen

6.1 Durchschreiten Sie das SD-Root - Verzeichnis, um das Datenaufzeichnungssteuerelement anzuzeigen

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, "未知"
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

-- 刷新按钮 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 (mit Fortschrittsleiste)

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)           -- 进度条
    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                      -- 复制
      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                  -- 删除
      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
Fünf Gruben, die man treten muss:1 Speicher-Eisengesetz - - es ist streng verboten, die globale Akkumulation von großen Puffern, nur ≤ 2 KB Block auf einmal zu verlassen, sonst setzt der Watchdog zurück;2 Das Add-on - Schreiben muss file_seek(file_size())sein, das Add-on des HMI ist nicht O _ APPEND;3 Der Pfad muss Gerätpräfix und /- "1: / config.txt" Paare, "1: config.txt" falsch sein;4 Die Lese-Schleife für große Dateien nutzen feed_dog (), keine zeitaufwändigen Dinge beim Rückruf zu tun;5 Unmittelbar nach dem Schreiben der Datei file_close ()Ein Stromausfall kann die Datei beschädigen oder teilweise schreiben.

Aus dem VisualHMI-Entwicklungsdokument von Guangzhou Dacai Technology (hmi-doc.gz-dc.com) LUA-Tutorial "Dateien lesen und schreiben", das Urheberrecht liegt bei Dacai Technology.

Sollte man diese Informationen für ein echtes Projekt verwenden?

Gehen Sie zum Tool Center, um Nachrichten zu analysieren, CRC-Prüfungen und Geräte-Debugging zu erledigen, oder senden Sie Anforderungen für Auswahl - und Zugangsvorschläge.

Ingenieur Mitglied

Verwandeln Sie diesen Artikel in ein umsetzbares Debuggermaterial

Erweiterte Nachrichtenanalyse, Paket-Downloads, Codebeispiele, Engineering-Szenarien und Priority-Technischer Support sind für die Real-Projekt - Bereitstellung möglich.

Unbegrenzte Werkzeuge
Datenpaket und Codepaket
Vollständige Engineering Case-Basis
Vorrangiger Zugang zum technischen Support

Antwort veröffentlichen

Ihre E-Mail - Adresse wird nicht öffentlich gemacht. Erforderliche Elemente wurden verwendet * markiert.