Data Logging – CSV in CODESYS erstellen

CODESYS-Blog DataLogger

Über kurz oder lang wird euch beim Programmieren das Thema „Data Logging“ beschäftigen. Hierfür gibt es zahlreiche Beispiele. So kann zum Beispiel in einer Fertigungsanlage die Protokollierung von Messdaten oder Umweltdaten bei der Fehlersuche bzw. zur Fehlerbehebung herangezogen werden. Ebenso ist durch eine umfassende Protokollierung der Maschinendaten eine Analyse über den Zustand der Anlage möglich – Stichwort „Big Data und Predictive Maintenance“. Ihr seht also es gibt eine Vielzahl an Gründen, die für ein Logging der Daten sprechen.

Mögliche Varianten des Loggers

Genauso viele Möglichkeiten gibt es, das Logging durchzuführen. Hierbei kann das Logging durch die Komponenten der Maschine selbst erfolgen, oder durch einen extern angebrachten Datenlogger. In diesem kurzen Blog-Beitrag möchte ich mich dem Data Logging in CODESYS widmen. Auch hier gibt es schon mehrere Möglichkeiten. Ich kann externe Software einsetzten und über OPC, OPC-UA, Modbus, etc. Daten abgreifen und Loggen. Genauso gibt es unzählige Möglichkeiten in CODESYS Werte zu Loggen. Es können zum Beispiel die Daten über ein Web Service in eine DB eingetragen werden, oder ich kann in CODESYS durch ein Gateway die Daten direkt in eine Datenbank schreiben. Das alles sind großartige Möglichkeiten.

Wir betrachten hier jedoch zunächst einmal die einfachste Form. Ich werde die Daten in eine CSV-Datei schreiben -> eine großartige Möglichkeit den Umgang mit Dateien in CODESYS zu erlernen. Hierfür gibt es schon vorgefertigte Bibliotheken und Funktionen. Z.B. die OSCAT Library und die CSV utility Funktionen im CODESYS Store.

DataLogging mithilfe von .csv und Funktionsbausteinen

Prinzipiell sind hierfür nur 3 Schritte und die Verwendung der „File Access“ Bibliothek erforderlich.

  1. Öffnen der CSV Datei oder anlegen einer neuen Datei mithilfe von Bausteinen der CAA File (File Access)
  2. Eintragen der Daten in die .csv Datei (CAA file.Write)
  3. Schließen der geöffneten Datei nach erfolgreichem Schreiben (CAA file.Close)

Bibliothek einfügen

Zunächst müssen wir die „File Access“ Bibliothek in CODESYS einbinden. Klicke hierzu Bibliotheksverwalter (1) ⇒ Bibliothek hinzufügen (2)

CODESYS Bibliotheksverwalter
CODESYS Bibliotheksverwalter

Suchmaske „File“ eingeben (1) ⇒ Bibliothek auswählen (2) OK (3)

Liste der CODESYS Bibliotheken
Liste der CODESYS Bibliotheken

CAA File Funktionsbausteine

Damit wir Daten in unser Dateisystem schreiben können, benötigen wir einige Bausteine aus der „File Access“ Bibliothek. Diese können wir in einer Variablenliste deklarieren. Für den Zugriff auf die Bausteine benötigen wir den Namespace „File“.  Weiter oben im Artikel hatte ich erwähnt, dass prinzipiell nur drei Schritte notwendig sind: öffnen, werte eintragen und Datei wieder schließen. Genau dafür benötigen wir die drei Bausteine.

PROGRAM PLC_PRG
VAR
	//function block for file handling
	fopen  : FILE.Open;
  	fwrite : FILE.Write;
  	fclose : FILE.Close;
END_VAR
CAA FileAccess Bausteine für Data Logging
CAA FileAccess Bausteine für Data Logging

1. Open

Mit dem Funktionsbaustein zum Öffnen der Datei könnt Ihr erntescheiden ob Ihr eine neue Datei erstellen wollt, oder eine vorhandene öffnen und euren Inhalt anfügen wollt. Dazu könnt Ihr am Parametereingang „sFileMode“ verschiedene Werte angeben:

  • File.MODE.MRDWR um eine neue Datei zu erstellen
  • File.MODE.MAPPD um die vorhandene Datei zu erweitern
