TC3 Strukturen in CSV Datei schreiben

Minehunter

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

ich bin neu hier im Forum und mein erster Beitrag. Hoffe ich stelle mich nicht allzu dumm an.

Ich muss mich seit urzem mit Beckhoff TC3 beschöftigen.
Wir planen eine System, welches Messdaten aufzeichnet und in absehbarer Zeit in eine Datenbank schreiben soll.
Um die Zeit bis zur Datenbankanwendung zu überbrücken, sollen die Daten in eine CSV gespeichert werden
So weit so gut.


Die Messdaten werden in einer Struktur vorgegen, um sie global für alle Prüflinge zu anpassen zu können.
Die Struktur besteht aus verschiedenen Variablen-Datentypen, aber keine inneren Arrays oder weitere Strukturen.



So, nun meine Frage:
1. Ist es möglich / Sinnvoll Strukturen für solch ein schreiben in ein Datei zu verwenden?
2. Wie kann man die Struktur mittels einer Schleife, komma-separiert dem Funktionsblock FileWrite übergeben, bzw. in einen String umwandeln, wenn man die Reihenfolge der Datentypen nicht kennt?



Der Funktionsblock FileWrite bietet zwar die Möglichkeien Strukturen in eine Datei zu schreiben,
leider habe ich da keine komma-separierung.

fbWriteFile(
sNetId:= '',
hFile:= hFile,
pWriteBuff:= ADR(stPruefling),
cbWriteLen:= SIZEOF(stPruefling),
bExecute:= TRUE,
tTimeout:= T#1S,
bBusy=> ,
bError=> ,
nErrId=> ,
cbWrite=>
);


Daher benötige ich irgendwie eine schleife mit umwandlung der Daten in einen String inkl Kommaseparierung.


Hat einer eine Idee, wie man die umsetzen kann, oder ist mein Ansatz ggf. Falsch?

Vielen Dank im Voraus.
 
Hallo Minehunter,
vielleicht wäre das Folgende eine Idee:

Nehmen wir an Deine Struktur hätte die Elemente "Temperatur, Druck, Drehzahl, Spannung", dann könntest Du Dir ein Enum definieren mit genau diesen Elementen. Als nächstes definierst Du Dir ein Array dieses Enum, dessen Größe der maximal möglichen Anzahl (evtl. +1) an Messwertelementen entspricht. Dieses füllst Du dann mit der gewünschten Reihenfolge (z.B. Spannung, Druck) und entweder am Schluss noch mit einem ebenfalls im Enum definierten Ende-Element oder Du legst Dir in einer Variable die Anzahl der verwendeten Elemente ab. Wenn jetzt ein Datensatz geschrieben werden soll gehst Du das Array Element für Element durch und füllst einen String, die Auswahl der Elemente kannst Du per Case-Anweisung treffen, anschließend nach jedem Element das Trennzeichen noch drangehängt und das Ganze nachdem zusammenstückeln weg schreiben. Im String würde also jetzt z.B. "120Bar;12.5V;" stehen. Zur besseren Übersicht könntest Du in der CSV-Datei am Anfang noch eine Titelzeile erstellen mit den erfassten Werten.
Habe gerade gesehen, dass es in TC3 eine Funktion gibt, die aus einem String ein CSV-Element macht, damit könntest Du Dir Klimmzüge mit CONCAT schenken und müsstest nicht selber für ein Element Strings aneinanderstückeln.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen Dank für deine Hilfe
Ich werde mir deine Idee morgen einmal anschauen und einmal ausprobieren.

Kannst du mir noch sagen, wie die Funktion heist, welche aus einem String ein csv Element macht.
Ich bemühe mich gerade mit der CONCAT zu Tode. Hab aber auf Anhieb nichts finden können.

Danke im Voraus.
 
Ich mache das immer so:

In Codesys V3, in V2 geht das auch und auch in TC2 aber dann gibt es diverse Enumerationen, Datentypen nicht bei der Deklaration, Code ist ansonsten portierbar. Es gibt in V2 z.B. CmpErrors.RTS_IEC_HANDLE nicht. Da muss man dann soweit ich mich erinnere einfach ein DWORD stattdessen nutzen. Am besten mittels Kompilerfehler durchhangeln und mit den entsprechenden Bibliotheken vergleichen.

1. Eine Funktion die mir Strings an ein Bytearray anhängt

Code:
FUNCTION FUN_AddString : DWORD
VAR_INPUT
 byBuffer   : POINTER TO ARRAY [0..MAX_BYTES_TO_EXPORT] OF BYTE;
 nActWritePos  : DWORD;
 strAddText   : STRING(255);
END_VAR
VAR
 i       : DWORD  := 0;
 pTemp    : POINTER TO BYTE;
 iLength    : UINT := 0;    
