Zu Langsames erstellen einer .csv Datei und deren Export

Gizzl0r

Level-1
Beiträge
142
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,
ich erstelle ein 2-Dimensionales Array welches ich mit Messwerten fülle die Variabel sind, je nach Verfahrgeschwindigkeit der Achse sind es mal 50 Daten und mal 2500 Daten.
Daher habe ich meine Array so gestaltet
database : ARRAY[0..2500, 0..2 ] OF STRING;

Diese werden mit den Bausteinen FB_File Bausteinen erstellt und auf eine Externe 2.5 Zoll Festplatte geschrieben die an meiner CX5xxx von Beckhoff per USB angeschlossen ist.
Jedoch braucht er vom Ende des Datensammeln -> Erstellen der Datei und Daten reinschreiben gute 5-7 Sekunden. In der Zeit ist jedoch bereits der nächste Messpunkt aktiv und er übernimmt nur ein Teil der 2. Messwerte....
Gibt es eine Möglichkeit diese Prozedur zu beschleunigen? Der PLC Task läuft bereits mit 0.5ms.
Gruß
Daniel
 
Hast Du mal einen USB-Stick versucht - nur um die Schreibgeschwindigkeit des Datenträgers als Ursache auszuschließen.
Alternativ könntest Du NOVRAM verwenden, um die Daten zu sammeln und am Stück zu schreiben.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi,
USB-Stick hatte ich schon ausprobiert, da ich auch dachte das die Festplatte evtl. das Nadelöhr ist. Jedoch keine Besserung. Die NOVRAM Geschichte ist doch nur bei bestimmten CX Modellen möglich oder Irre ich da?
 
Bzgl. NOVRAM: Hat das Gerät keinen kann eine Klemme nachgerüstet werden.

Wenn die Platte nicht das Nadelöhr ist, könnte es evtl Dein Code sein?
Füg doch mal bei jedem Schritt eine Zeitmessung hinzu. dann kannst Du im Code erstmal die Stelle mit dem Nadelöhr identifizieren und auf Ursachenforschung gehen.
Erstellst Du jedesmal eine neue Datei? Möglicherweise gehts schneller, wenn die Datei schon da ist und fortgeschrieben wird.

Die File-Opreationen laufen ja IMHO asynchron über die Windows-Unterlage. Wenn die Task mit 0,5ms schon für ordentlich Systemauslastung sorgt kann auch da der Flaschenhals entstehen.
 
Zuletzt bearbeitet:
Ich glaub eher dass das Problem woanders liegt.
Die Bausteine von Beckhoff zum Schreiben und Lesen von Strings sind auf 255 Zeichen (T_MaxString ALIAS STRING(255)) begrenzt.
Das heißt er kann pro Durchlauf maximal mit dieser Größe umgehen.

Ich hatte ein ähnliches Problem beim Lesen von Csv Dateien mit ca. 60 Zeichen pro Zeile und 30000 Zeilen.
Ich hab mir die Stringfunktionen dann so umgeschrieben, dass sie mit Pointerarithmetik (wie bei C) arbeiten und somit die Größe nur mehr von der Länge des Strings abhängt --> Nulltherminierumg.
Ab da ging das Schreiben wesentlich schneller da du beim FB_FileWrite auch 1000Byte unter einmal rausschreiben kannst und nicht nur 255.

Ich glaube in deinem Fall ergibt das 2500*80=200000 da STRING standardmäßig 80 Zeichen lang ist und das ganze mal 3 da von 0..2 in der zweiten Dimension vom Array.
Wenn man davon ausgeht, dass er alle 3 Zyklen 255 Byte rausschreiben kann, dann kommst du auf eine Zeit von 3,5 Sek (600000/255*0,0015 --> 1.5us). Vermutlich wird es nicht immer 3 sondern mehr Zyklen dauern und so ergibt sich deine Zeit.
--> Glaub ich...

Sg
seehma
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hab mir die Stringfunktionen dann so umgeschrieben, dass sie mit Pointerarithmetik (wie bei C) arbeiten und somit die Größe nur mehr von der Länge des Strings abhängt --> Nulltherminierumg.
Ähm, das gibts schon, und zwar in der Bibliothek Tc2_Utilities (Len2, Concat2, etc.)
 