PROGRAM PLC_PRG
//create or open my log-file
        fopen.sFileName:=sFileName;
        fopen.eFileMode:=File.MODE.MRDWR; //File.MODE.MAPPD; << append data
        fopen.xExclusive:=TRUE;
        fopen( xExecute:=TRUE);
        IF fopen.xDone THEN
            hFile:=fopen.hFile;
        END_IF

Wenn die Datei erfolgreich geöffnet wurde, erhaltet Ihr ein sogenannten Datei-Handle (hier hFile variable). Das ist eure temporäre Referenz zu dieser Datei. Wir speichern den Referenzbereich in unserer Variable, da wir über das Datei-Handle beim schreiben/lesen und schließen der Datei dem Betriebssystem genau mitteilen können welchen Speicherbereich wir verarbeiten wollen.

2. Write

Das Schreiben der Daten ist prinzipiell genauso einfach. Hier übergeben wir dem Baustein für den Schreibzugriff einfach die Adresse und die Größe der String Variable die wir in unsere Datei schreiben wollen. Dazu verwenden wir die Funktionen:

  • ADR() : liefert die Adresse des übergebenen Arguments/Variable
  • LEN() : bestimmt die Datenlänge
PROGRAM PLC_PRG
//write log data to file        
        fwrite.hFile:=hFile;
        fwrite.pBuffer:=ADR(sStringToWrite);
        fwrite.szSize:=LEN(sStringToWrite);
        fwrite.udiTimeOut:=100000;	//Timeout
        fwrite( xExecute:=TRUE);
        IF fwrite.xDone THEN
		fwrite( xExecute:=FALSE);
        END_IF

Da wir eine CSV-Datei erstellen wollen, können wir natürlich nicht einfach STRING Variablen in unsere Datei schreiben. Wir müssen den Aufbau unserer Textdatei an das CSV Format anpassen.

Innerhalb der Datei haben wir einige Zeichen mit einer Sonderfunktion zur Strukturierung der Daten.

  • Ein Zeichen wird zur Trennung von Datensätzen benutzt. Dafür wird meistens der Zeilenumbruch verwendet. Wir verwenden hier „$R$N“. Das sind zwar zwei Zeichen, ist aber meistens so üblich.
  • Ein weiteres Zeichen wird zur Trennung von Daten (unseren Spalten) innerhalb der Datensätze benutzt. Das kann mit Semikolon, Doppelpunkt, Tabulatorzeichen, Leerzeichen oder andere Zeichen realisiert werden. Allgemein wird dafür meistens das Komma eingesetzt, welches wir ebenfalls nutzen werden.

Im gesamten Code Beispiel weiter unten, seht Ihr wie mit der STRING Funktion „CONCAT“, mehrere Strings zu genau solch einem CSV Konformen Aufbau zusammen gesetzt werden.

3. Close

Abschließend müssen wir die Datei, nach erfolgreicher Bearbeitung, wieder schließen. Dazu verwenden wir den Baustein fclose. Wie oben erwähnt übergeben wir auch diesem das Datei-Handle, damit klar geregelt ist, welche Datei wir schließen wollen.

PROGRAM PLC_PRG
//close log file
        fclose.hFile:=hFile;
        fclose( xExecute:=TRUE);
        IF fclose.xDone THEN
		fopen( xExecute:=FALSE);
		fwrite( xExecute:=FALSE);
		fclose( xExecute:=FALSE);
        END_IF

Data Logging – Gesamtes CODESYS Programm

Nun, da Ihr die einzelnen Schritte kennt, möchte ich euch noch Beispielhaft ein Code Snippet bereitstellen. Damit könnt Ihr einfach gemessene Daten, aus einem Array (measuredData), in die CSV-Datei schreiben.