END_VAR

// Alte Version über Schleife = langsam (falls Memmove nicht vorhanden)
(*pTemp := ADR(strAddText);
FOR i := nActWritePos TO nActWritePos + INT_TO_DWORD(LEN(strAddText)) DO
 byBuffer^[i] := pTemp^;
 pTemp := pTemp + 1;
END_FOR;
FUN_AddString := i-1;
*)

// Neue Version = Memmove = Performant!
iLength := LEN(strAddText);
mem.MemMove(
 pDestination := ADR(byBuffer^[nActWritePos]),
 pSource := ADR(strAddText), 
 uiNumberOfBytes := iLength
);


FUN_AddString := nActWritePos + iLength;

2. In einem Programm ein Puffer definieren mit der Arraylänge die man zum schreiben braucht. Achtung: Nicht ein Riesenarray nutzen, sondern immer nur eine Zeile einer CSV abschätzen mit Freiraum hinten dann jeden Datensatz separat in Datei speichern:

Code:
PROGRAMM CSVERZEUGEN
VAR
 rtsHandle : CmpErrors.RTS_IEC_HANDLE;
 rtsResult : CmpErrors.RTS_IEC_RESULT;
 strTargetFileName : STRING(255);
 strSourceFileAndPath : STRING(255);
 strTargetFileAndPath : STRING(255);
 
 nWritePos : DWORD := 0;
 byWriteBuffer : ARRAY [0..MAX_BYTES_TO_EXPORT] OF BYTE;


END_VAR

// Zieldatei öffnen
rtsHandle := file.SysFileOpen(
     szFile := strTargetFileAndPath,
     am :=  file.AM_WRITE,
     pResult := ADR(rtsResult)
    );

// Puffer zusammenbauen
// Zuerst die Überschrift und allgemeine Daten in Datei schreiben
nWritePos := 0;
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, 'Parameterdatei als CSV Export$R$N');
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, '===================================$R$N$R$N');
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, '$R$N$R$N');



nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, 'ALLGEMEINE DATEN$R$N');
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, 'Nr.;Spaltenname1;Spaltenname2;Spaltenname3;Spaltenname4;Text$R$N');

// Schreiboperation auslösen, Handle geöffnet lassen da anschließend weiter geschriben wird

file.SysFileWrite(rtsHandle, ADR(byWriteBuffer), nWritePos,ADR(rtsResult));
nWritePos := 0; // Schreibzeiger nach Schreiben wieder auf 0 setzen damit der Puffer wieder von Anfang an verwendet werden kann


// Nun die gesamten Daten Satz für Satz in Datei schreiben (Achtung, der Puffer muss groß genug sein sonst schreibst du ins Nirvana! Hier gibt es keine Prüfung, könnte man noch einbauen, dass man nicht auf nWritepos > MAX_BYTES_TO_EXPORT abfragt)
FOR i := 0 TO MAX_DATASETS_TO_SAVE DO

// Feld für Feld durchlaufen und in String wandeln, für alle Datentypen gibt es ja _TO_STRING Funktionen
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, INT_TO_STRING(i) );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, ';' ); // Mittels ; die nächste Zelle wählen,...
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, REAL_TO_STRING(stParametersatz[i].Wert1) );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, ';' );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, DINT_TO_STRING(stParametersatz[i].Wert2) );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, ';' );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, BOOL_TO_STRING(stParametersatz[i].Wert3) );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, ';' );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, INT_TO_STRING(stParametersatz[i].Wert4) );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, ';' );
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, stParametersatz[i].strIrgendeinString);
nWritePos := FUN_AddString(ADR(byWriteBuffer), nWritePos, '$R$N' ); // $R$N ist der Code für Zeilenvorschub, in ASCII 

file.SysFileWrite(rtsHandle, ADR(byWriteBuffer), nWritePos,ADR(rtsResult));
nWritePos := 0;

END_FOR;

file.SysFileFlush(hFile:= rtsHandle);
file.SysFileClose(rtsHandle);

Wie du sehen kannst, ist es zwar ein Haufen Code aber eigentlich immer das gleiche. Musst halt wissen wie du mit der FUN_AddSTring umgehst dann ist das wirklich Pippifax so eine hübsche CSV Datei zu erstellen.

Um es noch kürzer zu machen könnte man die nWritePos auch per Referenz übergeben (ADR(nWritepos)) dann spart man sich das "nWritePos := " vor jeder Zeile.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Vielen Dank die Links waren sehr nützlich.
Habe mir nun eine Lösung mit dem STRING_TO_CSVFIELD und CsvMemBuffer gebastelt.
Geht eigentlich recht einfach.

Danke für die Hilfe
 
Zurück
Oben