String Array in Textdatei speichern

O'Gigis

Level-1
Beiträge
97
Reaktionspunkte
4
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,

ich möchte ein Array mit Strings in einer Datei mit FB_Write speichern. Klappt soweit - nur ein Haufen "Müll" wird mit gespeichert.

Code:
CASE iState OF
    0: IF bExecute THEN
            iState := istate +1;
        END_IF
        
    1: fbGetLocalAmsNetId.bExecute := TRUE;
        IF fbGetLocalAmsNetId.bBusy THEN
            iState := iState + 1;
        END_IF

    2: IF NOT fbGetLocalAmsNetId.bBusy AND NOT fbGetLocalAmsNetId.bError THEN
            fbGetLocalAmsNetId.bExecute := FALSE;
            sAmsNetId := fbGetLocalAmsNetId.AddrString;
            iState := iState + 1;
        ELSIF fbGetLocalAmsNetId.bError THEN
            iState := 99;
        END_IF
    
    3: fbFileOpen.nMode    := FOPEN_MODEWRITE ;
        fbFileOpen.sNetId    := sAmsNetId;
        fbFileOpen.ePath     := Path_GENERIC;
        fbFileOpen.sPathName     := 'C:\TwinCat\Trace.txt';
        
        fbFileOpen.tTimeout    := T#3S;
        fbFileOpen.bExecute    := TRUE;
        iState := iState +1;
        
    4: IF fbFileOpen.bBusy AND NOT fbFileOpen.bError THEN
            iState := iState +1;
        ELSIF fbFileOpen.bError THEN
            iState := 99;
        END_IF;
    
    5: IF NOT fbFileOpen.bBusy AND NOT fbFileOpen.bError THEN
            hFile := fbFileOpen.hFile;
            fbFileOpen.bExecute := FALSE;
            fbFileWrite.bExecute := FALSE;
            iState := iState + 1;
        ELSIF fbFileOpen.bError THEN
            fbFileOpen.bExecute := FALSE;
            iState := 99;
        END_IF;
    
    6: fbFileWrite.hFile          := hFile;
        fbFileWrite.sNetId       := sAmsNetId;
        fbFileWrite.tTimeout    := T#3S;
        fbFileWrite.pWriteBuff    :=  ADR(GVL_Trace.ConcatTrace );
        fbFileWrite.cbWriteLen   := SIZEOF(GVL_Trace.ConcatTrace);
        fbFileWrite.bExecute     := TRUE;
        iState := iState + 1;
    
    7: IF fbFileWrite.bBusy AND NOT fbFileWrite.bError THEN
            iState := iState + 1;
        ELSIF fbFileWrite.bError THEN
            fbFileWrite.bExecute := FALSE;
            iState := 99;
        END_IF;

    8: IF NOT fbFileWrite.bBusy AND NOT fbFileWrite.bError THEN
            fbFileWrite.bExecute     := FALSE;
            fbFileClose.bExecute     := FALSE;
            iState := iState + 1;
        ELSIF fbFileWrite.bError THEN
            fbFileWrite.bExecute := FALSE;
            iState := 99;
        END_IF;

    9: fbFileClose.hFile          := hFile;
        fbFileClose.sNetId       := sAmsNetId;
        fbFileClose.tTimeout    := T#3S;
        fbFileClose.bExecute   := TRUE;
        iState := iState + 1;
    
    10:IF fbFileClose.bBusy AND NOT fbFileClose.bError THEN
            iState := iState + 1;
        ELSIF fbFileClose.bError THEN    
            fbFileClose.bExecute := FALSE;
            iState := 99;
        END_IF;

    11:    IF NOT fbFileClose.bBusy AND NOT fbFileClose.bError THEN
            fbFileClose.bExecute     := FALSE;
            iState := 88;
        ELSIF fbFileClose.bError THEN            
            fbFileClose.bExecute := FALSE;
            iState := 99;
        END_IF;
    
    88: bExecute := FALSE;
        iState := 0;
    
    99: bError := TRUE;
        iState := 88;
END_CASE
fbGetLocalAmsNetId();
fbFileOpen();
fbFileWrite();
fbFileClose();

Ich habe mitlerweile herausgefunden, dass im State 6 der SIZEOF für Arrays so nicht anwendbar ist und für den zusätzlichen Datenmüll verantwortlich ist (LENOF geht auch nicht) - nur habe ich noch nicht herausgefunden wie man nun ein Array als Textdatei speichert :? Jede Hilfe willkommen ;)
 
