DLOG_STORE_FILE_CSV - Zeilenumbruch

Begonnen von skateman, 07. Juli 2015, 14:51:22

Vorheriges Thema - Nächstes Thema

0 Mitglieder und 2 Gäste betrachten dieses Thema.

skateman

Hallo,

ich habe folgendes Problem mit dem DLOG_STORE_FILE_CSV-Baustein:

Ich verwende den Baustein auf mehreren Beckhoff CX (basic 333 und network 130) - diese habe ich mit dem Baustein ausgiebig getestet, was auch einwandfrei funktioniert hat. Nach der Inbetriebnahme ist mir nun aufgefallen, dass es anfangs funktioniert aber irgendwann ist das LOG-Ergebnis unbrauchbar, weil die Zeilenumbrüche falsch gesetzt werden...

Hier ein Beispiel wie das Log-File dann aussieht:
ZitatDatum/Zeit;Durchfluss.Messwert;DFL_Zaehler.Zaehlerstand_Tag;2015-07-07 08:27:27;6.9;180;2015-07-07 08:27:46;5.2;;2015-07-07 08:29:31;;181;2015-07-07 08:32:22;;182;
Statt den Zeilenumbrüchen wird fälschlicherweise ein Separator gesetzt!


So sollte es eigentlich aussehen:
ZitatDatum/Zeit;Durchfluss.Messwert;DFL_Zaehler.Zaehlerstand_Tag
2015-07-07 08:27:27;6.9;180
2015-07-07 08:27:46;5.2;
2015-07-07 08:29:31;;181
2015-07-07 08:32:22;;182

Beim Testen konnte ich das Fehlverhalteneinfach vorerst noch nicht reproduzieren....

Ich habe den DLOG_REAL FB so abgeändert, dass nur bei einer wirklichen Delta-Änderung ein Wert geschrieben wird - wenn der Trigger von extern kommt, wird '' geschrieben (deshalb auch die Leereinträge in dem Log oben).

Die Ursache für die fälschliche Separator-Setzung habe ich in folgendem CODE-Teil gefunden:

n := n + USINT#1;
IF n = X.ID_MAX THEN (* letztes Element einer Zeile *)
n := USINT#0;
PT.BUFFER[idx] := BYTE#16#0D;
idx := idx + 1;
PT.BUFFER[idx] := BYTE#16#0A;
ELSE
PT.BUFFER[idx] := SEP;
END_IF;


Ich habe gesehen, dass im laufenden Programm die Variable n eine großen Wert hatte > X.ID_MAX. Da die Rücksetzbedingung für n allerdings eine ISTGLEICH-Operation ist, wird der Wert nie mehr zurückgesetzt, wenn er einmal größer als X.ID_MAX  ist.(dieser lag momentan richtigerweise beim Wert 3)

Damit das passieren kann, müsste X.ID_MAX kurzzeitig den Wert 0 oder einen größeren als den tatsächlichen Wert angenommen haben. Nachdem ich n manuell wieder auf 0 gesetzt habe, funktionierte das Logging auch wieder richtig.

Der DLOG_STORE_FILE_CSV-Baustein wird in meinem Programm nur in einem bestimmten Fehlerfall aktiviert (danach wird das ENABLE wieder auf false gesetzt) - ENABLE kann also zwischen true und false wechseln.
X.ID_MAX wird ja in der step_1 CASE-Anweisung wieder auf 0 gesetzt, wenn ENABLE von false auf true wechselt. Der Schreibvorgang unter der step_2-Anweisung könnte da aber noch laufen und dann hätte man den Fall, dass n weiterzählt und beim nächsten ENABLE dann größer als X.ID_MAX ist... Eventuell ist das die Ursache...

Ich habe vorerst mal eine zusätzliche Rücksetzung unter 00 von step_1 hinzugefügt und die ISTGLEICH gegen eine GRÖßerGLEICH-Operation ausgetauscht - ich hoffe dass das die einzige Stelle ist, an dem Variablen nicht richtig zurückgesetzt wurden...





peewit

hallo

es gibt immer einen hauptbaustein der die daten in irgendeiner form sammelt und es gibt sogeannte satellitenbausteine
DLOG_REAL, DLOG_STRING usw.. die die daten liefern

wenn der hauptbaustein erstmalig freigegeben wird, dann wird zuerst ermittelt wieviele satellitenbausteine es gibt (ID_MAX)
und die einzelnen satellitenbausteine erhalten aufgrund ihrer aufrufreihenfolge eine platznummer.

