Beckhoff TwinCAT PLC - Alle 10 Sekunden Werte von Variablen in eine CSV-Datei

mv08

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

ich sitze derzeit daran ein für Profis sehr einfach Programm mi ST zu schreiben.
Leider verzweifel ich derzeit daran alle 10 Sekunden eine Zeile in eine CSV-Datei zu schreiben.
Was ich geschafft habe, ist das Schreiben eines Arrays in die CSV-Datei mit Hilfe der Anleitung von hier: http://infosys.beckhoff.com/content/1031/tcplclib_tc2_utilities/html/tcplclibutilities_csv_sample.htm?id=15741

Der entsprechende Programmcode ist dieser hier:

Die Variablen:
Code:
PROGRAM P_TextModeWrite
(* Writing 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
    bWrite            : BOOL := FALSE;(* Rising edge starts program execution *)
    sNetId            : T_AmsNetId := '192.168.2.109.1.1';    (* TwinCAT system network address *)
    sFileName    : T_MaxString := '\Hard Disk\test\TextModeGen.csv';(* CSV destination file path and name *)
    sCSVLine        : T_MaxString := '';(* Single CSV text line (row, record), we are using string as record buffer (your are able to see created fields) *)
    sCSVField        : T_MaxString := '';(* Single CSV field value (column, record field) *)
    sTest            : T_MaxString :='DasTest';
    bBusy            : BOOL;
    bError            : BOOL;
    nErrId            : UDINT;
    nRow             : UDINT     := 0;(* Row number (record) *)
    nColumn        : UDINT     := 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 *)
    fbFilePuts        : FB_FilePuts;(* Writes one record (line) *)
    fbWriter        : FB_CSVMemBufferWriter;(* Helper function block used to create CSV data bytes (single record line) *)


    database        : ARRAY[0..MAX_CSV_ROWS, 0..MAX_CSV_COLUMNS ] OF STRING(MAX_CSV_FIELD_LENGTH) := (* Source PLC database *)
    '0_0', '0_1', '0_2', '0_3', '0_4', '0_5',
    '1_0', '1_1', '1_2', '1_3', '1_4', '1_5',
    '2_0', '2_1', '2_2', '2_3', '2_4', '2_5',
    '3_0', '3_1', '3_2', '3_3', '3_4', '3_5',
    '4_0', '4_1', '4_2', '4_3', '4_4', '4_5',
    '5_0', '5_1', '5_2', '5_3', '5_4', '5_5';