Du müsstest zunächst alle Strings des Arrays (mit der tatsächlichen Länge) zu einem String zusammenfassen (z.B. CONCAT) oder zumindest lückenlos (oder mit nur einem Trennzeichen) hintereinander weg im Speicher zusammenkopieren und nur den dabei belegten Speicher in die Datei schreiben. Der fbFileWrite schreibt ja nicht die Strings in die Datei, sondern den vom Array insgesamt belegten (und großen Teils unbenutzten) Speicherplatz.
Oder die Strings einzeln nacheinander als einzelne Zeilen in die Datei schreiben.

Mit welchem Programmiersystem programmierst Du denn?

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Das wird etwas aufwändiger. Du musst die Strings hintereinander in einen Byte-Puffer packen und dabei die tatsächliche Stringlänge berücksichtigen. Ausserdem die Nullterminierung durch LF für den Zeilenumbruch ersetzen.

Ah, zu spät.
 
Programmiere mit TwinCAT3.

Ich habe ein Array von 40 Strings mit einer Länge von 255 Zeichen, da weiss ich gar nicht ob die über CONCAT zu einem String zusammen geführt werden können? War da nicht was mit maximal 255 Zeichen die eine String Funktion maximal verarbeiten kann? Das mit dem unbenutzten Speicherplatz ergibt Sinn, ist wohl der Grund warum soviel "Müll" mitgeschrieben wird wobei ich nicht genau verstehe wo der herkommt. ich definiere doch die Größe und somit Speicherplatz meines Arrays :confused:

Habe jetzt mal auf die Schnelle versucht die Strings einzeln aus dem Array in einen String zu kopieren und zu speichern, da wird ebenfalls 'Müll' mit gespeichert :neutral:

Code:
                Teststring := LEFT(GVL_Trace.ConcatTrace[j],LEN(GVL_Trace.ConcatTrace[j]));
                fbFileWrite.pWriteBuff    := ADR(Teststring);
                fbFileWrite.cbWriteLen    := SIZEOF(Teststring);


Dachte ich könnte so den Index j nacheinander durchlaufen lassen aber der Test mit einer Zeile hat schon "Müll" mit gespeichert. Auch mein Filter"-Versuch mit LEFT und LEN des Array-Strings hat nichts gebracht :sm25:


Wie realisiere ich den Byte-Puffer am einfachsten, hierüber?

https://infosys.beckhoff.com/index....l/tcplclibutilities_fb_membuffermerge.htm&id=
 
Bei Strings musst Du zwischen der Datentypgrösse und der tatsächlichen Stringlänge unterscheiden. Ein String(100) ist immer 101 Byte gross (100 Zeichen + Nullterminierung), auch wenn nur '123' drinsteht. Und SizeOf liefert die Grösse des Datentyps, nicht die aktuelle Stringlänge. Und dass Müll drinsteht, liegt daran, dass bei einer Stringzuweisung der alte Inhalt nicht zwangsläufig komplett gelöscht wird. Wenn z. B in einer Stringvariable znächst "ABCDEFGH" drinsteht und dann Variable:='IJKL' ausgeführt wird, wird 'ABCD' durch 'IJKL' ersetzt, das 'E' wird durch den Wert 0 ersetzt, um das neue Stringende zu markieren, und 'FGH' bleibt drin stehen.

CONCAT kann, wie Du schon sagst, nur Strings bis zu 255 Zeichen verarbeiten und scheidet damit aus. Der Beckhoff Puffer-FB mag funktionieren, ist aber wohl nicht für so etwas gedacht.
Schau Dir lieber mal die eweiterten Stringfunktionen in der Tc2 Utilities Lib an, die können Strings mit bis zu 10000 Zeichen verarbeiten. Wenn Du die 255 Zeichen nicht bei jedem String ausnutzt, könnten die Funktionen reichen. Du musst dafür allerdings an die einzelnen Strings ein LF Zeichen anhängen.

Ansonsten selbermachen. Hier mal ein Ansatz:
Code:
FUNCTION BLOCK StringBuffer
VAR_OUTPUT
   Buffer:ARRAY[0..32767] OF BYTE;   // Puffer
   Fill:UINT;   // Aktueller Füllstand
END_VAR

METHOD AppendString
VAR_INPUT
   Strg:T_MaxString;   // String to append
END_VAR
VAR
   SLen:UINT;   // String length
END_VAR

SLen:=LEN(Strg);
IF (Fill+SLen+1) < 32768 THEN
   MEMCPY(                                  // String in Puffer kopieren
      destAddr:=ADR(Buffer[Fill]),
      srcAddr:=ADR(Strg),
      n:=SLen);
   Buffer[Fill+SLen]:=10;   // LF zeichen anhängen
   Fill:=Fill+SLen+1;   // Fill aktualisieren