damit dieses system pflegeleicht funktioniert ist es erforderlich das alle bausteine zyklisch und nicht bedingt aufgerufen werden
und sich die anzahl und reihenfolge später auch nicht mehr ändert

wenn bei dir n > id_max ist dann hast du hierbei etwas nicht eingehalten

genaueres kann man nur sagen wenn man deinen code analysiert.



skateman

Hallo,

die Bausteine werden zyklisch aufgerufen und unterliegen auch keiner Bedingung.

So werden die Bausteine in meinem Programm aufgerufen (LOG_Durchluss und LOG_Durchluss_Zaehler_Tag sind DLOG_REAL Bausteine):

DLOG_DT(
FMT:=,
COLUMN:='Datum/Zeit' ,
DELTA:= ,
X:=X );

LOG_Durchfluss(
VALUE:=MAIN.Durchfluss.MW ,
N:= 1, (*Nachkommastellen*)
D:='.' ,
COLUMN:= 'Durchfluss.Messwert',
DELTA:=5 ,
DELTA_ONLY:= TRUE,
MIN_DELAY:= t#10s,
X:= X);

LOG_Durchluss_Zaehler_Tag(
VALUE:=MAIN.DFL_Zaehler.Zaehlerstand_Tag ,
N:= 0, (*Nachkommastellen*)
D:='.' ,
COLUMN:= 'DFL_Zaehler.Zaehlerstand_Tag',
DELTA:=1 ,
DELTA_ONLY:= TRUE,
MIN_DELAY:= t#1m,
X:= X);



DLOG_STORE_FILE_CSV(
ENABLE:=Filename <> '' AND DLOG_FILE_TO_STRING.FILE_COUNT < 100 , (*Es wird nur aufgezeichnet, wenn die Freigabe gesetzt ist und weniger als 100 Files im Buffer vorhanden sind*)
TRIG_M:= TIMER_INIT_SAVE.Q,
TRIG_T:= ,
FILENAME:=Filename ,
DTI:= Uhrzeit_Lokal,
SEP:= 59,   (*ASCII-Zeichen für ;  *)
AUTO_CLOSE:=t#5m, (*in diesem Intervall wird das File zwingend gespeichert*)
RETAIN_DATA:=MAIN.DLOG_RETAIN,
X:= X,
ERROR_C=> ,
ERROR_T=> );


Hier noch der Code vom abgeänderten DLOG_REAL Baustein:
TIMER_delay(IN:=TRUE, PT:= MIN_DELAY); (*Mindestzeit zwischen zwei Wertänderungen*)

CASE x.ADD_COM OF

01: (* ADD INFO *)
X.ID_MAX := X.ID_MAX + USINT#1;
id := WORD#16#0201; (* Quelltype REAL , Zieltype STRING *)
02: (* ADD HEADER *)
X.UCB.D_STRING := COLUMN;
X.UCB.D_HEAD := id;
X.UCB.D_MODE := 1;
UCB(DATA:=X.UCB); (* Daten eintragen *)
value_log:= REAL_TO_STRF(IN:=VALUE,N:=N,D:=D); (*RHC - Wert initialisieren*)
03: (* ADD DATA *)
IF (DELTA_ONLY) THEN
X.UCB.D_STRING := value_log;
value_log:= '';
ELSE
X.UCB.D_STRING := REAL_TO_STRF(IN:=value_last,N:=N,D:=D);
delta_last := value_last;
END_IF
X.UCB.D_HEAD := id;
X.UCB.D_MODE := 1;
UCB(DATA:=X.UCB); (* Daten eintragen *)
04: (* ADD DATA REQ *)
IF DELTA <> 0.0 THEN
IF (VALUE <= (delta_last - DELTA) OR VALUE >= (delta_last + DELTA)) AND TIMER_delay.Q THEN
value_log:= REAL_TO_STRF(IN:=VALUE,N:=N,D:=D);
X.ADD_DATA_REQ := TRUE;
delta_last := VALUE;
TIMER_delay(IN:= FALSE);
END_IF;
END_IF;
END_CASE;
value_last := VALUE;


Ich sehe leider keinen Fehler...

peewit

#3
hi

hab mir das mal theoretisch ohne sps tests durchgesehen

ich habe beim logger baustein zwei hauptteile

eine kümmert sich um die datengenerierung
und der andere um die datenspeicherung

um das ganze flexibel zu machen gibt es einen grossen buffer wo die daten und befehle seriell verarbeitet werden.

wird der logger gesperrt so wird im zweiten teil das datenspeichern weiter durchgeführt.

