Empfehlung für Messwerterfassungssystem mit >100 analogen Eingängen

Zuviel Werbung?
-> Hier kostenlos registrieren
Ok:/
kurz nochmal eine Verständnisfrage.
Wenn ich die Daten in einem Puffer zwischenspeichere und die CPU grade dann einen anderen Task priorisiert. Habe ich dann nicht genau den selben Zeitversatz, nur halt im Puffer, sodass ich aufs selbe hinaus komme? Oder hab ich da einen Denkfehler?
Dann wäre der einzige Vorteil, dass die Datei weniger geöffnet und geschlossen wird. Dem Flash-Speicher ist es doch egal, ob ich nun 100 Datensätze auf einmal oder nacheinander schreibe. Schreiben bleibt Schreiben, oder?

EDIT:
Das war ja auch mein Ansatz aus #14, aber ...
alle 100 ms 1 Datensatz schreiben mit Datum+Uhrzeit und Messwert1 .. MesswertX und Druckwert0(Uhrzeit+0ms), Druckwert1(Uhrzeit+10ms) .. Druckwert9(Uhrzeit+90ms).

Kann es sein, dass Deine Zeitsprünge darauf zurückgehen, dass der Zeitwert "weit hergeholt" ist (aus Windows statt aus der SPS)?


Ich hatte es vorher mit der SPS-Zeit programmiert. Da war es ganz genau so :/
 
Zuletzt bearbeitet:
Ich denke, ich weiß jetzt wo der Fehler liegt.. Ihr werdet mich für bekloppt halten.

Das Schreiben wird in einer Case-Funktion nach Beckhoff-Vorlage ausgeführt. 0 - 10 Steps + Fehlerstep/Wartestep 100. Je nach dem, ob er einen Fehler hat oder die Datei nicht schnell genug geschlossen wird, dreht er ne extra Runde. Da kommen die zusätzlichen Millisekunden her. Pro Step 1 ms, bei einer Zykluszeit von 1 ms. Hmmm
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Du änderst Deinen Beitrag schneller, als ich die Änderungen aufschnappen kann.

Vorhin stand da noch "Ich hatte es vorher mit der SPS-Zeit programmiert. Da war es ganz genau so, nur um 2:XX h verschoben, da die Zeit dort absolut nicht passt."

Du hast vermutlich mit Ortszeit und Sommerzeit zu kämpfen, aber doch "nur" mit einer konstanten Abweichung zwischen Windows und SPS?
Wenn Du in Deiner 10-ms-Task eine eigene Zeitzählung machst, hinkt die dann umso mehr hinterher, je länger Du beide vergleichst?
Andererseits dürfte es höchst unwahrscheinlich sein, dass sich die 10-ms-Task selbst in die Hacken tritt, wenn Deine "eigentliche" Zykluszeit bei 1 ms liegt.

Ich würde mich lieber auf die SPS-Zeit stützen und unterstellen, dass die 10-ms-Task auch tatsächlich alle 10 ms durchlaufen wird.

Zu Deiner Verständnisfrage:
Wird die Datei tatsächlich vor dem Schreiben eines jeden Satzes geöffnet und danach wieder geschlossen? Dann würde ich hier EinsparPotenzial für das Auslasten der CPU wittern.
Der LebensDauer des FlashSpeicher dürften die (zu) häufigen SchreibZugriffe nicht egal sein.
Da Du mit jedem Datensatz den jeweiligen Zeitstempel mit abspeicherst bzw. bis zum Schreiben pufferst, dürfte das Endergebnis sich nicht unterscheiden.
Als Zeitstempel nimmst Du doch die Zeit der MesswertErfassung und nicht die Zeit des Wegschreibens?!
 
Dieser Editor versucht schon wieder, mich reinzulegen. ;)
Erst zitiert der Beitrag sich selbst und dann legt er für eine Änderung einen weiteren Beitrag an. :confused:

