# CSV erstellen



## Messi90 (21 Oktober 2015)

Mahlzeit!

Ich möchte gerne eine csv Datei erstellen für die Protokollierung von Messwerten.
Gibt es dafür fertige FB´s?
Ich habe es mit den FB´s von Oscat Network versucht aber, warum auch immer, es wird keine csv angelegt.
Anbei Screenshots zur Verdeutlichung.

Kann mir jemand sagen was ich falsch mache? Erstellt Codesys selbst die csv oder muss diese vorher im vorgegebenen Pfad angelegt werden?

Eckdaten:
Visuprogramm: Galileo 10 von Eaton
Codesys 3.5.6
Steuerung: XV 303
Lib: Oscat Basic 3.3.3.0 und Network 1.2.1.0


----------



## Hendrik (21 Oktober 2015)

Hey Messi90,

du wirst definitiv erst eine Datei anlegen müssen.

Bei TwinCat gibt es dafür FBs. 

- FB_FileOpen 
- FB_FileWrite
- FB_FileClose

Hier kannst du dir das Beispiel ansehen wie es Beckhoff machen. Da alles Codesys ist wird es bei dir genauso oder so ähnlich funktionieren.

http://infosys.beckhoff.com/index.htm

ps.: Wichtig wenn du mit Dateien arbeitest musst du die Fehler auffangen und die Datei definiert wieder schliesen.

MfG 
Hendrik


----------



## Messi90 (21 Oktober 2015)

Danke Hendrik. Nur leider stehe ich jetzt wieder einmal vor dem Problem, dass ich nicht mit Beckhoff arbeite und somit keine libs von TwinCat habe... Und da es mal wieder kaum möglich ist von Eaton eine passende lib zu finden, habe ich OSCAT in Erwägung gezogen. Theoretisch sollte das ja problemlos funktionieren, wenn ich nur wissen würde wo mein Fehler liegt


----------



## Hendrik (21 Oktober 2015)

Du programmierst aber mit Codesys oder?


----------



## Messi90 (21 Oktober 2015)

Ja. Codesys 3.5.6, allerdings eben mit der Variante XSoft von Eaton.


----------



## Hendrik (21 Oktober 2015)

http://store.codesys.com/csv-utility.html

hilft das eventuell?


----------



## Messi90 (23 Oktober 2015)

Um das zu nutzen müsste ich es kaufen und das möchte ich nicht.

Ich habe es nun durch Hilfe vom Support soweit zum Laufen bekommen, dass ich csv Datein bekomme.
Dabei ergeben sich neue Probleme:


Im Anhang ist eine Beispiel-CSV wie sie momentan aussieht. Dabei fällt auf, dass sich manche Werte doppelt und in der falschen Reihenfolge erstellen. 
Der Wert der Temperatur wird im falschen Format erkannt (Datum) 
Die Werte von Druck, Strecke und Geschwindigkeit sind falsch. 
Die Datei wird nur einmal am Tag angelegt. Besser wäre es jedoch, wenn sich die Datei bei jedem manuellen Speichern neu anlegt. 
 
Hat jemand einen Rat für mich?