END_VAR
Der Code:
Code:
CASE step OF
    0:    (* Wait for rising edge at bWrite variable *)
        IF bWrite THEN
            bWrite         := FALSE;
            bBusy         := TRUE;
            bError        := FALSE;
            nErrId        := 0;
            hFile        := 0;
            nRow         := 0;
            nColumn    := 0;
            step         := 1;
        END_IF


    1:    (* Open source file *)
        fbFileOpen(  bExecute := FALSE  );
        fbFileOpen(     sNetId := sNetId, sPathName := sFileName, nMode := FOPEN_MODEWRITE OR FOPEN_MODETEXT,(* Open file in TEXT mode! *)
                        ePath := PATH_GENERIC, bExecute := TRUE );
        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:(* Convert one PLC record to CSV format *)
        sCSVLine := '';
        fbWriter.eCmd := eEnumCmd_First;(* Write first field value *)
        IF nRow <= MAX_CSV_ROWS THEN


            FOR nColumn := 0 TO MAX_CSV_COLUMNS BY 1 DO


                sCSVField := STRING_TO_CSVFIELD( sTest, FALSE );(* TODO: Get field value from your application *)


                (* Add new field to the record buffer *)
                fbWriter(     pBuffer := ADR( sCSVLine ), cbBuffer := SIZEOF( sCSVLine ) - 1, putValue := sCSVField, pValue := 0, cbValue := 0,
                            bCRLF := ( nColumn = MAX_CSV_COLUMNS ) );(* bCRLF == TRUE => Write CRLF after the last field value *)
                IF fbWriter.bOk THEN
                    fbWriter.eCmd := eEnumCmd_Next;(* Write next field value *)
                ELSE(* Error *)
                    step := 100;
                    RETURN;
                END_IF


            END_FOR(* FOR nColumn := 0... *)


            (* FB_FilePuts adds allready CR (carriage return) to the written line.
            We have to replace the $R$L characters with $L character to avoid double CR. *)
            IF RIGHT( sCSVLine, 2 ) = '$R$L' THEN
                sCSVLine := REPLACE( sCSVLine, '$L', 2, LEN( sCSVLine ) - 1 );
            END_IF


            nRow := nRow + 1;(* Increment number of created records (rows) *)
            step := 4;(* Write record to the file *)


        ELSE(* All rows written => Close file *)
            step := 10;
        END_IF


    4:    (* Write single text line *)
        fbFilePuts( bExecute := FALSE );
        fbFilePuts( sNetId := sNetId, hFile := hFile, sLine := sCSVLine, bExecute := TRUE );
        step := 5;


    5:(* Wait until write not busy *)
        fbFilePuts( bExecute := FALSE, bError => bError, nErrID => nErrID );
        IF NOT fbFilePuts.bBusy THEN
            IF NOT fbFilePuts.bError THEN
                step := 3;(* Write next record *)
            ELSE(* Error *)
                step := 100;
            END_IF
        END_IF


    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 := 101;    (* Ready *)
        END_IF


END_CASE

Nach meinem Verständnis müsste ich nun bei Step 3 die Schleife ändern und dann irgendwie alle 10 Sekunden eine neue Zeile in die Datei schreiben. Bei beiden vorhaben komme ich derzeit nicht weiter.
Könnt ihr mir weiterhelfen?
Vielen Dank!
LG,
Dennis
 
Anbei mal ne ganz schnelle schlampige Idee von mir.
Vielleicht hiflts ja weiter. Ist ungetestet, aber ich denke die Idee ist klar.
Viel Spaß damit

Code:
    3:(* Convert one PLC record to CSV format *)
        sCSVLine := '';
        fbWriter.eCmd := eEnumCmd_First;(* Write first field value *)
        IF nRow <= MAX_CSV_ROWS THEN


            FOR nColumn := 0 TO MAX_CSV_COLUMNS BY 1 DO


                sCSVField := STRING_TO_CSVFIELD( sTest, FALSE );(* TODO: Get field value from your application *)


                (* Add new field to the record buffer *)
                fbWriter(     pBuffer := ADR( sCSVLine ), cbBuffer := SIZEOF( sCSVLine ) - 1, putValue := sCSVField, pValue := 0, cbValue := 0,
                            bCRLF := ( nColumn = MAX_CSV_COLUMNS ) );(* bCRLF == TRUE => Write CRLF after the last field value *)
                IF fbWriter.bOk THEN
                    fbWriter.eCmd := eEnumCmd_Next;(* Write next field value *)
                ELSE(* Error *)
                    step := 100;
                    RETURN;
                END_IF


            END_FOR(* FOR nColumn := 0... *)


            (* FB_FilePuts adds allready CR (carriage return) to the written line.
            We have to replace the $R$L characters with $L character to avoid double CR. *)
            IF RIGHT( sCSVLine, 2 ) = '$R$L' THEN
                sCSVLine := REPLACE( sCSVLine, '$L', 2, LEN( sCSVLine ) - 1 );
            END_IF


            nRow := nRow + 1;(* Increment number of created records (rows) *)
            step := 4;(* Write record to the file *)


        ELSE(* All rows written => Close file *)
            step := 10;
        END_IF

Hi zuerst würde ich mir mit einem Ton eine weiterschaltbedingung machen, dass ich weitermachen darf:

