TwinCAT3 CSV einlesen

MX7534

Level-1
Beiträge
3
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen,

bin neu in der Beckhoff Welt und versuche seit einiger Zeit eine CSV Datei in ein Array einzulesen.
Dazu habe ich auch hier im Forum einige Beispiele gefunden. Danke dafür!

Das einlesen funktioniert auch soweit. Das Problem was ich habe, es wird nicht die komplette CSV eingelesen.
Habe nun eine Demo CSV mit 26 Spalten und 50 Zeilen erstellt.
In meinen Augen scheint das begrenzende Element das 2D-Array zu sein. Auch wenn ich dieses größer mache, wird immer nur ein gewisser Anteil gelesen.
Mache ich beim Array die erste Dimension kleiner, kann ich mehr in der zweiten Dimension lesen und umgekehrt. Wenn ich mir das online anschaue, werden auch nicht alle Elemente des Arrays angezeigt.

Meine Frage wäre ob es da ein Begrenzung bei den Dimensionen gibt? Bei Beckhoff konnte ich darüber nichts finden.
Oder liegt das Problem doch wo anders?


Code:
FUNCTION_BLOCK FB_CSV_EVU_Read
(* Reading of CSV file in text mode. None of the field value is containing any non-printing control characters like line feed, carriage return, quotation marks, semicolon... *)
VAR CONSTANT
    nMaxRow            : INT := 30;    // CSV Zeilen
    nMaxColumn        : INT := 50;    // CSV Spalten
END_VAR

VAR_INPUT
    bRead           : BOOL := FALSE;(* Rising edge starts program execution *)
    sNetId          : T_AmsNetId := '';    (* TwinCAT system network address *)
    sFileName       : T_MaxString := 'D:\temp\Parameter_E22.csv';(* CSV source file path and name *)   
END_VAR

VAR
    database    : ARRAY[1..nMaxRow,1..nMaxColumn] OF STRING(100);(* Target PLC database *)
END_VAR   

VAR_OUTPUT
    bBusy       : BOOL;
    bError      : BOOL;
    nErrId      : UDINT;
END_VAR

VAR
    nRow        : INT     := 0;(* Row number (record) *)
    nColumn     : INT     := 0;(* Column number (record field) *)
    hFile        : UINT    := 0;(* File handle of the source file *)
    step        : DWORD   := 0;
    fbFileOpen    : FB_FileOpen;(* Opens file *)
    fbFileClose    : FB_FileClose;(* Closes file *)
    fbFileRead    : FB_FileRead;(* Reads one record (line) *)
    fbRead        : FB_CSVMemBufferReader;

    record        : ARRAY[0..25000] OF BYTE;(* Binary (row) data buffer. The size of this buffer have to be > length of longest record line + 2 (CRLF)  *)
    cbRecord    : UDINT := 0;(* Number of bytes in row buffer *)
    
END_VAR

Code:
CASE step OF
    0:(* Wait for rising edge at bRead variable *)
        IF bRead THEN
            bRead:= FALSE;
            bBusy:= TRUE;
            bError:= FALSE;
            nErrId:= 0;
            hFile:= 0;
            nRow:= 1;
            nColumn:= 1;
            MEMSET(ADR(database),0 ,SIZEOF(database));
            step:= 1;
        END_IF

    1:(* Open source file *)
        fbFileOpen(bExecute:= FALSE);
        fbFileOpen(sNetId:= sNetId, sPathName:= sFileName, nMode:= FOPEN_MODEREAD OR FOPEN_MODEBINARY, ePath:= PATH_GENERIC, bExecute:= TRUE);(* Open file in Binary mode! *)
        step:= 2;

    2:(* Wait until open not busy *)
        fbFileOpen(bExecute:= FALSE, bError=> bError, nErrID=> nErrID, hFile=> hFile);
        IF NOT fbFileOpen.bBusy THEN
            IF NOT fbFileOpen.bError THEN
                step:= 3;
            ELSE(* Error: file not found? *)
                step:= 100;
            END_IF
        END_IF

    3:(* Read single line (record) *)
        fbFileRead(bExecute:= FALSE);
        fbFileRead(sNetId:= sNetId, hFile:= hFile, bExecute:= TRUE, pReadBuff:= ADR(record), cbReadLen:= SIZEOF(record));
        step:= 4;

    4:(* Wait until read not busy *)
        fbFileRead(bExecute:= FALSE, bError=> bError, nErrID=> nErrID,);
        IF NOT fbFileRead.bBusy THEN
            IF NOT fbFileRead.bError THEN
                IF fbFileRead.bEOF THEN
                    step:= 10;(* End of file reached => Close source file *)
                ELSE
                    cbRecord := fbFileRead.cbRead;
                    step:= 5;
                END_IF
            ELSE(* Error *)
                step:= 100;
            END_IF
        END_IF

    5:(* Parse single line (record)*)
        fbRead.eCmd:= eEnumCmd_First;(* Write first field value *)
        REPEAT
            fbRead(pBuffer:= ADR(record), cbBuffer:= SIZEOF(record),);(* bCRLF == TRUE => Write CRLF after the last field value *)
            IF fbRead.bOk THEN
                fbRead.eCmd := eEnumCmd_Next;(* Read next field value *)

                IF ( nRow <= nMaxRow ) THEN
                    IF ( nColumn <= nMaxColumn ) THEN
                        database[nRow,nColumn]:= fbRead.getValue;
                    END_IF           
                END_IF
                
                //nColumn := nColumn + 1;(* Increment number of read columns *)
                nRow := nRow + 1;(* Increment number of read records *)
                IF fbRead.bCRLF THEN(* CRLF == TRUE => End of reacord reached *)
                    nColumn := nColumn + 1;(* Increment number of read columns *)
                    nRow := 1;
                    //nRow := nRow + 1;(* Increment number of read records *)
                    //nColumn := 1;
                END_IF

            ELSE(* Error: End of record reached or all fields read *)
                step := 3;(* Try to read next line *)
            END_IF
            
        UNTIL NOT fbRead.bOk
        
        END_REPEAT
    
    10:(* Close source file *)
        fbFileClose(bExecute:= FALSE);
        fbFileClose(sNetId:= sNetId, hFile:= hFile, bExecute:= TRUE);
        step:= 11;

    11:(* Wait until close not busy *)
        fbFileClose(bExecute:= FALSE, bError=> bError, nErrID=> nErrID);
        IF (NOT fbFileClose.bBusy) THEN
            hFile:= 0;
            step:= 100;
        END_IF

    100:(* Error or ready step => cleanup *)
        IF (hFile <> 0) THEN
            step:= 10; (* Close the source file *)
        ELSE
            bBusy:= FALSE;
            step:= 0;    (* Ready *)
        END_IF