Hi
bedanke mich schonmal für eure Antworten.
@ Eddi
Len2 gibt mir ja nur die Stringlänge aus, soweit ich das verstehe. Und Concat reiht mir 2 Strings aneinander. Wie hilft mir das bei meinem Problem? Oder habe ich da was falsch verstanden?
 
Der TE schreibt leider nicht, ob er jeden String einzeln in die Datei schreibt oder ob er FB_FileWrite verwendet.

Am schnellsten funktioniert sicherlich, zunächst (ggf. über mehrere Zyklen) die Daten aus dem String-Array (nur die tatsächlichen Stringlängen) lückenlos hintereinander in einen Puffer (CHAR- oder BYTE-Array) zu kopieren, und dann den Puffer (mit der tatsächlich belegten Länge) per FB_FileWrite auf die Festplatte zu schreiben.

Harald
 
Jupp. Ich war mal von der schnellsten Methode (Filewrite) ausgegangen, weil die Daten ja prinzipiell schon in einem Puffer liegen (Array).
Wenn natürlich jede Zeile einzeln geschrieben wird kostet das Zeit.
 
Sobald ein Pruefkoerper meinen Prüfling berührt, fängt er an die Daten zu sammeln. Dieses tut er so lange bis der Pruefkoerper nicht mehr in Kontakt mit dem Prüfkörper steht. Dannach erstellt er die Datei und füllt diese mit Fileput. Unten der Code:

VAR CONSTANT
ARRAY_UPPER_BOUND : INT:= 2500;
END_VAR
Code:
//Startbedingung Datensammeln Datenbank
IF (GL_VAR.Pruefkoerper) OR ( NOT GL_VAR.Pruefkoerper_ohne_kontakt AND GL_VAR.KMZ_Anzeige > 5  AND GL_VAR.Widerstand < 8000) THEN b_sammeln:=TRUE; ELSE b_sammeln:=FALSE; 	  END_IF;
bWrite:= NOT b_sammeln AND GL_VAR.Direction_z_ret; //OR GL_VAR.Messung_fertig);
bCreate:= GL_VAR.start_b OR GL_VAR.start_e OR GL_VAR.start_m OR GL_VAR.start_end;
IF row>=2501 THEN b_sammeln:=FALSE; row:=0; column:=0; END_IF;






IF NOT b_sammeln   THEN 
		


         row:=0; Column := 0  ;            				 
ELSE
cyclecount:=cyclecount+1;	
IF GL_VAR.Newton > 400 AND GL_VAR.Messung =2
	THEN Cyclecount2 := 50;
	ELSE Cyclecount2 := 25;
END_IF
IF 


cyclecount=Cyclecount2 THEN   
    cyclecount:=0;
                          database[row+10,0]:=Widerstand;
                          database[row+10,1]:=GL_VAR.KMZ_String;
						  database[row+10,2]:=GL_VAR.hoehe_dif;
                          row:=row+1;
						  
						
									  
END_IF;  
	
END_IF;
IF GL_VAR.Messung_fertig
	THEN
	 row:=0; column:=0;
END_IF








//Systemzeit ermitteln
systemzeit(
    sNetID:= , 
    bEnable:=TRUE ,
    dwCycle:=5 ,
    dwOpt:=1 ,
    tTimeout:= , 
    bValid=> ,
    systemTime=> ,
    tzID=> );






CASE step OF


0: //Bedingung bWrite erfüllt
IF bWrite THEN
bWrite := FALSE;
bBusy := TRUE;
bError := FALSE;
nErrId := 0;
hFile := 0;
nRow := 0;
nColumn := 0;
step := 1;
END_IF