PS:
Ich würde beim Sammeln der Daten und beim Wegschreiben abwechselnd zwei Puffer belegen:
Daten in Puffer1 aufbereiten, während das BetriebsSystem damit beschäftigt ist, Puffer2 in die CSV-Datei zu schreiben und
Daten in Puffer2 aufbereiten, während das BetriebsSystem damit beschäftigt ist, Puffer1 in die CSV-Datei zu schreiben u.s.w..
 
Zuletzt bearbeitet:
Sorry für die Änderungen. Manchmal schreibe ich schneller als ich denken kann und dann fällt mir auf, dass da Blödsinn steht, der mit der Thematik nichts zu tun hat.

Der Zeitstempel bezieht sich auf den Zeitpunkt des Erfassens der Messwerte.
Zur Zeit öffne und schließe ich noch jedes Mal die Datei. Ist natürlich nicht sonderlich schön. Bin grade schon dabei das zu ändern.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die Windows-Zeit kannst du vergessen. Es wird nie Synkron mit der SPS-Zeit.

Dann wäre der einzige Vorteil, dass die Datei weniger geöffnet und geschlossen wird.
Das macht definitiv die Schreibzeiten kürzer.

Dem Flash-Speicher ist es doch egal, ob ich nun 100 Datensätze auf einmal oder nacheinander schreibe. Schreiben bleibt Schreiben, oder?
Nicht egal. Das Flashspeicher hat ein begrenzten Anzahl schreibzyklen. 'industrielle' Flashspeicher sind besser als Konsum, aber lebt trotzdem nicht unendlich. 100-mal pro sekunde = 360000 pro Stunde = 8.6 M pro Tag, 260 M pro Monat = 3.1 G pro Jahr. Ich habe es erlebt das ein Siemens Panel das Flashspeicher durch zu viele Schreibzyklen defekt ging. Und das war leider von versehen das interne Speicher --> Panel = Schrott.
 
Ich sags mal so - Als Speicher dient eine externe SSD oder USB-Stick. Die Messdatenerfassung läuft, wenn es hoch kommt 2 Stunden am Tag. Da sehe ich eher kein Problem.
Was ich aber grade merke ist, dass wenn ich den Ablauf leicht ändere, ich andere Zeitintervalle bekomme. Teilweise 9 ms, Teilweise 11 ms. Je nach Programmcode. Heißt für mich - ich muss den Programmcode so "anpassen", dass ich genau auf 10 ms komme. Irgendwie fühlt sich das nicht richtig an. Es muss doch eine Möglichkeit geben exakte Zeiten vorzugeben :confused:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was ich aber grade merke ist, dass wenn ich den Ablauf leicht ändere, ich andere Zeitintervalle bekomme. Teilweise 9 ms, Teilweise 11 ms. Je nach Programmcode. Heißt für mich - ich muss den Programmcode so "anpassen", dass ich genau auf 10 ms komme. Irgendwie fühlt sich das nicht richtig an. Es muss doch eine Möglichkeit geben exakte Zeiten vorzugeben :confused:
Dann würde ich doch empfehlen,
1. Deine eigene Zeitzählung im 10-ms-Task zu basteln und
2. diese eigene Zeit mit der "realen" zu vergleichen, ob sich hier etwas eklatantes entwickelt, das auf einen HandlungsBedarf deutet.
3. die "realen" ms auf das nächst liegende Vielfache von 10 zu runden.
 
Verwendest du das ?
Habe grade mal geschaut und ich finde nicht mal die Einstellung dazu. Eine neue Baustelle für morgen..