END_IF
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich kenne TwinCAT nicht, vermutlich kann man den FB_MemBufferMerge verwenden. Eventuell gibt es auch anderer schnelle String-Kopierfunktionen.

Du könntest die Strings "zu Fuß" etwa so zusammenkopieren (ungetestet):
Code:
MyStringArray : ARRAY [0..39] OF STRING; // 40 Strings je max 255 Zeichen
WriteBuffer : ARRAY [0..10279] OF CHAR;  // 40 * (255 + 2)
ptStr : POINTER TO CHAR;                 // Pointer auf die Zeichen des StringArray
ptBuf : POINTER TO CHAR;                 // Pointer auf AusgabePuffer
c : CHAR;


// die Strings aus dem StringArray lückenlos in den AusgabePuffer kopieren
ptBuf := ADR(WriteBuffer);               // Pointer auf Anfang AusgabeArray

FOR i := 0 TO 39 DO                      // für jeden String des StringArray
  ptStr := ADR(MyStringArray[i]);        // Pointer auf String-Anfang
  c := ptStr^;                           // erstes Zeichen aus dem String

  // String in Ausgabepuffer kopieren ohne das abschließende 0-Byte
  WHILE c <> 0 DO
    ptBuf^ := c;             // das Zeichen in den Ausgabepuffer übernehmen
    ptBuf := ptBuf + 1;      // und die Pointer weiterstellen
    ptStr := ptStr + 1;      // ="=
    c := ptStr^;             // nächstes String-Zeichen
  END_WHILE;

  // '$r$n' Zeilenschaltung anfügen
  ptBuf^ := '$r';
  ptBuf := ptBuf + 1;
  ptBuf^ := '$n';
  ptBuf := ptBuf + 1;
END_FOR;

// Länge für fbFileWrite
cbWriteLen := ptBuf - ADR(WriteBuffer);

Harald
 
Bei Strings musst Du zwischen der Datentypgrösse und der tatsächlichen Stringlänge unterscheiden. Ein String(100) ist immer 101 Byte gross (100 Zeichen + Nullterminierung), auch wenn nur '123' drinsteht. Und SizeOf liefert die Grösse des Datentyps, nicht die aktuelle Stringlänge. Und dass Müll drinsteht, liegt daran, dass bei einer Stringzuweisung der alte Inhalt nicht zwangsläufig komplett gelöscht wird. Wenn z. B in einer Stringvariable znächst "ABCDEFGH" drinsteht und dann Variable:='IJKL' ausgeführt wird, wird 'ABCD' durch 'IJKL' ersetzt, das 'E' wird durch den Wert 0 ersetzt, um das neue Stringende zu markieren, und 'FGH' bleibt drin stehen.

CONCAT kann, wie Du schon sagst, nur Strings bis zu 255 Zeichen verarbeiten und scheidet damit aus. Der Beckhoff Puffer-FB mag funktionieren, ist aber wohl nicht für so etwas gedacht.
Schau Dir lieber mal die eweiterten Stringfunktionen in der Tc2 Utilities Lib an, die können Strings mit bis zu 10000 Zeichen verarbeiten. Wenn Du die 255 Zeichen nicht bei jedem String ausnutzt, könnten die Funktionen reichen. Du musst dafür allerdings an die einzelnen Strings ein LF Zeichen anhängen.

Ansonsten selbermachen. Hier mal ein Ansatz:

...

Vielen Dank, es funktioniert :) Ich habe mir die erweiterten Strings angeguckt, die sehen auch sehr interessant aus - leider habe ich damit keinen Erfolg gehabt :confused:

Somit funktioniert es, falls jemand mal danach suchen sollte:

Code:
Funktionsabschnitt:

FILL := 0;

FOR i := 1 TO 40 DO
    sLen := LEN(Trace[i].Msg);
    IF (FILL + sLen + 1) < 32768 THEN
        MEMCPY(
              destAddr :=ADR(Buffer[Fill]),
              srcAddr   :=ADR(Trace[i].Msg),
              n:=sLen);
        Buffer[GVL_Trace.Fill+SLen]:=10;    
        Fill:=GVL_Trace.Fill+SLen+1;   
    END_IF
END_FOR

Anpassen des iState 6 des Eingangspost:

6:   fbFileWrite.pWriteBuff    := ADR(Buffer);
      fbFileWrite.cbWriteLen    := FILL;
 
Zurück
Oben