```
IF Start_CSV = TRUE THEN
(*Data logging every 5s*)
Timer(IN:=NOT Timer.q , PT:=Messintervall2);
xStrobeWriteData:=Timer.q;



(*Take Header if the file is new*)
IF xStrobeWriteData THEN
    uiLoggingCounter:=uiLoggingCounter+1;
    (*Header*)
    sHeader:='Datum;Zeit;Temperatur;Druck;Strecke;Geschwindigkeit;$N';
(*sHeader:='Datum;Zeit;Temperatur;Druck;Strecke;Geschwindigkeit;$0D$0A';*)

    (*Date and Time for file name and data*)
    S40_GetRealTimeClock1(
    Month=>usiMonth ,
    Day=>usiDay ,
    Hour=>usiHour ,
    Minute=>usiMinute ,
    Second=>usiSecond );
    uiYear:=USINT_TO_UINT(S40_GetRealTimeClock1.Year)+2000;

    (*Data String*)

    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiDay));
    sDataString1:= CONCAT(sDataString1,'.');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiMonth));
    sDataString1:= CONCAT(sDataString1,'.');
    sDataString1:= CONCAT(sDataString1,UINT_TO_STRING(uiYear));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiHour));
    sDataString1:= CONCAT(sDataString1,':');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiMinute));
    sDataString1:= CONCAT(sDataString1,':');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiSecond));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,REAL_TO_STRING(PLC_PRG.Output_Temp));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1, REAL_TO_STRING(PLC_PRG.Output_Druck));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,REAL_TO_STRING(PLC_PRG.Strecke_Encoder));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1, REAL_TO_STRING(PLC_PRG.Geschwindigkeit_Encoder));
    sDataString1:= CONCAT(sDataString1,'$N');
(*sDataString1:= CONCAT(sDataString1,'$0D$0A');*)



    (*Filename for data logging. Every day a new file name*)
    IF xSD_card THEN
        sDataloggingFileName:= '\StorageCard\' ;
    ELSE
        sDataloggingFileName:= '\UsbStorage\' ;
    END_IF
    (*sDataloggingFileName:= CONCAT(sDataloggingFileName, 'Directory\');*)
    sDataloggingFileName:= CONCAT(sDataloggingFileName, 'DataloggingFile_F');
    sDataloggingFileName:= CONCAT(sDataloggingFileName,'_');
    sDataloggingFileName:= CONCAT(sDataloggingFileName,USINT_TO_STRING(usiDay));
    sDataloggingFileName:= CONCAT(sDataloggingFileName,'-');
    sDataloggingFileName:= CONCAT(sDataloggingFileName,USINT_TO_STRING(usiMonth));
    sDataloggingFileName:= CONCAT(sDataloggingFileName,'-');
    sDataloggingFileName:= CONCAT(sDataloggingFileName,UINT_TO_STRING(uiYear));
    sDataloggingFileName:= CONCAT(sDataloggingFileName,'.csv');

    (*Header or Data*)
    xHeader:=sDataloggingFileNameOld<>sDataloggingFileName;
    sDataloggingFileNameOld:=sDataloggingFileName;
END_IF

END_IF


OpenWriteCloseFileAsync1(
    xStrobeWriteData:=xStrobeWriteData ,
    xHeader:=xHeader ,
    sHeader:=sHeader ,
    sDataString:=sDataString1 ,
    sFileName:=sDataloggingFileName ,
    xFileOpen=> ,
    xDone=> , 
    xBusy=> , 
    xOpen=> , 
    xWriteHeader=> , 
    xWrite=> , 
    xClose=> , 
    udiErrorCounterOpen=> , 
    udiErrorCounterWrite=> , 
    udiErrorCounterClose=> , 
    udiBytesWrittenHeader=> , 
    udiBytesWritten=> , 
    udiWriteCounter=> );
```