Es ist auf jeden Fall im Moment so, dass ich paar Schreibvorgänge im Fehler-Step lande und das kann ja nicht Sinn der Sache sein. Schreiben tut er, aber halt nur mit Fehlern zwischendurch.
Schauen wa mal..
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Habe grade mal geschaut und ich finde nicht mal die Einstellung dazu. Eine neue Baustelle für morgen..
Schau Dir mal das EtherCAT Handbuch an, da ist Distributed Clock beschrieben.
Es ist auf jeden Fall im Moment so, dass ich paar Schreibvorgänge im Fehler-Step lande und das kann ja nicht Sinn der Sache sein. Schreiben tut er, aber halt nur mit Fehlern zwischendurch.
Schauen wa mal..
Dann stell doch bitte Deinen Code über den Code Button hier rein, das sollte eigentlich nicht passieren.
 
Es ist auf jeden Fall im Moment so, dass ich paar Schreibvorgänge im Fehler-Step lande und das kann ja nicht Sinn der Sache sein. Schreiben tut er, aber halt nur mit Fehlern zwischendurch.
Welcher Art sind die auftretenden Fehler?
Was heisst mit Fehlern zwischendurch? Fehler nur dann, wenn das Schreiben gerade mal nicht gefordert ist, sondern nur beim Öffnen bzw. Schliessen der Datei?
Oder ist erkennbar, dass nicht alle Sätze geschrieben werden, also ab und zu ein Satz in der Reihenfolge fehlt?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Guten Morgen liebe Leute,
ich habe es eben geschafft, die Fehler auszumerzen!Ich habe nun eine Bombenfeste Schreibzeit von 4 ms, wobei die Datei nur einmal geöffnet wird. Yeah! :)
Jetzt setze ich die Zykluszeit auf 2 ms und bastel mir noch 1/2 Wartesteps dazu, dass die 10 ms passen.
Vielen Dank für eure Anregungen. Ich wäre sonst nicht drauf gekommen, dass der Fehler evtl. ganz wo anders liegt.
Viele Grüße und einen schönen und sonnigen Tag
 
Moin Leute,

ich muss mich nochmal an euch wenden. Und zwar geht es um den FB_CSVMemBufferWriter. Dieser besitzt einen Puffer zum Speichern von Strings mit einer Größe von 255 (-1). Dieser Puffer läuft bei mir allerdings mit einer größer werdenden Anzahl an Messwerten über, sodass dieser einen Fehler auswirft.

Nach kurzer Rücksprache mit Beckhoff kann ich mir selbst einen Puffer mit fast unbegrenzter Größe erstellen. Ich weiß allerdings nicht wie ich diesen deklarieren soll, noch wie der Aufruf des FBs auszusehen hat. Die Info-Seite ist dies bezüglich leider etwas spärlich. Vielleicht hat jemand von euch schonmal damit zu tun gehabt und kann mir weiterhelfen. Alle meine bisherigen Versuche einen Puffer zu erzeugen und einzusetzen sind bis jetzt fehlgeschlagen.

Hier nochmal die Info-Seite des FBs:
https://infosys.beckhoff.com/index....PlcLibUtilities_FB_CSVMemBufferWriter.htm&id=

Vielen Dank schon mal im Voraus!! :)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Soo, ich hab hier mal einen Teil des Programmcodes gepostet. Der Rest ist für die Thematik irrelevant hoffe ich.
Ich habe jetzt ein Byte Array erstellt und es versucht mit in den aufruf des FB_CSVMemBuffer_Writers (fbWriter) zu integrieren. Den FB_FilePuts habe ich durch den FB_FileWrite ersetzt, da der FilePuts glaube ich nur Strings verarbeiten kann. Ich bekomme dennoch immer noch Fehler in meiner FOR-Schleife, bei der Abfrage des fbWriter.b0k. Der FB-Aufruf wird wahrscheinlich einfach nicht passen. Habe aber schon diverses ausprobiert, aber nichts scheint so wirklich richtig zu sein.. :/