ePath := SEL( bBootFolder, PATH_GENERIC, PATH_BOOTPATH );
sPathName := SEL( bBootFolder, CONCAT('D:\Pruefberichte\', GL_VAR.Ordnername), sFolderName );


IF bCreate THEN
        bCreate := FALSE;
        fbCreateDir( bExecute := FALSE );
        fbCreateDir(sNetId:= '',
                sPathName:= sPathName,
                ePath:= ePath,
                bExecute:= TRUE,
                tTimeout:= DEFAULT_ADS_TIMEOUT,
                bBusy=>bCreate_Busy, bError=>bCreate_Error, nErrId=>nCreate_ErrID );
ELSE
        fbCreateDir( bExecute := FALSE, bBusy=>bCreate_Busy, bError=>bCreate_Error, nErrId=>nCreate_ErrID );
END_IF






1: //Öffne Quelldatei




//Dateinnamen zusammensetzen




bastelstring(
        sFormat:='%s\P%2u_%2u-%2u-%4u_%2u-%2u-%2u.csv',	
  		arg1:=F_STRING(sPathName) ,	
		arg2:=F_WORD(MAIN.Dimensionen.Punkt) ,
        arg3:=F_WORD(systemzeit.systemTime.wDay) ,
        arg4:=F_WORD(systemzeit.systemTime.wMonth) ,
        arg5:=F_WORD(systemzeit.systemTime.wYear) ,
		arg6:=F_WORD(systemzeit.systemTime.wHour) ,
		arg7:=F_WORD(systemzeit.systemTime.wMinute) ,
		arg8:=F_WORD(systemzeit.systemTime.wSecond) ,
        sOut=>sFileName );




		
//Öffne Datei im Text Mode		
fbFileOpen( bExecute := FALSE );
fbFileOpen( sNetId := sNetId, sPathName := sFileName, nMode := FOPEN_MODEWRITE OR FOPEN_MODETEXT,
ePath := PATH_GENERIC, bExecute := TRUE );
step := 2;
2:// Wearten bis open nicht busy *)
fbFileOpen( bExecute := FALSE, bError => bError, nErrID => nErrID, hFile => hFile );
IF NOT fbFileOpen.bBusy THEN
IF NOT fbFileOpen.bError THEN
step := 3;
ELSE//Fehler: Datei nicht gefunden
step := 100;
END_IF
END_IF


3:// Konvertierung in .CSV Format
sCSVLine := '';
fbWriter.eCmd := eEnumCmd_First; //Schreiben der ersten Zeile
IF nRow <= MAX_CSV_ROWS THEN
FOR nColumn := 0 TO MAX_CSV_COLUMNS BY 1 DO
sCSVField := STRING_TO_CSVFIELD( database[ nRow, nColumn ], FALSE );//Daten aus dem Array lesen




//Neues Feld in Buffer Schreiben
fbWriter( pBuffer := ADR( sCSVLine ),
cbBuffer := SIZEOF( sCSVLine ) - 1,
putValue := sCSVField, pValue := 0,
cbValue := 0,
bCRLF := ( nColumn = MAX_CSV_COLUMNS ));
IF fbWriter.bOk THEN
fbWriter.eCmd := eEnumCmd_Next;


ELSE


step := 100;
RETURN;


END_IF


END_FOR




IF RIGHT( sCSVLine, 2 ) = '$R$L' THEN
sCSVLine := REPLACE( sCSVLine, '$L', 2, LEN( sCSVLine ) - 1 );
	
END_IF
nRow := nRow + 1;//Inkrementieren der Zeile
step := 4;
ELSE	
Memset(ADR(Database), 0, SIZEOF(Database));
step := 10;
END_IF


4: //Schreibe einzelne Zeile
fbFilePuts( bExecute := FALSE );
fbFilePuts( sNetId := sNetId, hFile := hFile, sLine := sCSVLine, bExecute := TRUE );
step := 5;


5:// Warten bis Schreiben nicht busy
fbFilePuts( bExecute := FALSE, bError => bError, nErrID => nErrID );
IF NOT fbFilePuts.bBusy THEN			
IF NOT fbFilePuts.bError THEN	
step := 3;
ELSE


step := 100;
END_IF
END_IF


10:// Erstelle Quell Datei
fbFileClose( bExecute := FALSE );
fbFileClose( sNetId := sNetId, hFile := hFile, bExecute := TRUE );
step := 11;


11:// Warten bis Schließen nicht busy
fbFileClose( bExecute := FALSE, bError => bError, nErrID => nErrID );
IF ( NOT fbFileClose.bBusy) THEN
hFile := 0;
step := 100;


END_IF


100: //Beenden
IF ( hFile <> 0 ) THEN	
step := 10; 


ELSE


bBusy := FALSE;
step := 0; 
END_IF
END_CASE
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Codeteile kommen mir bekannt vor ;)

In der Quelle wird ein großes Array in einem Rutsch geschrieben. An der Stelle wo das vorformatierte Array aus dem NOVRAM zurückgelesen wird. Der Sinn des Codes war ja, nicht viele kleine Zugriffe zu machen sondern einen großen.
Zwar vor dem Hintergrund der Schreibhäufigkeit auf Flash-Datenträger - doch der Zeitvorteil ist auf jeden Fall auch ein Argument. Festplatten haben einen RAM-Cache mindestens in typischer Blockgröße.
 
Zuletzt bearbeitet:
Hi
bedanke mich schonmal für eure Antworten.
@ Eddi
Len2 gibt mir ja nur die Stringlänge aus, soweit ich das verstehe. Und Concat reiht mir 2 Strings aneinander. Wie hilft mir das bei meinem Problem? Oder habe ich da was falsch verstanden?
Die Idee war, daß man erst alle Strings (z.b. CSV-Zeilen) zu einem großen String zusammenfaßt, und dann nur eine Schreiboperation hat, anstatt für jede Zeile einzeln eine Schreiboperation zu machen.

Das braucht i.d.R. mehr RAM, aber weniger Zeit. Insbesondere braucht das Strings länger als 256 Zeichen, deshalb die erweiterten Stringfunktionen nutzen. (Die sind theoretisch unbegrenzt, die Tc2_Utilities begrenzt das aber auf 10000 Zeichen, um Endlosschleifen zu vermeiden. Diesen Wert kann man erhöhen)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Meine Scham hält sich in Grenzen.
1. Funktionierts
2. Hab ich das ganze überarbeitet
3. bin ich bestenfalls ambitioniert aber kein Könner und schon garkein Profi.
 
@weißnix
hast du denn noch die Quelle da ;) Ist schon so lange her das ich mich an dieser bedient habe :D Würde dieses auch ohne NOVRAM funktionieren?
Also Fileputs raus und Filewrite reinpacken?
 
