TIA VB CSV. Datei auf Storage Card anlegen (Datenlogger)

Zuviel Werbung?
-> Hier kostenlos registrieren
Das habe ich leider immer noch nicht verstanden.
Nach jeder Runde heisst nach jedem Satz oder nach jedem Block von 1000 Sätzen?
Wo hängst Du die aktuelle Zeit eine Zeile nach dem letzten Eintrag an? In der Tabelle in der SPS oder in der csv-Datei?
Deinen Satz verstehe ich jetzt so, dass Du nach jedem "Tabellen"-Satz, den Du in die csv schreibst einen weiteren Satz schreibst, der nur die aktuelle Zeit enthält.

Ich hoffe, mein Verständnis ist richtig, dass Du die csv-Datei mit der SPS/HMI erzeugst und in Excel einlesen willst?
Neee ich glaube wir reden aneinander vorbei.

Nach jedem fertigen Teilesatz wird die Taktzeit gemessen und in eine csv. Datei geschrieben (zusammen mit dem Datum, der Uhrzeit, dem Artikelnamen, usw.).

Und das beginnend mit einer neues csv. Datei
Zeile1: = Taktzeit 1 Teilesatz
Zeile2: = Taktzeit nächster Teilesatz
Zeile3: = Taktzeit nächster Teilesatz
Zeile4 ...... Zeile X
usw.

Wenn ich jetzt den 1000 Teilesatz gefertigt habe, müsste ich in Zeile 1000 speichern. Diese Zeilennummer "1000" würde ich gerne auslesen aus der csv. Datei. Nur die Zahl. Damit ich weiß, das jetzt meine gewünschte Menge an Taktzeiten im Archiv sind und ich es dann abspeichern kann (ohne das ich das abspeichern jedesmal zählen muss).

Hoffe, ich konnte es etwas deutlicher machen :)

Gruß Willi
 
Also, ich hänge ja nach jeder Runde die aktuelle Zeit eine Zeile unter dem letzten Eintrag an.
(...)
Ich dachte, wenn ich jetzt bei jedem Durchlauf des Skriptes die Nummer der Zeilen (Die Zeilennummer aus Excel) auslesen könnte
Die Anzahl vorhandener Zeilen kann man bei einer csv-Datei nicht auslesen, aber abzählen. Einfach die Datei im Lesemodus (1) öffnen und bis zum Dateiende zeilenweise einlesen und mitzählen. Etwa so:
Code:
...
f.Open myfile, 1 'ForReading
zeilenanzahl = 0
Do While f.EOF = False
    linebuffer = f.LineInputString
    zeilenanzahl = zeilenanzahl + 1
Loop
f.Close

If zeilenanzahl > 999 Then
   'Datei verschieben
   ...
Else
   'Datenzeile anhängen
   ...
End If