Code:
PROGRAM Daten_schreiben(* 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
    // Rising edge starts program execution
    dateiname        : STRING      := 'DefaultDateiname';
    bWrite             : BOOL        := FALSE;
    sNetId            : T_AmsNetId  := '';    (* TwinCAT Netzwerkadresse *)
    sFileName        : T_MaxString := CONCAT(CONCAT('C:\Users\Administrator\Desktop\Messdatenerfassung_Ver0.7\',dateiname),'.csv');(* CSV destination file path and name *)
    sCSVLine        : T_MAXSTRING := '';(* Einzelne CSV Line (Zeile) *)
    sCSVField        : T_MAXSTRING := '';(* Einzelnes CSV-Feld (Spalte)*)
    bBusy            : BOOL;
    bError            : BOOL;
    nErrId            : UDINT;
    nZeile             : UDINT     := 1;(* Nummer der Zeile *)
    nSpalte            : UDINT     := 1;(* Nummer der Spalte *)
    hFile            : UINT        := 0;(* "File handle" der Zieldatei *)
    step            : DWORD     := 0;
    fbFileOpen        : FB_FileOpen;(* Öffnet die Datei *)
    fbFileClose        : FB_FileClose;(* Schließt die Datei *)
    fbFilePuts        : FB_FileWrite;(* Schreibe eine Zeile *)
    fbWriter        : FB_CSVMemBufferWriter;(* Helfer-Funktion zur Erzeugung von CSV-Daten-Bytes (einzelne Zeile)*)
    sCSVField_array : ARRAY [0..999] of BYTE;
    
    
    
        
    getTime            : NT_GetTime; (* Funktion zurm Holen der Windows-Systemzeit *)
    systemTime        : TIMESTRUCT; (* Variable zur Zwischenspeicherung der Systemzeit *)
    bGetTimeStart    : BOOL:=FALSE;
    bGetTimeBusy    : BOOL;
    bGetTimeERR        : BOOL;
    bGetTimeERRID    : UDINT;    
    
    .
    .
    .


        
    database        : ARRAY[1..MAX_CSV_SPALTEN, 1..MAX_CSV_ZEILEN ] OF STRING (MAX_CSV_FELD_LAENGE); (* Zwischespeicher für eine gesamte Messdatenerfassung zu einem einzelnen Zeitpunkt *)
                                    
    
END_VAR


_____________PROGRAMM________________________________________________________________________________________


.
.
.


            sCSVField_array := null; (* sCSVField_array "Löschen"*)
            fbWriter.eCmd := eEnumCmd_First;(* Schreibe ersten Feldwert *)


            IF nZeile <= MAX_CSV_ZEILEN THEN
            
                    FOR nSpalte := 1 TO ANZAHL_SPALTEN BY 1 DO
                    
                                        
                    sCSVField := STRING_TO_CSVFIELD(database[ nSpalte, nZeile ], TRUE ); //-------------
                    
                    (* Add new field to the record buffer *)
                                
                    
                    fbWriter(pBuffer := ADR(sCSVField_Array), cbBuffer := SIZEOF(sCSVField_Array), putValue := '', pValue := ADR(sCSVField), cbValue := SIZEOF(sCSVField),
                            bCRLF := ( nSpalte = ANZAHL_SPALTEN ) );(* bCRLF == TRUE => Neuer Datensatz (Zeile)*)
                    IF fbWriter.bOk THEN
                        fbWriter.eCmd := eEnumCmd_Next;(* Schreibe den nächsten Feldwert *)
                    ELSE(* Error *)
                        step := 100;
                        RETURN;
                    END_IF
                    
                    END_FOR(* FOR Spalte 0... Anzahl an Spalten *)
                    
                (* FB_FilePuts fügt eine s. g. carriage Return der geschriebenen Zeile zum Abschluss der Zeile hinzu.
                Dazu muss abgefragt werden, ob die letzten beiden Zeichen R und L sind und je nach dem nur durch ein L ersetzt werden,
                um einen doppelten Zeilenumbruch zu vermeiden. *)
            
                //IF RIGHT( sCSVField_array, 2 ) = '$R$L' THEN
                //sCSVLine := REPLACE( sCSVLine, '$L', 2, LEN( sCSVLine ) - 1 );
                //END_IF


            nZeile := nZeile + 1;(* Increment-Nummer der erzeugten Zeilen. Hier wird nur Zeilenweise geschrieben *)
            step := 4;
            ELSE 
            nZeile := 1;
            END_IF
            
        ELSE    (* Daten Schreiben stoppen *)
            step := 10; 
        END_IF
        
    4:    (* Schreibe eine einzelne Textzeile *)
    
        IF RUN AND bWrite  THEN
        fbFilePuts( bExecute := FALSE );
        fbFilePuts( sNetId := sNetId, hFile := hFile, pWriteBuff := ADR(sCSVField_array),cbWriteLen :=SIZEOF(sCSVField_array), bExecute := TRUE );
        step := 5;
        ELSE 
                step := 10;
        END_IF


    .
    .
    .



EDIT:

Habe jetzt die Eingänge des CSVMemBufferWriters für einen externen Puffer wieder auf 0 gesetzt und einfach für den Standardpuffer den BytePuffer angegeben. Als Input habe ich wieder den einzelnen Wert aus meiner Database (sCSVFeld) genommen:

Code:
.
.
.

 fbWriter(pBuffer := ADR(sCSVField_Array), cbBuffer := SIZEOF(sCSVField_Array), putValue := sCSVField '', pValue := 0, cbValue := 0 ,
                            bCRLF := ( nSpalte = ANZAHL_SPALTEN ) );(* bCRLF == TRUE => Neuer Datensatz (Zeile)*)
                    IF fbWriter.bOk THEN
                        fbWriter.eCmd := eEnumCmd_Next;(* Schreibe den nächsten Feldwert *)
                    ELSE(* Error *)
                        step := 100;
                        RETURN;
                    END_IF
.
.
.

Das läuft soweit! Nur schreibt er die erste Zeile linksbündig, manchmal die zweite Zeile nach rechts versetzt "in die Mitte" und die darauffolgenden Zeilen schön untereinander, rechtsbündig und abgeschnitten in die CSV-Datei. Da suche ich grade noch den Fehler bzw. die Stelle, wo das passiert.

EDIT2: Ich glaube er schreibt die Leerzeichen bis zu den Werten der 2. und darauffolgenden Zeilen mit. Diese Leerzeichen belegen komischerweise auch Speicherplatz. Im ByteArray fangen die Messdaten immer im 1. Byte an. Ich kanns mir grade noch nicht erklären, wo die Leerzeichen herkommen:confused:
 
Zuletzt bearbeitet:
Also :D
Wenn ich den Puffer mit 1000 Werten deklariere, dann schreibt er mir alle 1000 Bytes in die CSV. Jedes Byte, welches den Wert 0 hat, erzeugt ein Leerzeichen :(
Hat jemand eine Idee, wie ich das löse? Dynamisches Array, oder Abfrage auf den Wert 0, aber wo?
 
Ich glaube er schreibt die Leerzeichen bis zu den Werten der 2. und darauffolgenden Zeilen mit. Diese Leerzeichen belegen komischerweise auch Speicherplatz. Im ByteArray fangen die Messdaten immer im 1. Byte an. Ich kanns mir grade noch nicht erklären, wo die Leerzeichen herkommen:confused:
Die Leerzeichen sind nicht so leer, wie sie heissen. ;) Wenn Du sie nicht haben willst, musst Du sie vor dem Schreiben (oder nach dem Lesen) ersetzen durch "nichts", also einen Leerstring.
Wo sie herkommen? Formatierte Ausgaben, bei denen die VorlaufNullenUnterdrückung die unterdrückten Nullen durch Leerzeichen ersetzt, vermute ich mal.
 
Zurück
Oben