```
R_Trig_StrobeWriteData(clk:=xStrobeWriteData);
IF R_Trig_StrobeWriteData.q AND NOT xBusy THEN
    xOpen:=TRUE;
    xDone:=FALSE;
    xBusy:=TRUE;
    xHeaderIntern:=xHeader;
    udiBytesWrittenHeader:=0;
    udiBytesWritten:=0;
    xError:=FALSE;
END_IF

my_SysFileOpenAsync(bEnable:= xOpen,stFileName:=sFileName ,stMode:= 'a' );


IF  NOT my_SysFileOpenAsync.bBusy  AND my_SysFileOpenAsync.bDone AND NOT my_SysFileOpenAsync.bError THEN    (* open OK-->prepare write*)
    xOpen:=FALSE;
    xWrite:=TRUE;
    xWriteHeader:=xHeaderIntern;
    dwhdlFile:=my_SysFileOpenAsync.hFile;
END_IF

IF  my_SysFileOpenAsync.bDone AND  my_SysFileOpenAsync.bError THEN                (* open  with error--> no write*)
    xWrite:=FALSE;    (* no write with no handle*)
    udiErrorCounterOpen:=udiErrorCounterOpen+1;
    xError:=TRUE;
    xOpen:=FALSE;
    my_SysFileOpenAsync(bEnable:= FALSE,stFileName:=sFileName ,stMode:= 'a' );
END_IF


IF xHeaderIntern THEN
    my_SysFileWriteAsync(bEnable:=xWrite ,hFile:=dwhdlFile ,pBuffer:=ADR(sHeader),dwSize:=LEN(sHeader) ,dwWrite=>udiBytesWritten );
    IF NOT my_SysFileWriteAsync.bBusy AND my_SysFileWriteAsync.bDone AND NOT my_SysFileWriteAsync.bError THEN   (*write OK-->initiate close*)
        xHeaderIntern:=FALSE;
        xWriteHeader:=FALSE;
        my_SysFileWriteAsync(bEnable:=FALSE ,hFile:=dwhdlFile ,pBuffer:=ADR(sHeader),dwSize:=LEN(sHeader) ,dwWrite=>udiBytesWritten );
    END_IF    
    IF  my_SysFileWriteAsync.bDone AND  my_SysFileWriteAsync.bError THEN                    (* write error*)
        udiErrorCounterWrite:=udiErrorCounterWrite+1;
        xError:=TRUE;
        xWrite:=FALSE;
        my_SysFileWriteAsync(bEnable:=FALSE ,hFile:=dwhdlFile ,pBuffer:=ADR(sHeader),dwSize:=LEN(sHeader) ,dwWrite=>udiBytesWritten );
    END_IF
ELSE
    my_SysFileWriteAsync(bEnable:=xWrite ,hFile:=dwhdlFile ,pBuffer:=ADR(sDataString),dwSize:=LEN(sDataString) ,dwWrite=>udiBytesWritten );
    IF NOT my_SysFileWriteAsync.bBusy AND my_SysFileWriteAsync.bDone AND NOT my_SysFileWriteAsync.bError THEN   (*write OK-->initiate close*)
        xWrite:=FALSE;
        xClose:=TRUE;
    END_IF
    IF  my_SysFileWriteAsync.bDone AND  my_SysFileWriteAsync.bError THEN                    (* write error*)
        udiErrorCounterWrite:=udiErrorCounterWrite+1;
        xError:=TRUE;
        xWrite:=FALSE;
        my_SysFileWriteAsync(bEnable:=FALSE ,hFile:=dwhdlFile ,pBuffer:=ADR(sDataString),dwSize:=LEN(sDataString) ,dwWrite=>udiBytesWritten );
    END_IF
END_IF

my_SysFileCloseAsync(bEnable:= xClose,hFile:=dwhdlFile );

IF NOT my_SysFileCloseAsync.bBusy AND my_SysFileCloseAsync.bDone AND NOT my_SysFileCloseAsync.bError THEN
    xClose:=FALSE;
    xDone:=TRUE;
    xBusy:=FALSE;
    udiWriteCounter:=udiWriteCounter+1;
END_IF

IF my_SysFileCloseAsync.bDone AND  my_SysFileCloseAsync.bError THEN
    udiErrorCounterClose:=udiErrorCounterClose+1;
    xError:=TRUE;
    xClose:=FALSE;
    my_SysFileCloseAsync(bEnable:=FALSE,hFile:=dwhdlFile );
END_IF

IF xError THEN
    xDone:=TRUE;
    xBusy:=FALSE;
END_IF
```


----------



## Hendrik (23 Oktober 2015)

Hey Punkt zwei kann ich dir erklären =)

Excel denkt es ist ein Datum in der Datei steht aber das richtige -> einfach mal mit einem Texteditor öffnen.

Wenn du das umgehen möchtest musst du ein Temperaturformat wählen, welches nicht wie ein Datum ausschaut. Hier vll ohne Kommstelle, aber da musst du wiesen wie genau du das brauchst.

Zu Punkt 1 und 3 nur eine Idee du löscht glaube ich deinen String den du wegschreibst nicht oder ich habe es noch nicht gefunden.

einfach bevor du mit dem zusammensetzen anfängst leer machen

```
(*Data String*)
sDataString1:= ''; (* leer machen *)
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiDay));
    sDataString1:= CONCAT(sDataString1,'.');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiMonth));
    sDataString1:= CONCAT(sDataString1,'.');
    sDataString1:= CONCAT(sDataString1,UINT_TO_STRING(uiYear));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiHour));
    sDataString1:= CONCAT(sDataString1,':');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiMinute));
    sDataString1:= CONCAT(sDataString1,':');
    sDataString1:= CONCAT(sDataString1,USINT_TO_STRING(usiSecond));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,REAL_TO_STRING(PLC_PRG.Output_Temp));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1, REAL_TO_STRING(PLC_PRG.Output_Druck));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1,REAL_TO_STRING(PLC_PRG.Strecke_Encoder));
    sDataString1:= CONCAT(sDataString1,';');
    sDataString1:= CONCAT(sDataString1, REAL_TO_STRING(PLC_PRG.Geschwindigkeit_Encoder));
    sDataString1:= CONCAT(sDataString1,'$N');
```