Alternativ (wenn Du z.B. noch Kopfzeilen in der csv-Datei hast) kannst Du auch die Zeilennummer in jede Zeile schreiben (wie Heinileini schon in #18 vorgeschlagen hat) und generell vor dem Datenzeile schreiben die vorhandene Datei bis zur letzten Datenzeile einlesen - dann steht in der zuletzt gelesenen Zeile die letzte Zeilennummer.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn ich jetzt den 1000 Teilesatz gefertigt habe, müsste ich in Zeile 1000 speichern. Diese Zeilennummer "1000" würde ich gerne auslesen aus der csv. Datei. Nur die Zahl. Damit ich weiß, das jetzt meine gewünschte Menge an Taktzeiten im Archiv sind und ich es dann abspeichern kann (ohne das ich das abspeichern jedesmal zählen muss).
D.h. also die ArchivierMimik zählt mit und Du kannst auf diesen Zähler nicht unmittelbar zugreifen?
Und, um an diese Information zu kommen, müsstest Du die Datei von Anfang an einlesen bis zum letzten Satz, um den Zählerstand aus dem letzten Satz zu entnehmen? Bzw. Du müsstest selbst die Zahl der Sätze mitzählen?
Hmmm.
Was hindert Dich, die Anzahl der geschriebenen Sätze selbst mitzuzählen. Das dürfte doch wesentlich weniger Aufwand bedeuten, als die soeben geschriebene Datei komplett wieder einzulesen?
 
Alternativ (wenn Du z.B. noch Kopfzeilen in der csv-Datei hast) kannst Du auch die Zeilennummer in jede Zeile schreiben (wie Heinileini schon in #18 vorgeschlagen hat) und generell vor dem Datenzeile schreiben die vorhandene Datei bis zur letzten Datenzeile einlesen - dann steht in der zuletzt gelesenen Zeile die letzte Zeilennummer.
Hallo Harald,

Das habe ich jetzt so umsetzen können und funktioniert auch soweit.
Ich bekomme nur (nicht immer) die Fehlermeldung Überlast Skript.

Ich setze mir jetzt in dem Skript, wo ich die Taktzeiten abspeichere nach erreichen der Zeilenvorgabe ein Bit.
Dieses Bit startet ein weiteres Skript. Und ich denke mal genau hier liegt das Problem.
Dadurch, das das eine Skript quasi noch am laufen ist, wird ein weiteres angestoßen.

Dies könnte der Überlast sein oder?
Wir würdet Ihr das umsetzen?
Ich habe jetzt ein Skript, das die Taktzeiten abspeichert und ein zweites was das sichern der csv. mit datum/uhrzeit etc. übernimmt und die "Alte" Datei löscht.

Evtl. die Funktionen in ein Skript bringen ?

Danke Euch

Gruß Willi
 
Dieses Bit startet ein weiteres Skript. Und ich denke mal genau hier liegt das Problem.
Dadurch, das das eine Skript quasi noch am laufen ist, wird ein weiteres angestoßen.

Dies könnte der Überlast sein oder?
Genau, maximal 1 aktives Skript.
Wodurch und wann wird denn dieses Skript gestartet (das das zweite Skript zu starten versucht)?
Wodurch und wann wird es bisher "fertig"?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
grundsätzlich ist es so, wie schon oben erwähnt, dass nur 1 scriptausgeführt wird. alle weiteren scripts werden gestapelt und nacheinander abgearbeitet. je nach dem wie du die scripte aufrufst kann das durchaus zu überlauf führen.
 
Guten Morgen zusammen,

Ich kann den Code nachher hochladen. Bin momentan nicht am Rechner. Grundsätzlich ist es so. Skript 1 wird von der sps angestoßen (bei Wertänderung). Dieses Skript öffnet eine Csv Datei und schreibt die Taktzeit in die immer letzte Zeile. Danach zählt es die gefüllten Zeilen. Hat das Skript 10000 Zeilen gefüllt, setze ich in dem Skript eine sps varaiable in einem DB.

Dieses Bit reagiert auch wieder auf wertänderung und startet das Skript 2 welches die Datei mit den 10000 werten unter dem aktuellen Tagesstempel abspeichern und die die alte csv. Löscht. Diese wird dann wieder neu angelegt und der Prozess beginnt von neuem.

@harald: fehlernummer muss ich im meldearchiv schauen. Meine die Endnummer wäre 015. davor bin ich mir nicht ganz sicher.

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Aus deiner Beschreibung schliesse ich, dass dein Script_2 länger arbeitet und die Ausführung des Script_1 somit verhindert.
Du kannst mit den Scripten generell nur synchron arbeiten - eine parallele Abarbeitung ist nicht möglich und an der Stelle, so vermute ich ohne deinen Code zu kennen, entsteht der Fehler. Ich bin mal auf das Konstrukt gespannt ...
 
Skript 1 wird von der sps angestoßen (bei Wertänderung). Dieses Skript öffnet eine Csv Datei und schreibt die Taktzeit in die immer letzte Zeile. Danach zählt es die gefüllten Zeilen.
Vermutlich ist der PC noch beschäftigt mit dem Schreiben der csv-Datei, wenn die Datei erneut zum Lesen geöffnet wird? Wie ich in Beitrag #22 schon schrieb, würde ich zuerst die Zeilen zählen und danach die neue Datenzeile schreiben, entweder an die vorhandene Datei anhängen oder vorher die vorhandene Datei verschieben. Sehr wahrscheinlich dauert es eine gewisse Weile, wenn man in eine Datei geschrieben hat, bevor man diese Datei wieder öffnen kann.

Hat das Skript 10000 Zeilen gefüllt, setze ich in dem Skript eine sps varaiable in einem DB.

Dieses Bit reagiert auch wieder auf wertänderung und startet das Skript 2 welches die Datei mit den 10000 werten unter dem aktuellen Tagesstempel abspeichern und die die alte csv. Löscht. Diese wird dann wieder neu angelegt und der Prozess beginnt von neuem.
Warum kann das erste Skript nicht direkt das zweite Skript aufrufen? Warum der zusätzliche Aufwand mit dem Bit in der SPS?

fehlernummer muss ich im meldearchiv schauen. Meine die Endnummer wäre 015. davor bin ich mir nicht ganz sicher.
Es wäre schon interessant, was genau de Fehlermeldung sagt. (Hoffentlich ist die aussagekräftig).

Harald
 
Hallo zusammen,

Das wäre das Skript zum Archiv beschreiben:

Code:
Sub Archiv()
'Skript dient dazu, Taktzeiten in eine .CSV Datei zu speichern


'Variablen definieren
Dim f, fs, fso, Datei, Pfad, Artikel, Solltaktzeit, Isttaktzeit
Dim DD, MM, JJJJ, HH, Min, Sek               
Dim DD_HV, MM_HV, JJJJ_HV, HH_HV, Min_HV, Sek_HV    'Hilfsvariablen für Datum / Uhrzeit
Dim strDatum, strUhrzeit                            'Hilfsvariablen für Stringzusammensetzung Datum / Uhrzeit
Dim strName                                            'Hilfsvariable für Dateiname
Dim Zeilenanzahl, Zeilenpuffer

'Variablen vorbelegen
Artikel = SmartTags("DB_Akt_Daten_Daten_Wkz-Name")
DD = Day(Now)
MM = Month(Now)
JJJJ = Year(Now)
HH = Hour(Now)
Min = Minute(Now)
Sek = Second(Now)
Solltaktzeit = SmartTags("Taktzeiten_Taktzeit_Soll")
Isttaktzeit = SmartTags("Taktzeiten_Taktzeit_1_Real")
SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = False
strName = DD & "." & MM & "." & JJJJ & "/" & HH & ":" & Min & ":" & Sek        'Datum und Uhrzeit als Text in Dateinamen anfügen


'Pfad und Datei festlegen
Pfad = "Storage Card SD\Taktzeitarchiv\"
Datei = "Archiv.csv"


'Objekt für das Filehandling erstellen
Set fs = CreateObject("filectl.filesystem")
Set f= CreateObject("filectl.file")
Set fso = CreateObject("filectl.FileSystem")


'Kontrolle ob MMC Karte gesteckt ist
If fso.Dir("\Storage Card SD\") = "" Then     
    ShowSystemAlarm "SD Karte ist nicht gesteckt, daher Abbruch der Datenübertragung"
Else     
    
    
'Kontrolle ob Verzeichnis vorhanden. Wenn nicht, dann Verzeichnis erstellen
If fs.dir(Pfad) = "" Then
    fs.mkdir(Pfad)
    f.close
End If


'Kontrolle ob CSV.Datei vorhanden. Wenn nicht, dann CSV.Datei erstellen
If fs.dir(Pfad + Datei) = "" Then
    f.open Pfad + Datei, 8
    f.LinePrint "Datum" & ";" & "Uhrzeit" & ";" & "Artikel" & ";" & "Solltaktzeit" & ";" & "Isttaktzeit" & ";"
    f.close
End If


'Kontrolle ob Tag < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If DD < 10 Then
    DD_HV = CStr("0" & DD)
Else
    DD_HV = CStr(DD)
End If

'Kontrolle ob Monat < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If MM < 10 Then
    MM_HV = CStr("0" & MM)
Else
    MM_HV = CStr(MM)
End If

'Kontrolle ob Stunde < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If HH < 10 Then
    HH_HV = CStr("0" & HH)
Else
    HH_HV = CStr(HH)
End If

'Kontrolle ob Minute < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If Min < 10 Then
    Min_HV = CStr("0" & Min)
Else
    Min_HV = CStr(Min)
End If

'Kontrolle ob Sekunde < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If Sek < 10 Then
    Sek_HV = CStr("0" & Sek)
Else
    Sek_HV = CStr(Sek)
End If

'String für Datum zusammensetzen
strDatum = DD_HV & "." & MM_HV & "." & JJJJ
strUhrzeit = HH_HV & ":" & Min_HV & ":" & Sek_HV


'Zeile nur beschreiben, wenn Skriptbit auf True ist
If SmartTags("DB_Skript_Skript_Archiv") = True Then

    
'Pfad und Datei öffnen und beschreiben, danach wieder schließen   
f.open Pfad + Datei, 8        '8 = Daten anhängen
f.LinePrint strDatum & ";" & strUhrzeit & ";" & Artikel & ";" & Solltaktzeit & ";" & Isttaktzeit & ";"
f.close

'Datei öffnen und beschriebene Zeilen zählen
f.open Pfad + Datei, 1 'Lesen
Zeilenanzahl = 0
Do While f.EOF = False
    Zeilenpuffer = f.LineInputString
    Zeilenanzahl = Zeilenanzahl + 1
Loop
f.Close

'Kontrolle ob beschriebene Zeilenzahl größer 9999 ist. Wenn ja, dann umkopieren
If Zeilenanzahl > 9 Then
'Taktzeitarchiv umkopieren
    SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = True
End If   
Else
    SmartTags("DB_Skript_Skript_Archiv") = False
End If
End If

 
'Filehandling zurücksetzen
Set fs = Nothing
Set f = Nothing
Set fso = Nothing

'Skriptvariable zurücksetzen
SmartTags("DB_Skript_Skript_Archiv") = False
End Sub

und das um es weg zu kopieren und das alte zu löschen:

Code:
Sub Archiv_Kopieren_Loeschen()
'Skript dient dazu eine .CSV Datei in einen anderen Ordner zu kopieren und die aktuelle Datei zu löschen


'Variablen definieren
Dim fs, fso, Quelle, Ziel
Dim DD, MM, JJJJ, HH, Min, Sek   

'Quell- und Zielpfad festlegen
'Pfad und Datei festlegen
Quelle = "\Storage Card SD\Taktzeitarchiv\Archiv.csv"
Ziel = "\Storage Card SD\Sicherung_Taktzeitarchiv\"
Set fs = CreateObject("filectl.filesystem")
Set fso = CreateObject("filectl.filesystem")

DD = Day(Now)
MM = Month(Now)
JJJJ = Year(Now)
HH = Hour(Now)
Min = Minute(Now)
Sek = Second(Now)

'Kontrolle ob MMC Karte gesteckt ist
If fso.Dir("\Storage Card SD\") = "" Then     
    ShowSystemAlarm "SD Karte ist nicht gesteckt, daher Abbruch der Datenübertragung"
Else

'Taktzeitarchiv nur umkopieren, wenn Bit "Taktzeitarchiv_umkopieren" True ist
If SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = True Then
'Kontrolle ob Verzeichnis vorhanden. Wenn nicht, dann Verzeichnis erstellen
If fs.dir(Ziel) = "" Then
    fs.mkdir(Ziel)
End If

fs.filecopy Quelle , Ziel & "Taktzeitarchiv" & "_"  & DD & "_" & MM & "_" & JJJJ & "_" & HH & "_" & Min & "_" & Sek & ".csv"           
fs.kill Quelle
ShowSystemAlarm "Taktzeitarchiv wurde erfolgreich abgespeichert"
Else
    SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = False
End If
End If

'Skriptvariable zurücksetzen
SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = False

'Filehandling zurücksetzen
Set fs = Nothing
Set fso = Nothing

End Sub

Mit dem Bit "DB_Skriptvariablen_Taktzeitarchiv_umkopieren" wird das zweite Skript angestoßen.

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Warum kein On Error Resume Next ?
dieses "On Error Resume Next" bewirkt bei einem Laufzeitfehler, das der Code danach trotzdem ausgeführt wird?

Sprich also wäre ein Ansatz so:

Code:
'Scriptanfang
On Error Resume Next

'Kontrolle ob Verzeichnis vorhanden. Wenn nicht, dann Verzeichnis erstellen
If Err.Number = 0 Then
If fs.dir(Ziel) = "" Then
    fs.mkdir(Ziel)
End If
End If

Wird dann der Laufzeitfehler auch im Panel dann angezeigt? Über Systemmeldungen o.ä.?
es fehlt die Auswertung von die Err Zustand.

Etwas in diesen Stil:
Code:
If Err.Number <> 0 Then
    ShowSystemAlarm "Script error auf Position 1. " & Err.Number & " " & Err.Description
    Err.Clear
    Exit Sub
End If
Das Exit bewirkt dass das Skript gestoppt wird.
Das Showsystemalarm bewirkt dass die Fehler in das Meldearchiv gespeichert wird. Ohne dies kann Skript Fehler schwierig zu erkennen sein.

edit: Und die Auswertung macht nur Sinn nach die Skript Code die 'gefährlich' sein kann.
 
Zuletzt bearbeitet:
Die Meldung kommt wenn zu viele VBS Skripts auf einmal ausgeführt werden muss.
1. Die Skript-Aufruf über Wertänderung passiert zu häufig.
2. Ein Skript bleibt in eine Zeile stehen und wird nicht abgeschlossen. Wiederholte Aufrufe führt nur zu dass mehrere Aufrufe warten, bis die Grenze errreicht wird, 20 glaube ich.

Passiert die Fehler wenn du das Skript über ein Button nur einmal aufrufst ?
Probier mal das On Err usw zu implementieren.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Passiert die Fehler wenn du das Skript über ein Button nur einmal aufrufst ?
Ich habe aktuell ja drin stehen, wenn die Zeilennummer größer 9 ist, dann soll er das umkopieren.
Mach ich das umkopieren das erste mal, funktioniert es ohne Fehlermeldung.

Beim zweiten mal umkopieren (also neues Archiv wieder einmal gefüllt) kommt dann die Fehlermeldung.

Probier mal das On Err usw zu implementieren.
Wo sollte ich die Abfrage denn einbinden? Hinter jede If Abfrage wo irgendwas gemacht wird?

Gruß
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was macht denn KILL, wenn die Datei gerade geöffnet ist? Hängt der ggf. und das Script läuft nicht weiter?

Versuche mal bitte, wenn Du die Zeilen gezählt hast, erst einmal die Datei in Archiv.old umzubenennen. Erst dann den Trigger setzen.
Und im Kopier-Script kopierst Du dann nicht die Archiv.csv, sondern die Archiv.old.

Nur eine Idee beim schnellen Drübergucken.

Warum machst Du das überhaupt in zwei Scripten und behandelst das Umkopieren nicht sofort in dem Logging-Script? Dann können sich auch nicht zwei Scripte behaken?
 
Die Fehlermeldung ist die 20015
Überlast: Skript <Archiv_Kopieren_Loeschen> wird verworfen.
Es werden zwar nicht zuviele Skripte in die Warteschlange eingestellt, aber das selbe Skript mehrmals bzw. während es noch läuft!
Weil in den Skripten mehrere Zuweisungen/Schreibzugriffe auf das Trigger-Bit stattfinden ("DB_Skriptvariablen_Taktzeitarchiv_umkopieren")

Beachte: Auch Triggerbit rücksetzen löst das Ereignis "Wertänderung" aus! (wenn das Triggerbit in der PLC liegt)

Das Rücksetzen des Triggerbits sollte die letzte Anweisung im Skript sein. Und gleich am Skriptanfang sollte die Triggervariable abgefragt werden, welchen Wert sie hat und nur bei True das Skript ausführen bzw. bei False das Skript direkt beenden:
Code:
Sub Archiv_Kopieren_Loeschen()
Dim ...
If SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = False Then
    Exit Sub 'Skript beenden
End If

...

'Triggerbit rücksetzen
SmartTags("DB_Skriptvariablen_Taktzeitarchiv_umkopieren") = False
End Sub

Wie schon geschrieben, würde ich aber das Skript Archiv_Kopieren_Loeschen() gar nicht über eine Wertänderung aufrufen, sondern das erste Skript kann dieses Skript direkt aufrufen.

Harald
 
Weitere unschöne und unnötig umständliche Sachen:
Code:
Sub Archiv()
(...)
'Kontrolle ob Verzeichnis vorhanden. Wenn nicht, dann Verzeichnis erstellen
If fs.dir(Pfad) = "" Then
    fs.mkdir(Pfad)
    f.close      <----- !!!
End If
Das f.close ist da zuviel.


Code:
DD = Day(Now)
MM = Month(Now)
JJJJ = Year(Now)
HH = Hour(Now)
Min = Minute(Now)
Sek = Second(Now)
Will man die aktuelle Systemzeit mehrfach verarbeiten, dann darf man sie trotzdem nur einmal abfragen! Betrifft die VBS-Funktionen: Now, Date, Time
z.B. so:
Code:
' aus der Systemzeit einen String in der Form "20991231_015959" erzeugen
Dim t, s
t = Now ' Systemzeit
s = Year(t) & Right("0" & Month(t), 2) & Right("0" & Day(t), 2) & "_" _
  & Right("0" & Hour(t), 2) & Right("0" & Minute(t), 2) & Right("0" & Second(t), 2)
siehe auch Datum_Uhrzeit-String für Dateinamen erzeugen

Code:
'Kontrolle ob Tag < 10 ist. Wenn ja, dann eine 0 vor die Zahl hängen
If DD < 10 Then
    DD_HV = CStr("0" & DD)
Else
    DD_HV = CStr(DD)
End If
Zahlen zweistellig mit ggf. Vornull erzeugt man einfacher so:
DD_HV = Right("0" & DD, 2)


In csv-Dateien ist der Separator ";" ein Trennzeichen zwischen Werten, aber kein Endezeichen hinter dem letzten Wert. Am Zeilenende gehört kein ";" hin.
Weil beim Einlesen der csv-Datei gibt es dann ein Problem: Split() liefert ein leeres Element zuviel

Harald
 
Zurück
Oben