wenn nun bevor alle daten gespeichert wurden der logger wieder enabled wird dann wird wieder der scan nach satellitenbausteine durchgeführt und x.id_max wird auf 0 gesetzt, bzw. erhöht sich dann wieder.

wenn in dieser phase noch daten geschrieben werden dann kann es passieren das n > id_max ist
und die sache bleibt hängen

einfache idee ist nun den satelliten scan einfach nur einmal zu machen

korrigierte version
00: IF ENABLE THEN
aw_enable := AUTO_CLOSE >= T#5s;
wd_time := SEL(X.LOAD_TIME_MAX > T#0s, T#5ms,X.LOAD_TIME_MAX);
fn_last := fn; (* aktuellen Dateinnamen speichern *)

                (* nur einmal nach satellitenbausteinen scannen *)
IF X.ID_MAX = USINT#00 THEN
X.ADD_COM := 01; (* ADD INFO *)
X.STORE_TYPE := BYTE#2; (* CSV-Modus *)
END_IF;
step_1 := 10;

   

das ist aber nicht getestet und theoretisch .....
habe leider nicht mehr zeit...

skateman

Hi,
das wäre eine Möglichkeit...
In dem Code ist allerdings ein ":" nach X.ID_MAX zu viel.  ;)

peewit

ja richtig... wie gesagt ungetestet ... schnelle idee
-> im kopf funktionierte die änderung :-)

skateman

Hallo peewit,

ich habe mir grade überlegt, ob das nicht die sauberere Lösung wäre:


CASE step_1 OF

00: IF ENABLE AND step_2 = 0 THEN
n:=0;
aw_enable := AUTO_CLOSE >= T#5s;
wd_time := SEL(X.LOAD_TIME_MAX > T#0s, T#5ms,X.LOAD_TIME_MAX);
fn_last := fn; (* aktuellen Dateinnamen speichern *)

X.ID_MAX := USINT#00;
X.ADD_COM := 01; (* ADD INFO *)
X.STORE_TYPE := BYTE#2; (* CSV-Modus *)
step_1 := 10;



Nur wenn step_2 gleich 0 ist (Schreibvorgang beendet), wird der erste Schritt von step_1 ausgeführt und somit mit neuem Logging bzw. Initialisierung (inkl. Rücksetzung von X.ID_MAX) begonnen. Das hätte auch den Vorteil, dass man die Anzahl der Sattelitenbausteine/DLOG-Bausteine per Online-Change ändern könnte, wenn ENABLE auf FALSE gesetzt ist. Habe ich noch irgendeine Falltür übersehen?
Bei meinen Tests hats zumindest funktioniert...

peewit

das wird zwar prinzipiell gehen, es macht aber eine geiwsse zeit blind
da erst wenn alles geschrieben wurde kann die aufzeichnung wieder aktiviert werden.


skateman

Dass das Logging dann erst mit einem kurzen Delay nach Sperre/Freigabe de DLOG_STORE_CSV-Bausteins beginnt, wäre bei meiner Applikation kein Problem.

Ich habe aber grade gesehen, dass die Änderung die Wirkung verfehlt, da der Vergleich von n mit X.ID_MAX ja dann erfolgt, wenn step_2 = 0 ist.

Wenn ich das richtig interpretiere, wird X.UCB.D_HEAD auf 0xEE00 gesetzt, wenn ENABLE false wird. In der CASE-Anweisung step_2 gibt es in der WHILE-Schleife eine Abbruchbedingung: wenn X.UCB.D_HEAD=0xEE00 ist, wird step_2=10. Demnach dürfte das Problem mit dem Überlauf von n gar nicht passieren. Aber D_HEAD wird doch vom zuvor aufgerufenen UCB-Baustein überschrieben, wenn DATA.BUF_COUNT > 0 ist, oder? Dann würde das File auch erst geschrieben werden, wenn das zeitgesteuerte Schreiben aktiv wird. Kann das sein?



peewit

da die beiden hauptroutinen ziemlich autarkt sind, kann es eben sein das der eine teil daten schreibt und eine korrekte id_max noch benötigt, während die andere routine einen neuen aufnahmezyklus beginnt und id_max zerstört und neu ermittelt.

ich bin noch immer der meinung das mein erster änderungsvorschlag der optimalste ist.


Andy_Scheck

@ Skateman: Hast du das Logging auf einer Cx mit TwinCAT 3 am laufen? Würde selbst gerne mal auf einem Cx9020 mit TwinCAT 3 das ganze testen...