Messwerte von Start bis Ende der Messung nach Excel exportieren

Da war mein Konzeptcode dabei. Das ganze funzt auch ohne NOVRAM nach kleinen Modifikationen. NOVRAM war bei mir nur bequem, um bei Netzausfall keine Daten zu verlieren. Der Kenner wird im Code sofort die gefährliche Stelle erkennen, an der es trotzdem passieren kann. Ich hatte den NOVRAM schon zum neubeschreiben freigegeben ohne den Schreibvorgang auf die Platte zu verifizieren.

Schritt 31 der Statemachine schreibt einfach nur ein Array auf die Platte. Die Länge des Arrays und die Startadresse sind die notwendigen Parameter.
Es ist eigentlich egal ob es wie oben ein Bytearray oder ein Stringarray ist. Damit ein lesbares csv dabei rauskommt muss dass Array nur entsprechend formatiert sein. Das kann gleich beim befüllen erledigt werden. Ein (furchtbares ;) ) Beispiel ist auch im Code. Wenn Du bei der Formatierung ob des Ergebnisses nicht sicher bist schau Dir zur Kontrolle das resultierende File im Binärmodus an.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi habe dein Schritt 31 und 32 in meinem Code statt der FilePuts Anweisung eingefügt und die Schritte angepasst. Jetzt hat er mir ne 6 MB Große Datei geschrieben :p. Keine Formatierung mehr vorhanden :/
Wo liegt mein Fehler?

Code:
4: //Schreibe einzelne ZeilefbFileWrite( bExecute := FALSE );
fbfilewrite(sNetId:= sNetId ,hFile:=openhandle ,pWriteBuff:=ADR(database),cbWriteLen:=1000 ,bExecute:= TRUE,tTimeout:=T#3S ,bBusy=> ,bError=> ,nErrId=> ,cbWrite=>cbwrite_vrfy );
step := 5;




5:// Warten bis Schreiben nicht busy
fbfilewrite(bExecute:=FALSE);
IF NOT fbFileWrite.bBusy THEN            
IF NOT fbFileWrite.bError THEN    
step := 3;
ELSE




step := 100;
END_IF
END_IF




10:// Erstelle Quell Datei
fbFileClose( bExecute := FALSE );
fbFileClose( sNetId := sNetId, hFile := hFile, bExecute := TRUE );
step := 11;




11:// Warten bis Schließen nicht busy
fbFileClose( bExecute := FALSE, bError => bError, nErrID => nErrID );
IF ( NOT fbFileClose.bBusy) THEN
hFile := 0;
step := 100;




END_IF




100: //Beenden
IF ( hFile <> 0 ) THEN    
step := 10; 




ELSE




bBusy := FALSE;
step := 0; 
END_IF
END_CASE
 
Zuletzt bearbeitet:
Zurück
Oben