Code:
MyWaitTON(IN:=startNextColumn , PT:=t#10ms , Q=> , ET=> );

Dann die Statemachine vielleicht so aufdrösseln

Code:
    3: (*Starte die Zeit an deinem Ton*)
        startNextColumn := TRUE;
        step := 4;

    4: (* Convert one PLC record to CSV format *)
        sCSVLine := '';
        fbWriter.eCmd := eEnumCmd_First;(* Write first field value *)
        nColumn := 0;
        IF nRow <= MAX_CSV_ROWS THEN
           step := 5;
        ELSE
            step := 10;
        END_IF
      5: (**)
               startNextColumn := TRUE;
               sCSVField := STRING_TO_CSVFIELD( sTest, FALSE );(* TODO: Get field value from your application *)
                (* Add new field to the record buffer *)
                fbWriter(     pBuffer := ADR( sCSVLine ), cbBuffer :=  SIZEOF( sCSVLine ) - 1, putValue := sCSVField, pValue := 0, cbValue :=  0,
                            bCRLF := ( nColumn = MAX_CSV_COLUMNS ) );(*  bCRLF == TRUE => Write CRLF after the last field value *)
                IF fbWriter.bOk THEN
                    fbWriter.eCmd := eEnumCmd_Next;(* Write next field value *)
                ELSE(* Error *)
                    step := 100;
                    RETURN;
                END_IF
         6: IF  nColumn < TO MAX_CSV_COLUMNS BY
              nColumn := nColumn +1;
              step := 7;
             ELSE
              step 8=;
             END_IF
           
         7: IF MyWaitTON.Q THEN
               startNextColumn := FALSE;
               step := 5;
             END_IF




           8: (* FB_FilePuts adds allready CR (carriage return) to the written line.
            We have to replace the $R$L characters with $L character to avoid double CR. *)
           IF RIGHT( sCSVLine, 2 ) = '$R$L' THEN
                sCSVLine := REPLACE( sCSVLine, '$L', 2, LEN( sCSVLine ) - 1 );
            END_IF


            nRow := nRow + 1;(* Increment number of created records (rows) *)
            step := 4;(* Write record to the file *)


        ELSE(* All rows written => Close file *)
            step := 10;
        END_IF

EDit!!!
Ups ich habe gerade gesehen, dass du die Zeilen, nicht die Spalten alle 10ms schreiben willst. Aber vielleicht siehst du ja jetzt ungefähr wie es geht.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hier jetzt nochmal ne anständige Lösung (natürlich auch ungetestet, aber die Idee müsste klar sein).

Der Code:
Code:
(*neues ton einfügen. Das TON geht erst nach 10s an, wenn die Eingangsvaribele IN TRUE ist*)
startNewRowTON(IN:= startNewRow, PT:=t#10s , Q=> , ET=> );

CASE step OF
....

    3:(* Convert one PLC record to CSV format *)
(*-------------> Hier wird die Zeit gestartet, bei der du in das ton reinschreiben darfst.*)
        startNewRow := TRUE;
                        ....
    
   4:    (* Write single text line *)
                        ...


    5:(* Wait until write not busy *)
        fbFilePuts( bExecute := FALSE, bError => bError, nErrID => nErrID );
        IF NOT fbFilePuts.bBusy THEN
            IF NOT fbFilePuts.bError 
(*erst weiterschalten, wenn die 10ms abgelaufen sind*)
           AND startNewRowTON.Q
            THEN
(*startNewRow zurücksetzten, weil sonst das Ton nicht mehr bei 0 startet*)
                startNewRow := FALSE;
                step := 3;(* Write next record *)
            ELSE(* Error *)
                step := 100;
            END_IF
        END_IF
                       ...
END_CASE

Hoffe das hilft. Bei Fragen einfach fragen. :)
Gruß
wonderfulworld
 
Leider scheint der TON bei mir nicht zu greifen. Er rast einfach ohne 10s Pause durch die Schleifen:

ganz oben:
Code:
startNewRowTON(IN:= startNewRow, PT:=t#10s , Q=> , ET=> );

CASE step OF
	0:	(* Wait for rising edge at bWrite variable *)
.
.
.

und im Code:
Code:
	5:(* Warten, wenn der Writer noch beschäftigt ist *)
		fbFilePuts( bExecute := FALSE, bError => bError, nErrID => nErrID );
		IF NOT fbFilePuts.bBusy THEN
			IF NOT fbFilePuts.bError THEN
			IF  zaehler < 10 THEN
			startNewRow := TRUE;
			(*erst weiterschalten, wenn die 10s abgelaufen sind*)
	        		startNewRowTON.Q;
			(*startNewRow zurücksetzten, weil sonst das Ton nicht mehr bei 0 startet*)
                		startNewRow := FALSE;
			zaehler := zaehler +1;
			step := 4;
			ELSE (*Fertig, jetzt Datei schließen*)
				step := 10;
			END_IF
			ELSE(* Error *)
				step := 100;
			END_IF
		END_IF

Kannst du einen Fehler erkennen?

Vielen Dank!
Dennis
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Code:
IF  zaehler < 10 THEN
	startNewRow := TRUE; 
	startNewRowTON.Q;
           startNewRow := FALSE;
...

Da ist dein Fehler, dein TON Aufruf ganz oben bekommt nie ein StartNewRow := TRUE da du die Variable in jedem Zyklus zwar auf True, aber direkt danach wieder auf False setzt. Den Sinn deiner Zähler-Variable verstehe ich auch nicht ganz!?
Hätte jetzt erwartet, dass step := 10 durchgeführt wird sobald startNewRowTON.Q auf True geht oder?
 
Den Zähler kannst du ignorieren, der war nur zum Test drinnen, damit ich keine Schleife habe.
Aber jetzt stehe ich mit dem TON an der Stelle auf dem Schlauch. Wie müsste denn der Quellcode aussehen, damit das TON funktioniert?

Code:
5:(* Warten, wenn der Writer noch beschäftigt ist *)
		fbFilePuts( bExecute := FALSE, bError => bError, nErrID => nErrID );
		IF NOT fbFilePuts.bBusy THEN
			IF NOT fbFilePuts.bError THEN
			IF  zaehler < 10 THEN
			startNewRow := TRUE;
			(*erst weiterschalten, wenn die 10s abgelaufen sind*)
	        		startNewRowTON.Q;
			(*startNewRow zurücksetzten, weil sonst das Ton nicht mehr bei 0 startet*)
                		startNewRow := FALSE;
			zaehler := zaehler +1;
			step := 4;
			ELSE (*Fertig, jetzt Datei schließen*)
				step := 10;
			END_IF
			ELSE(* Error *)
				step := 100;
			END_IF
		END_IF
 
so müsste es klappen
Code:
tonDelay(IN := xStartDelay, PT := T#10MS);
xStartDelay := NOT tonDelay.Q;
IF tonDelay.Q THEN
	//Anweisungen, welche nach 10 sekunden erfolgen sollen
END_IF
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Bei diesem Programm vom Beckhoff bin ich gerade auch. (Erster Beitrag).
Ich habe 10 Werte (Real, INT,...) und würde diese gerne in die CSV Datei schreiben, in etwa so:

'Messdaten1', 'Strom', Spannung, 'P', Q, 'W,
'Messdaten2', '1_1', '1_2', '1_3', '1_4', '1_5',
'Messdaten3', '2_1', '2_2', '2_3', '2_4', '2_5',
'Messdaten4', '3_1', '3_2', '3_3', '3_4', '3_5',
'Messdaten5', '4_1', '4_2', '4_3', '4_4', '4_5',
'Messdaten6', '5_1', '5_2', '5_3', '5_4', '5_5';

Strom, Spannung usw. berechne ich extern und schreibe das in dieses Array. Ich hoffe jemand kann mir einen Tipp geben. Danke.
 
Zurück
Oben