END_CASE

Gruß und Danke
 
Folgendes behauptet meine KI:
Das sieht nicht nach einer Begrenzung des 2D-Arrays aus, sondern nach vertauschter Indexlogik.
Du schreibst nach database[nRow,nColumn], erhöhst aber bei jedem Feld nRow und erst bei bCRLF nColumn.
Damit ist die 1. Dimension bei dir faktisch die Spalte und die 2. die Zeile.
Genau deshalb ändert sich das Verhalten, wenn du eine Dimension kleiner machst.

Für database[Zeile,Spalte] muss es so sein:
  • pro Feld: nColumn := nColumn + 1
  • bei bCRLF: nRow := nRow + 1; nColumn := 1
Außerdem ist deine Testdatei 50 Zeilen / 26 Spalten groß, dein Array aber auf nMaxRow := 30 und nMaxColumn := 50 gesetzt.

Zusätzlich prüfen, ob die CSV wirklich CRLF als Zeilenende hat und die letzte Zeile ebenfalls mit CRLF endet, da FB_CSVMemBufferReader genau das erwartet.
Könnte das zutreffen? ;)
 
Ich kann auch die Zeile und Spalte beim hochzählen tauschen. Kann sie sogar gleich groß machen. Ändert aber nichts.
Die CSV wird nie komplett eingelesen.
Das die CSV am Zeilenende und am ende mit CRLF endet habe ich geprüft. Das ist gegeben.
 
Bringt Dich bei Deinem eigentlichen Problem leider nicht weiter, aber mir sind bei der Durchsicht Deines Programms zwei Dinge aufgefallen.
Zum einen sollten FBs die über mehrere Zyklen laufen, z.B. Timer oder, wie bei Dir, Datei-FBs nicht innerhalb einer CASE Anweisung oder einer IF-Anweisung ausgeführt werden, sondern außerhalb. Innerhalb von CASE/IF sollte nur die Logik (z.B. bExecute) beeinflusst werden. Ich habe schon viele Fälle gesehen, da wurde zum Beispiel ein Timer innerhalb eines CASE Schrittes ausgeführt, aber bei den Weiteren dann nicht mehr und der Fragensteller hat sich dann gewundert, dass der Ausgang Q nie TRUE wurde.
Dann lässt die Art, wie Du die REPEAT Schleife nutzt darauf schließen, dass Du, so wie ich, ursprünglich Anwendungsprogramme für PCs erstellt hast. Diese Art der Nutzung einer REPEAT-Schleife kann zur Katastrophe, sprich einer Zykluszeitüberschreitung und/oder dem Absturz der SPS führen. Durch die REPEAT Schleife wartest Du bis der Ausgang bOK des FBs fbRead TRUE ist. Dauert dies zu lange kommt es zu Problemen, da das SPS Programm an dieser Stelle immer kreist und der Rest nicht ausgeführt wird, was bei einer SPS aber entscheidend ist, da zum Beispiel andere wichtige Programmteile, die zum Beispiel Grenzwerte überwachen und bei Bedarf reagieren müssen, in der Zeit nicht ausgeführt werden, außerdem werden die Ein- und Ausgänge in der Zeit nicht verarbeitet/gesetzt.
Ein SPS-Programm muss kontinuierlich abgearbeitet werden und darf nicht (länger) an einer Stelle verharren. Aus diesem Grund sind Schleifen in SPS-Programmen auch mit Vorsicht zu genießen. Wenn Du auf etwas länger warten musst, muss dies auf eine andere Art erfolgen, zum Beispiel in dem eine CASE-Anweisung genutzt wird und der Schritt so lange aktiv ist, bis das gewünschte Ereignis eintritt, dabei muss der CASE-Schritt aber auch immer komplett abgearbeitet werden.
Nachtrag: Wenn ich so darüber nachdenke, ist die REPEAT-Schleife vielleicht auch die Ursache für Dein Problem.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
@oliver.tonn
Danke für Deine Anregungen! Verstehe soweit was Du meinst. Mal schauen wie ich das noch umsetzen kann...

Die Lösung zu meinem Problem ist aber was anderes (peinlich!). :rolleyes:
Der FB scheint soweit zu funktionieren. Schlussendlich war/ist es nur ein Darstellungsproblem in TwinCAT, daß online nicht alle Elemente des Arrays angezeigt werden. Erst beim Doppelklick auf "Datentyp -> Array" wird ein Fenster angezeigt, wo man den Bereich der Angezeigt werden soll, erweitern kann.
Manchmal sind es doch nur die kleinen Dinge im Leben....

Gruß und Danke
 
Zurück
Oben