PROGRAM PLC_PRG
VAR
	bWriteData:BOOL:=FALSE;		//trigger to write log-data
	uiStpDataLogger:UDINT:=0;	//sequencer
	
	//File name and handle (path is only for windows)
	sFileName:  CAA.FILENAME:= 'C:\CODESYS-Blog\logData.csv';;	//../Log/logData.csv << Relative paths in the local file system
    hFile:      CAA.HANDLE;
	
	//example data
	measuredData: ARRAY [1..5] OF logData := [(index:='16#DEADBEEF',data:='dead beef'),(index:='16#C001CAFE',data:='cool cafe'),(index:='16#BADCAB1E',data:='bad cable'),(index:='16#BADC0DED',data:='bad coded'),(index:='16#C0EDBABE',data:='coed babe')];	//Hexspeak
    sStringToWrite:    STRING:='no data';
	writeIndex:UINT:=1;
	
	//control characters
	CRLF:STRING(2):= '$R$N';
	LF:STRING(1):= '$N';
	
	//function block for file handling
	fopen  : FILE.Open;
  	fwrite : file.Write;
  	fclose : file.Close;
END_VAR


CASE uiStpDataLogger OF
	(*wait for trigger*)
	0: 	IF bWriteData THEN
			bWriteData:=FALSE;
			uiStpDataLogger:=10;
		END_IF

	10:	(* create or open my log-file *)
        fopen.sFileName:=sFileName;
        fopen.eFileMode:=File.MODE.MRDWR; //File.MODE.MAPPD;
        fopen.xExclusive:=TRUE;
        fopen( xExecute:=TRUE);
        IF fopen.xDone THEN
            hFile:=fopen.hFile;
			sStringToWrite:='';
			writeIndex:=1;
            uiStpDataLogger:=20;
        END_IF
		
	20: (*generate data*)
		sStringToWrite:=CONCAT(measuredData[writeIndex].index,',');
		sStringToWrite:=CONCAT(sStringToWrite,measuredData[writeIndex].data);
		sStringToWrite:=CONCAT(sStringToWrite,CRLF);
		//sStringToWrite:=CONCAT(sStringToWrite,LF);
		uiStpDataLogger:=30;
		
	30:	(* write log data to file *)
        fwrite.hFile:=hFile;
        fwrite.pBuffer:=ADR(sStringToWrite);
        fwrite.szSize:=LEN(sStringToWrite);
        fwrite.udiTimeOut:=100000;	//Timeout
        fwrite( xExecute:=TRUE);
        IF fwrite.xDone THEN
			fwrite( xExecute:=FALSE);
			writeIndex:=writeIndex+1;
			IF writeIndex <= 5 THEN
					uiStpDataLogger:=20;
					sStringToWrite:='';
				ELSE
					uiStpDataLogger:=40;
			END_IF
        END_IF
        IF fwrite.xError THEN
			//Error
            uiStpDataLogger:=16#DEADBEEF;
        END_IF
		
	40:  (* close log file *)
        fclose.hFile:=hFile;
        fclose( xExecute:=TRUE);
        IF fclose.xDone THEN
			fopen( xExecute:=FALSE);
			fwrite( xExecute:=FALSE);
			fclose( xExecute:=FALSE);
            uiStpDataLogger:=0;
        END_IF
        IF fclose.xError THEN
            //Error
            uiStpDataLogger:=16#DEADBEEF;
        END_IF

	16#DEADBEEF:
		//ErrorHandling
		;
END_CASE

Fazit

Natürlich sind nicht alle Ausnahmen abgefangen. So kann es zum Beispiel sein, dass Ihr Daten an eine Datei anhängen wollt und diese noch gar nicht existiert. Oder Ihr eventuell gar keine Schreibberechtigung auf die Ressource habt.

Es gibt hier noch so viele Punkte, die beim Data Logging, beachtet werden müssen. Allerdings wollte ich euch mit dem kurzen Beispiel einen netten Überblick verschaffen. Solltet Ihr hier näheres wissen wollen, dann schreibt mir einfach.

Ein Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website benutzt Cookies und Google Analytics. Wenn du die Website weiter nutzt, gehen wir von deinem Einverständnis aus Weitere Informationen

Die Cookie-Einstellungen auf dieser Website sind auf "Cookies zulassen" eingestellt, um das beste Surferlebnis zu ermöglichen. Wenn du diese Website ohne Änderung der Cookie-Einstellungen verwendest oder auf "Akzeptieren" klickst, erklärst du sich damit einverstanden.

Schließen