vll hilft dir das.

MfG
Hendrik


----------



## Messi90 (23 Oktober 2015)

Wuuaaaa super danke dir Hendrik! Ich Idiot hab genau diese Zeile selber rausgelöscht :lol:
Also wegen der Temperatur hat sich auch geklärt. Wenn der Wert stimmt ist mir das Wurscht wie der in der Excel angezeigt wird. Später wirds eh geplottet.

So, nun hab ich trotzdem noch die Probleme 3 und 4, dass die Werte Strecke, Druck und Geschw. nicht stimmen und das Abspeichern :-(
Das Problem 3 kann ja nicht am Real-Typ liegen oder? Wäre jetzt noch mein einziger Gedanke...


----------



## Hendrik (23 Oktober 2015)

Wie sollten die Realwerte aussehen? 

Es gibt da ein Problem mit der Formatierung/Umwandlung  von Real_to_String ....

Dafür gibt es separate FBs

bei Beckhoff heist das ding:
*FUNCTION LREAL_TO_FMTSTR
*


http://infosys.beckhoff.com/index.htm


----------



## Messi90 (23 Oktober 2015)

Oha also liegt es wahrscheinlich echt daran. Wie genau ist deine Frage jetzt gemeint? Ich erwarte Werte in folgenden Bereichen:
Druck: 0...1,6bar
Geschwindigkeit: 0...200cm/min
Temperatur: 0...200 °C (evtl. auch ab -30°C)
Strecke: 0...200m


----------



## Hendrik (23 Oktober 2015)

Fragte weil es hier bei Kommawerten auch wieder zu den Formatierungsproblemen von Excel kommen könnte.

hast du schon eine Funktion gefunden um es Richtig umzuwandeln?


----------



## Messi90 (23 Oktober 2015)

Nein, leider nicht


----------



## Hendrik (23 Oktober 2015)

Probier es mal damit.


```
FUNCTION REAL_TO_STRING_PREC : STRING
VAR_INPUT
    In:    REAL;
    Precision:    UINT;
END_VAR
VAR
    LongIn:        LREAL;
    tmpVal:        LREAL;
END_VAR

REAL_TO_STRING_PREC := '';

LongIn := REAL_TO_LREAL(In);
(* Alles vor dem Komma wandeln *)
tmpVal := LTRUNC(LongIn);
REAL_TO_STRING_PREC := DINT_TO_STRING(LREAL_TO_DINT(tmpVal));

IF Precision = 0 THEN
    REAL_TO_STRING_PREC := CONCAT(REAL_TO_STRING_PREC,'');
ELSE
    REAL_TO_STRING_PREC := CONCAT(REAL_TO_STRING_PREC,'.');
    tmpVal := LongIn - LTRUNC(LongIn);
    tmpVal := tmpVal * EXPT(UINT_TO_LREAL(10), UINT_TO_LREAL(Precision));
    REAL_TO_STRING_PREC := CONCAT(REAL_TO_STRING_PREC ,UDINT_TO_STRING(LREAL_TO_UDINT(tmpVal)));
END_IF
```


MfG Hendrik


----------



## PN/DP (23 Oktober 2015)

Welches Dezimaltrennzeichen (Komma oder Punkt) man in der CSV-Datei verwenden sollte hängt davon ab, welche regionalen Einstellungen auf dem Zielcomputer mit dem Excel verwendet werden (in der Regel bei Deutsch ',' / sonst oft '.'). Danach richtet Excel seine Format-Interpretationen, wenn man die CSV-Datei per Doppelklick öffnet. Weichen die regionalen Einstellungen vom CSV-Format ab, dann kann man die CSV-Datei mit dem Import-Assistent als Text importieren und da die Trennzeichen festlegen. Oder man schreibt sich sicherheitshalber eine eigene VBA-Routine zum Einlesen der CSV-Datei und umgeht dabei die lokalen regionalen Einstellungen.

Harald


----------

