# Archivierung per skript



## Tigerente1974 (14 September 2013)

Hallo Forum.

Ich möchte mich erstmalig an skripte heranwagen.

Die Aufgabenstellung ist folgende:

Eine Palette wird gescannt. Der Barcode soll zusammen mit 2 weiteren Werten in einer csv-Datei abegelegt werden.

Dieses Beispiel von Siemens scheint mir der richtige Ansatz zu sein: http://support.automation.siemens.com/WW/view/de/28937151

Ich habe das Beispielporjekt MP277 geöffnet.
Ich möchte nicht die Variante #2 mit dem Meldearchiv nutzen, sondern die Variante #1.

In der Simulation am laptop erhalte ich die Fehlermeldung "Active X - Komponente kann kein Objekt erstellen FileCtl.FileSystem <Script 01> in Zeile 13"

Ich stehe noch total am Anfang und weiß nicht woran das liegt. Kann jemand helfen?

Edit: Ich arbeite mit WinCC flexible 2008-SP3


----------



## Thomas_v2.1 (14 September 2013)

Es gibt Unterschiede zwischen VB auf Windows CE und auf einem "normalen" Windows PC. Wenn du es in der Simulation laufen lässt sind die Windows Funktionen zu verwenden. Für die Simulation müsstest du das Skript anpassen, oder eine Abfrage für das Betriebssystem einbauen.

Das ist in deinem Link auch weiter verlinkt:
http://support.automation.siemens.com/WW/view/de/13408815


----------



## Tigerente1974 (14 September 2013)

Bei dem Beispielprojekt war auch eines für die PC Runtime dabei.

Soweit hat damit alles geklappt


----------



## Tigerente1974 (14 September 2013)

Jetzt brauche ich nochmal Hilfe.

In dem Beispielprojekt wurden interne Variablen verwendet.
Ich möchte Werte aus einem Global-DB auf der SPS archivieren.

Leider habe ich weder durch Lesen noch durch Probieren herausgefunden wie das geht.

In dem Beispielskript sah das so aus:


```
' Werte der Variablen in die Archivdatei schreiben (Tag_x)
	' Write values of variables in archive file (Tag_x)
	Dataset = CStr(Now) & ";" & CStr(SmartTags("TAG_01")) & ";" & CStr(SmartTags("TAG_02")) 'Create the Dataset'
```

TAG_01 und TAG_02 sind interne Variablen.

An die Stelle der internen Variablen möchte ich jetzt meine Werte aus dem Global-DB "Daten" schreiben.

Z.B.: Daten.Wert_1

Wie bekomme ich das hin?


----------



## Didaddy (14 September 2013)

Wenn du die internen Variablen in der Variablentabelle von WinCC Flexible an die entsprechenden Hardwareadressen von deienm GLOBAL DB bindest müsste es das doch gewesen sein. Oder verstehe ich das falsch?


----------



## Tigerente1974 (14 September 2013)

Vielleicht, oder wahrscheinlich stehe ich auf dem Schlauch...

Aber die interne Variable kann ich nicht mit der Adresse verbinden.
Intern bedeutet ja auch, dass nur das Panel die Variable kennt. So richtig nachvollziehbar fände ich das auch nicht.


----------



## Didaddy (14 September 2013)

Immer bissel blöd wenn man zuhause ist und nicht mal geschwind reinschauen kann. In TIA hab ich das erst kürzlich gemacht. Dachte das handling in WinCCFlexible 2008 müsste da gleich sein. Kannste nicht da wo die Variablen deklariert sind, also in der allgemeinen Variablentabelle in der Zeile wo die Varable deklariert ist, einfach eine Hardwareadresse auswählen. Die Variable ist dann nicht mehr intern, das ist richtig.


----------



## Matze001 (14 September 2013)

Du deklarierst einfach deine Variable aus dem Global DB unter Variablen im WinCCflex.

Diese kannst du dann einfach im Script verwenden mit:


```
SmartTags("MEINE_GLOBALE_VARIABLE")
```

Grüße

Marcel

P.S: Der Name im WinCCflex zählt!


----------



## Onkel Dagobert (14 September 2013)

Die Tastenkombination <alt> + <rechts> erleichtert das Editieren an dieser Stelle ungemein.

Gruß, Onkel


----------



## Tigerente1974 (14 September 2013)

Argh...

Manchmal sieht man den Wald vor Bäumen nicht :roll:

Ich habe bei den Variablen Unterordner angelegt. In diesem Fall "Archivierung".

Also muss ich die Variable natürlich auch so angeben: "Archivierung\MeinDB.Meine_Variable". Der Compiler hat zu Recht gemeckert, dass ich nur "MeinDB.Meine_Variable" geschrieben habe.

Die Tastenkombination [Alt]+[Rechts] hat mir dabei geholfen meinen Fehler zu finden. Dann wird der Name inkl. Pfad nämlich korrekt angelegt.
Vielen Dank an alle!


----------



## Tigerente1974 (15 September 2013)

Ich denke ich habe das verstanden, wie man Daten auf einem USB-Stick archivieren kann.
Jetzt kommt der nächste step.
Ich habe schon mal Meldearchive mit der Archivierungsfunktion -also ohne skript- auf den server des Kunden abgelegt.
Dabei konnte ich die Archive nicht unmittelbar auf dem Kundenserver ablegen. Die Archivierung wurde immer abgebrochen, wenn die Verbindung zum server "verlorenging". Die Archivierung erfolgt nun auf einem USB-Stick am Panel.
Ich habe das dann über den Aufgabenplaner so gelöst, dass die Archivierung stündlich gestoppt, auf den Server kopiert und wieder gestartet wird.
Das funktionierte dann wie gewünscht.

Ich denke ich brauche für die Skriptlösung des Variablenarchivs eine ähnliche Lösung.
Nach einigem Lesen erschien mir die Lösung aus der FAQ brauchbar.

http://www.sps-forum.de/hmi/41710-move-bzw-dateien-kopieren-2.html

Ich habe da noch ein paar Fragen:

1. Stimmt es, dass nicht kopiert/verschoben wird, wenn die Datei schon am Ziel vorhanden ist?
2. Falls überschrieben werden kann. Was geschieht, wenn die Zieldatei geöffnet ist?


----------



## Tigerente1974 (15 September 2013)

update:

Ich habe jetzt erstmal zum Reinkommen mit der PC_Runtime geübt. Dort habe ich den "copyfolder"-Befehl benutzt, um den gesamten Archivordner zu kopieren.
Das hat funktioniert. Die Dateien werden auch standardmäßig überschrieben, wenn kein entsprechender Parameter mitgegeben wird.
Für die WIN CE-Variante werde ich prüfen, ob es etwas ähnliches gibt.


----------



## Tigerente1974 (22 September 2013)

Jetzt könnte ich noch etwas Hilfe gebrauchen.
Das komplette Szenario sieht jetzt wie folgt aus:

1. Auf einem USB-Stick werden Daten im Append-Modus in einer csv-Datei archiviert. Das klappt problemlos
2. Auf dem server des Kunden soll in einer festen Datei ebenfalls im Append-Modus archiviert werden.
Bei jedem neuen Eintrag soll geprüft werden, ob die Datei auf dem server aktualisiert werden kann.
Dies hängt von der Erreichbarkeit des servers und dem Vorhandensein einer "WriteAccess"-Datei ab.
Es ist also möglich, dass mehrere Einträge gesammelt werden, bevor geschrieben werden kann.

Wenn ich die Archivdatei immer nur kopiere, wird sie wohl irgendwann die Speicherkapazität des Sticks sprengen...

Also möchte ich die server-Datei nur aktualisieren und anschließend die Quelldatei auf dem Stick löschen.
Bei der Aktualisierung muss ich dafür dann wissen, wieviele Einträge ich aktualisieren muss.
Das geschieht aktuell über den Umweg, die Einträge der Quelldatei in ein Array zu kopieren. Das gefällt mir nicht so gut.
In der Zieldatei bleiben bei jeder Aktualisierung 2 Zeilen zwischen den Werten frei.

1. Gibt es einen eleganteren Weg für mein Vorhaben?
2. Wie bekomme ich die Leerzeilen weg?


```
Dim PathSearch, Path, Index, Result, fso, SourceFile, DestFile, f, fs, Header, StartTime, StopTime, DelayTime, i, Zeile(10000)

Set PathSearch = CreateObject("FileCtl.FileSystem")

'USB-Stick suchen
'--------------------

    Path = "Storage Card USB" 
    
    SetValue SmartTags("Status_kopieren"), 10                 'Text: Suche USB-Stick
    
    StartTime = Now
    DelayTime = 500        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do
    If  PathSearch.Dir("\" & Path) = ""_
        And Now >= StopTime Then
        SetValue SmartTags("Status_kopieren"), 20            'Text: USB-Stick nicht gefunden!
        
        ElseIf PathSearch.Dir("\" & Path) = Path Then     
        SetValue SmartTags("Status_kopieren"), 30            'Text: Suche Zielordner
    End If
    Loop Until Now >= StopTime

'30: Ziellaufwerk suchen
'-----------------------

If SmartTags("Status_kopieren") = 30 Then
    Path = "\\LAPTOP_WO\Logs\"
    
    StartTime = Now
    DelayTime = 500        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
    Result = Ziel_pruefen (Path)
    If  Result = "Logs" Then
        SetValue SmartTags("Status_kopieren"), 50            'Text: Schreibfreigabe prüfen
    ElseIf Now >= StopTime Then    
        SetValue SmartTags("Status_kopieren"), 40            'Text: Zielordner nicht gefunden
    End If
    Loop Until Now >= StopTime
End If 


'50: Schreibfreigabe prüfen
'--------------------------

If SmartTags("Status_kopieren") = 50 Then
    Path = "\\LAPTOP_WO\Logs\WriteAccess.ok"
    
    StartTime = Now
    DelayTime = 500        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
    Result = Ziel_pruefen (Path)
    If  Result = "WriteAccess.ok" Then
        SetValue SmartTags("Status_kopieren"), 70            'Text: Zieldatei suchen
    ElseIf Now >= StopTime Then    
        SetValue SmartTags("Status_kopieren"), 60            'Text: keine Schreibfreigabe
    End If
    Loop Until Now >= StopTime
End If 

'70: Zieldatei abfragen
'---------------------

If SmartTags("Status_kopieren") = 70 Then
    
    Set f = CreateObject("FileCtl.File")
    Set fs = CreateObject("FileCtl.FileSystem")
    
    Path = "\\LAPTOP_WO\Logs\Data.csv"
    
    'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
    Ordnerpfad_anlegen Path ' Übergabe des Ablagepfades aus dem Script "Script_Storage_Path" / Handing over of the storage path from the script "Script_Storage_Path"

    StartTime = Now
    DelayTime = 1000        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
    Result = Ziel_pruefen (Path)
    If  Result = "Data.csv" Then
        SetValue SmartTags("Status_kopieren"), 90            'Text: Einträge kopieren
    ElseIf Now >= StopTime Then    
        SetValue SmartTags("Status_kopieren"), 80            'Text: Datei nicht gefunden
    End If
    Loop Until Now >= StopTime
End If 

' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
' Open File or create file if it does not exist 
 f.open Path, 8                                 

' Tabellenkopf zusammenstellen 
' Create table header 
 Header = "Storage time;Barcode;Wert" &  Chr(10)
 
' Wenn Datei 0 Byte groß, dann ist sie neu
' If file is 0 byte the file is new
 If fs.FileLen(Path) = 0 Then                      
   f.lineprint(Header)
' =0 => Tabellenkopf muss eingefügt werden
 End If 
 ' Datei wieder schließen
     f.Close
     
'90: letzten Eintrag suchen
'--------------------------

If SmartTags("Status_kopieren") = 90 Then
    
    Set fso = CreateObject("FileCtl.FileSystem")
    
    SmartTags("Name") = fso.Dir("\Storage Card USB\*.csv")        'Suchen nach ersten Dateiname auf Stick
    f.open("\Storage Card USB\" & SmartTags("Name")),1,1
    
    For i = 1 To 10000
    If f.EOF = True Then Exit For
    Zeile(i) = f.LineInputString
    SmartTags("tmpZeile") = Zeile(i)
    Next
    ' Datei wieder schließen
     f.Close
     SetValue SmartTags("Status_kopieren"), 100
End If 


'100: Einträge im Appendmodus anhängen
'-------------------------------------

If SmartTags("Status_kopieren") = 100 Then
    
' FileObject erstellen
' Create FileObject
 Set f = CreateObject("FileCtl.File")
 
' Datei öffnen im Append-Modus
 f.open "\\LAPTOP_WO\Logs\Data.csv", 8 
 
For i = 1 To 10000
    f.lineprint Zeile(i+1)
    If Zeile(i) = SmartTags("tmpZeile") Then Exit For
    Next
    ' Datei wieder schließen
     f.Close 

 SetValue SmartTags("Status_kopieren"), 110
End If
    
'110: Quelldatei löschen
'-----------------------

If SmartTags("Status_kopieren") = 110 Then
     fs.Kill ("\Storage Card USB\" & SmartTags("Name"))
     SetValue SmartTags("Status_kopieren"), 200
End If
' Verwendeten Speicher wieder freigeben

Set f  = Nothing
Set fs = Nothing
Set fso = Nothing
Set PathSearch = Nothing
```


----------



## faust (23 September 2013)

Hallo Tigerente,

zuerst eine Frage: wie sieht der Inhalt deiner Quelldatei genau aus?

Und eine zweite Frage: Verwendest du die Anzahl der hinzuzufügenden Zeilen irgendwie weiter?


Denn die Zeilenumbrüche könntest du schon beim Einlesen der Quelldatei dem zu kopierenden Dateiinhalt hinzufügen:

#######################################################
'90: letzten Eintrag suchen
'--------------------------
....
Statt
    Zeile(i) = f.LineInputString
Verwende
    fileContent = fileContent & f.LineInputString & vbCrLf
....
#######################################################

Beim Anfügen der neuen Zeilen reicht dann:

#######################################################
'100: Einträge im Appendmodus anhängen
'-------------------------------------
....
Statt
    For i = 1 To 10000
        f.lineprint Zeile(i+1)
        If Zeile(i) = SmartTags("tmpZeile") Then Exit For
    Next
Verwende
    f.lineprint fileContent
....
End If
#######################################################


Gruß, Fred


----------



## rostiger Nagel (23 September 2013)

kennt VBS von WinCCflexibel die 'fileContent' Eigenschaft?


----------



## faust (23 September 2013)

'fileContent' ist in diesem Fall einfach eine Variable (im Script zu deklarieren).

Gruß, Fred


----------



## Tigerente1974 (23 September 2013)

Hallo faust,

der Vorschlag hat mich auf jeden Fall weitergebracht. In der bisherigen Version musste ich ein Array deklarieren und war nicht sicher, ob die Anzahl der Felder im Zweifelsfall reicht.
Der Code sieht jetzt wie folgt aus.

90:

```
For i = 1 To 10000
	If f.EOF = True Then Exit For
	fileContent = fileContent & f.LineInputString & vbCrLf
	SmartTags("Laenge") = i
	Next
```

100:

```
f.open "\\LAPTOP_CHRIS_WO\Logs\NVE.csv", 8 

 f.lineprint fileContent
```

Damit der Header der Quelldatei nicht jedesmal in die Zieldatei kopiert wird, habe ich das Erzeugen des Headers in der Quelldatei entfernt.
In der "neuen" Version bleibt eine Zeile bei jeder Aktualisierung frei.
Siehe Anhänge.


----------



## Tigerente1974 (23 September 2013)

Die Länge zeichne ich jetzt auf. Das funktioniert auch. Eine Auswertung der Länge nehme ich aber nicht vor.


----------



## faust (24 September 2013)

Hallo Tigerente,

die Leerzeile erklärt sich dadurch, dass mit dem Befehl 'f.linePrint' automatisch ein Zeilenumbruch eingefügt wird. Da dein Quell-Dateiinhalt (temporär gespeichert in der Variable 'fileContent') am Ende ebenfalls einen Zeileumbruch enthält...

Du musst also beim Einlesen der Quelldatei den letzten Zeilenumbruch mit Hilfe des Befehls 'Left(fileContent, (Len(fileContent) - 2)) abschneiden.


Gruß, Fred


----------



## Tigerente1974 (26 September 2013)

Hallo Fred.

Super!!! Jetzt klappt es 

Danke dafür.

Der Compiler hat allerdings den Code so nicht akzeptiert.
Ich musste das dann so schreiben:


```
For i = 1 To 1000000
		If f.EOF = True Then Exit For
			fileContent = fileContent & f.LineInputString & vbCrLf
			'Leerzeile am Ende löschen
			Laenge = Len (fileContent - 2)
 			fileContent = Left(fileContent,Laenge)
	Next
```

Vielleicht noch eine letzte Frage.
Ich lasse meine Schleife jetzt bis 1000000 laufen. Das wird sicher reichen. Ist aber auch nicht ganz sauber ausprogrammiert.
Gibt es eine Möglichkeit, das sauber zu programmieren?
Ach ja. Warum muss ich eigentlich "2" abziehen, um die Leerzeile zu löschen?


----------



## Tigerente1974 (26 September 2013)

Abschließend stelle ich jetzt mein Ergebnis rein. Vielleicht hilft es nochmal jemandem...

1. Archivdatei auf dem Stick anlegen.


```
Dim fs, f, FName, Header, strDir

' FileSystemObject erstellen
' Create FileSystemObject
Set fs = CreateObject("FileCtl.FileSystem")
 
' Prüfen ob USB-Stick vorhanden ist
' Checking if USB-device available
strDir = fs.dir ("\Storage Card USB\")    

If strDir = "" Then 'Ablagepfad nicht vorhanden / directory not exists, card not available
    Call ShowSystemAlarm ("Kein USB-Stick vorhanden / No USB-device available")
Else
    
' Archivierungspfad (Eingangsparameter) und Dateiname zusammensetzen 
' Combine archivepath (input parameter) and filename
FName = StoragePath & "\PAL_ID_" & CStr(DatePart("m",Date)) & "_" & CStr(DatePart("yyyy",Date)) & ".csv"

' Tabellenkopf zusammenstellen 
' Create table header 

 Header = "Zeitstempel;Barcode;Hoehe" &  Chr(10)

' FileObject erstellen
' Create FileObject
 Set f = CreateObject("FileCtl.File")

'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
'Checks storage path availably, if not -> create
Ordnerpfad_anlegen StoragePath ' Übergabe des Ablagepfades aus dem Script "Script_Storage_Path" / Handing over of the storage path from the script "Script_Storage_Path"

' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
' Open File or create file if it does not exist 
 f.open FName, 8                                 

' Werte der Variablen in die Archivdatei schreiben (VAR_1)
' Write values of variables in archive file (VAR1)
 f.lineprint(CStr(Now) & ";" & CStr(SmartTags("PAL_ID")) & ";" & CStr(SmartTags("Hoehe"))) 

' Datei wieder schließen
' Close file
 f.Close
End If
' Verwendeten Speicher wieder freigeben
' Used storage will be freed
Set f  = Nothing
Set fs = Nothing
```

2. Daten vom Stick auf den Server kopieren

Dazu wird die Quelldatei Zeile für Zeile ausgelesen und im Append-Modus auf die Zieldatei geschrieben.
Schreibfreigabe und Vorhandensein der Dateien werden geprüft.


```
Dim PathSearch, Path, Index, Result, fso, SourceFile, DestFile, f, fs, Header, StartTime, StopTime, DelayTime, i, Laenge, fileContent

Set PathSearch = CreateObject("FileCtl.FileSystem")

'USB-Stick suchen
'--------------------

Path = "Storage Card USB" 
    
SetValue SmartTags("Status_kopieren"), 10                 'Text: Suche USB-Stick
    
StartTime = Now
DelayTime = 500        'Timer 100 endspricht 1sec.
StopTime = StartTime + DelayTime / 24 / 360000
Do
    If  PathSearch.Dir("\" & Path) = ""_
        And Now >= StopTime Then
        SetValue SmartTags("Status_kopieren"), 20            'Text: USB-Stick nicht gefunden!
        
        ElseIf PathSearch.Dir("\" & Path) = Path Then     
        SetValue SmartTags("Status_kopieren"), 30            'Text: Suche Zielordner
    End If
Loop Until Now >= StopTime

'30: Ziellaufwerk suchen
'-----------------------

If SmartTags("Status_kopieren") = 30 Then
    Path = "\\LAPTOP_WO\Logs\"
    
    StartTime = Now
    DelayTime = 500        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
        Result = Ziel_pruefen (Path)
        If  Result = "Logs" Then
            SetValue SmartTags("Status_kopieren"), 50            'Text: Schreibfreigabe prüfen
        ElseIf Now >= StopTime Then    
            SetValue SmartTags("Status_kopieren"), 40            'Text: Zielordner nicht gefunden
        End If
    Loop Until Now >= StopTime
End If 


'50: Schreibfreigabe prüfen
'--------------------------
' Die Datei "Write_Access.ok" muss im Zielordner vorhanden sein.
If SmartTags("Status_kopieren") = 50 Then
    Path = "\\LAPTOP_WO\Logs\Write_Access.ok"
    
    StartTime = Now
    DelayTime = 500        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
        Result = Ziel_pruefen (Path)
        If  Result = "Write_Access.ok" Then
            SetValue SmartTags("Status_kopieren"), 70            'Text: Zieldatei suchen
        ElseIf Now >= StopTime Then    
            SetValue SmartTags("Status_kopieren"), 60            'Text: keine Schreibfreigabe
        End If
    Loop Until Now >= StopTime
End If 

'70: Zieldatei prüfen
'---------------------

If SmartTags("Status_kopieren") = 70 Then
    
    Set f = CreateObject("FileCtl.File")
    Set fs = CreateObject("FileCtl.FileSystem")
    
    Path = "\\LAPTOP_WO\Logs\Archiv.csv"
    
    'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
    Ordnerpfad_anlegen Path ' Übergabe des Ablagepfades aus dem Script "Script_Storage_Path" / Handing over of the storage path from the script "Script_Storage_Path"
    
    ' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
    f.open Path, 8                                 

    ' Tabellenkopf zusammenstellen 
    Header = "Zeitstempel;Barcode;Hoehe" &  Chr(10)
 
    ' Wenn Datei 0 Byte groß, dann ist sie neu
    If fs.FileLen(Path) = 0 Then                      
           f.lineprint(Header)
    '=0 => Tabellenkopf muss eingefügt werden
     End If 
     ' Datei wieder schließen
     f.Close
     
    StartTime = Now
    DelayTime = 1000        'Timer 100 endspricht 1sec.
    StopTime = StartTime + DelayTime / 24 / 360000
    Do                        
        Result = Ziel_pruefen (Path)
        If  Result = "Archiv.csv" Then
            SetValue SmartTags("Status_kopieren"), 90            'Text: Einträge kopieren
        ElseIf Now >= StopTime Then    
            SetValue SmartTags("Status_kopieren"), 80            'Text: Datei nicht gefunden
        End If
    Loop Until Now >= StopTime
End If 

'90: Datei suchen und auslesen
'-----------------------------

If SmartTags("Status_kopieren") = 90 Then
    
    Set fso = CreateObject("FileCtl.FileSystem")
    
    SmartTags("Name") = fso.Dir("\Storage Card USB\*.csv")        'Suchen nach ersten Dateiname auf Stick
    f.open("\Storage Card USB\" & SmartTags("Name")),1,1
    
    For i = 1 To 1000000
        If f.EOF = True Then Exit For
            fileContent = fileContent & f.LineInputString & vbCrLf
            'Leerzeile am Ende löschen
            Laenge = Len (fileContent - 2)
             fileContent = Left(fileContent,Laenge)
    Next
    ' Datei wieder schließen
     f.Close
     SetValue SmartTags("Status_kopieren"), 100
End If 


'100: Einträge im Appendmodus anhängen
'-------------------------------------

If SmartTags("Status_kopieren") = 100 Then
    
    ' FileObject erstellen
    Set f = CreateObject("FileCtl.File")
 
    ' Datei öffnen im Append-Modus
    f.open "\\LAPTOP_WO\Logs\Archiv.csv", 8 
    ' Einträge anhängen
    f.lineprint fileContent
    ' Datei wieder schließen
    f.Close 

    SetValue SmartTags("Status_kopieren"), 110
End If
    
'110: Quelldatei löschen
'-----------------------

If SmartTags("Status_kopieren") = 110 Then
     fs.Kill ("\Storage Card USB\" & SmartTags("Name"))
     SetValue SmartTags("Status_kopieren"), 200
End If

' Verwendeten Speicher wieder freigeben

Set f  = Nothing
Set fs = Nothing
Set fso = Nothing
Set PathSearch = Nothing
```

Es werden noch die Sub-Routine "Ordernpfad_anlegen" und die Funktion "Ziel_prüfen" verwendet.

Ordnerpfad anlegen:


```
'Hinweis/Note:
'Wenn der vorgegebene Ablagepfad im "Hauptscript" mehrere Unterordner enthält, 
'müssen diese Unterordner auf der Speicherkate komplett vorhanden sein. 
'Dieses Script dient dazu, dieses automatisch umzusetzen.

'If the predefined storage path includes several sub-folders in the "Main script", 
'the path of the sub-folders must already be completely available on the memory card . 
'This script serves to convert this automatically. 

Dim fs, strDir, strTemp, arr

' FileSystemObject erstellen
' Create FileSystemObject
Set fs = CreateObject("FileCtl.FileSystem") 

strTemp = "" ' Hilfsvariable / Help tag

arr = Split (strPath, "\") ' Ablagepfad in einzelen "\" zerlegen / split the Storage path in several "\" 

For Each strDir In arr ' Schleifenzähler / loop counter
    If Len(strDir) > 0 Then
        strTemp = strTemp + "\" + strDir
        
        strDir = fs.dir (strTemp)
        
        If strDir = "" Then ' Wenn kein "\" mehr vorhanden ist -> keine weiteren Unterpfade / If no "\" existing, then no further sub-folders
            fs.mkDir strTemp
        End If    
    End If
Next 

Set fs = Nothing
```

Ziel_pruefen:


```
Dim fso
  Set fso = CreateObject("FileCtl.FileSystem")
  Ziel_pruefen = fso.Dir(Ziel)
```


----------



## faust (26 September 2013)

Tigerente1974 schrieb:


> Vielleicht noch eine letzte Frage.
> Ich lasse meine Schleife jetzt bis 1000000 laufen. Das wird sicher reichen. Ist aber auch nicht ganz sauber ausprogrammiert.
> Gibt es eine Möglichkeit, das sauber zu programmieren?
> Ach ja. Warum muss ich eigentlich "2" abziehen, um die Leerzeile zu löschen?



Guten Morgen,

zu deiner ersten Frage: ich würde eine Do...Loop-Schleife o.ä. verwenden, da du den Index 'i' ja nicht weiter benötigst/verwendest. Eine Abbruchbedingung hast du ja bereits jetzt integriert (If f.EOF...).
Zu deiner zweiten Frage: Ein Zeilenumbruch stellt sich unter Windows als die beiden Zeichen Chr(13) für CarriageReturn und Chr(10) für LineFeed dar.


Gruß, Fred


----------



## Tigerente1974 (26 September 2013)

Jetzt habe ich quasi bei 0 angefangen und bin an 2 Wochenenden zum Ziel gekommen.

Toll, dass es dieses Forum gibt!


----------



## PN/DP (26 September 2013)

Tigerente1974 schrieb:


> Abschließend stelle ich jetzt mein Ergebnis rein. Vielleicht hilft es nochmal jemandem...


Zunächst: Ein schönes Stück Arbeit - viele Programmierer kommen gar nicht so weit... 
	

	
	
		
		

		
			





Natürlich gibt es da noch Sachen, die man verbessern kann:

*1. Skriptnamen*

Schreibe doch bitte in den Skripten als erste Zeile den (exakt geschriebenen!) Name der Sub/Function als Kommentar, wie z.B. hier


> ```
> ' Sub SettingsIniSpeichern()
> ```
> 
> ...




*2. In Deinem 2. Skript: Deine eigentlich gut gemeinten Timeout-Schleifen sind "unglücklich" gebaut und unnötig.*

Auch wenn die gewünschte Operation erfolgreich war, so wird sie trotzdem 5 Sekunden lang wiederholt, ehe die Timeout-Schleife verlassen wird. Da fehlt "*Exit Do*". (Oder baue die Schleife anders: die gewünschte Operation in die Loop-Abbruchbedingung und den Timeout in den "Notausgang" Exit Do)

```
Do                        
        Result = Ziel_pruefen (Path)
        If  Result = "Logs" Then
            SetValue SmartTags("Status_kopieren"), 50            'Text: Schreibfreigabe prüfen
            [COLOR="#FF0000"]Exit Do[/COLOR]
        ElseIf Now >= StopTime Then    
            SetValue SmartTags("Status_kopieren"), 40            'Text: Zielordner nicht gefunden
        End If
    Loop Until Now >= StopTime
```

Alle Deiner Zwangs-Warteschleifen (bzw. gewollte Timeout-Schleifen) sind aus meiner Sicht unnötig.
Z.B. die erste 5s-Warteschleife zum Suchen des USB-Sticks: Ein USB-Stick ist angeschlossen oder nicht, er taucht nicht innerhalb von 5s doch noch auf ...

Bei den meisten Funktionen ist es unerheblich, ob man sie einmal oder 5s lang immer wieder aufruft - wenn der erste Aufruf einen Fehler meldet, dann wird höchstwahrscheinlich auch der 1000. Aufruf den Fehler melden.
Es gibt echt sehr wenig Funktionen wo es sinnvoll ist, diese wiederholt aufzurufen. Da fällt mir gerade nur ein Beispiel ein: das Suchen einer entfernten Netzwerkressource, wo eventuell erst ein VPN-Kanal aufgebaut wird oder ein Computer aufgeweckt werden muß.

Funktionen wo ein Timeout sinnvoll ist haben meist einen eigenen Timeout und kommen sowieso nicht eher zum Skript zurück.


*3. Variablen lesen von SPS in RT*

Wenn Dein Skript dann nicht mehr die unnötigen langen Warteschleifen hat, dann wirst Du wahrscheinlich auf ein neues noch unentdecktes Problem stossen: Wie ist sichergestellt, daß die RT die aktuellen Werte Deiner zu archivierenden Variablen vor dem Schreiben in die Datei auch aus der SPS gelesen hat?
Zu diesem Thema schaue mal hier (Link zu einem Codebeispiel) und benutze die Forumssuche nach *GetDataRecordTagsFromPLC*



*4. PS:*


Tigerente1974 schrieb:


> Jetzt habe ich quasi bei 0 angefangen und bin an 2 Wochenenden zum Ziel gekommen.


Ich kann mir sehr gut vorstellen, daß viele heute übliche Software genau in dem Stadium vergleichbar mit Deinem Entwicklungsstand ist. Die Programmierer sind bestimmt "stolz wie Hubatz" über ihre Software (weil die ohne sichtbare Fehlermeldungen irgendwann zu einem Ende kommt) und ahnen gar nicht, was für unsicheren Dreck sie da programmiert haben. Da wird nur aufwendig an bunten Animationen gefeilt statt die eigentliche Funktion kritisch zu überprüfen.

Also Tigerente1974: Du bist in professioneller Gesellschaft 

Für Deinen Willen, das Ergebnis Deiner Arbeit auch anderen zur Verfügung zu stellen, gebe ich Dir aber trotzdem ein DANKE. 

Harald


----------



## PN/DP (26 September 2013)

Übrigens:
Um SmartTags einen Wert zuzuweisen oder den Wert zu lesen ist es nicht nötig, die Funktionen SetValue und GetValue zu benutzen. Man kann direkt das SmartTag-Objekt verwenden - das liest sich oft besser:

```
Dim scriptVar

scriptVar = SmartTags("Tag_1")
SmartTags("Tag_2") = scriptVar
SmartTags("Tag_3") = 50
SmartTags("Tag_4") = "Alles OK"
SmartTags("Tag_5") = SmartTags("Tag_3")
```

Harald


----------



## Tigerente1974 (26 September 2013)

Hallo Harald. Vielen Dank für Dein feedback.
Es drückt grade etwas die Zeit für mein Projekt. Trotzdem werde ich versuchen die Vorschläge noch umzusetzen.
Wenn ich getesteten Code habe, stelle ich noch eine Aktualisierung ein.


----------



## Tigerente1974 (28 September 2013)

Hallo Harald.
Ich hatte heute morgen etwas Zeit mich intensiver einzulesen.
Das "Problem" mit der Datenkonsistenz wäre mir -wenn überhaupt- erst viel zu spät aufgefallen.
Ich habe jetzt eine fertige Lösung, die Deine Anregungen aufgreift.
Nächste Woche kann ich den Code testen und werde berichten.


----------



## Tigerente1974 (4 Oktober 2013)

Wie angekündigt poste ich die neue Version.
Diese berücksichtigt die bisherigen Anregungen. Vor allem auch das Einlesen der Variablen über eine Rezeptur, um Datenkonsistenz zu gewährleisten.
Bisher sind mir noch keine Fehler aufgefallen. Für weitere Anregungen bin ich weiterhin dankbar.

1. Variablen per Rezeptur einlesen

```
' Sub Variable_einlesen
' Um Datenkonsistenz für die zu archivierenden Variablen sicherzustellen, wurden diese in der
' Rezeptur "ReadVar" deklariert. Mit "GetDataRecordTagsFromPLC" werden die Variablen aus der SPS
' gelesen. Anschließend wird das Skript "String_archivieren_USB" angestossen, um die Daten zunächst 
' auf dem USB-Stick am HMI zu archivieren.

Dim wrStat, rdStat

' Variable in SPS schreiben mit Fertigmeldung
 SetDataRecordTagsToPLC "WriteVar", SmartTags("Archivierung\Schreibstatus")
' in Schleife auf Ende Var-Schreiben warten
Do
    wrStat = SmartTags("Archivierung\Schreibstatus")
Loop While (wrStat And 4) = 0 ' 4=fertig OK / 12=abgebrochen mit Fehler

' wenn Schreiben ohne Fehler, dann Antwort (Protokoll-Datensatz) aus SPS lesen
If wrStat = 4 Then
    ' Variablen aus SPS lesen mit Fertigmeldung
    GetDataRecordTagsFromPLC "ReadVar", SmartTags("Archivierung\Lesestatus")
    ' in Schleife auf Ende Var-Lesen warten
    Do
        rdStat = SmartTags("Archivierung\Lesestatus")
    Loop While (rdStat And 4) = 0 ' 4=fertig OK / 12=abgebrochen mit Fehler

    If rdStat = 4 Then
        String_archivieren_USB("\Storage Card USB\")
    End If
End If

If wrStat <> 4 Or rdStat <> 4 Then
    ShowSystemAlarm "Fehler beim Lesen der Protokollwerte!"
End If
```

2. Archivieren auf den USB-Stick

```
' Sub String_archivieren_USB(StoragePath)

Dim fs, f, FName, Header, strDir

' FileSystemObject erstellen
' Create FileSystemObject
Set fs = CreateObject("FileCtl.FileSystem")
 
' Prüfen ob USB-Stick vorhanden ist
strDir = fs.dir ("\Storage Card USB\")	

If strDir = "" Then 'Ablagepfad nicht vorhanden
	Call ShowSystemAlarm ("Kein USB-Stick vorhanden")
Else
	
' Archivierungspfad (Eingangsparameter) und Dateiname zusammensetzen 
FName = StoragePath & "\PAL_ID" & ".csv"

' FileObject erstellen
 Set f = CreateObject("FileCtl.File")

'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
Ordnerpfad_anlegen StoragePath ' Übergabe des Ablagepfades aus dem Script "Ordnerpfad anlegen"

' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
 f.open FName, 8 								

' Werte der Variablen mit Zeitstempel in die Archivdatei schreiben
 f.lineprint(CStr(Now) & ";" & CStr(SmartTags("Archivierung\ARCHIV_COMMDB.PAL_ID")) & ";" & CStr(SmartTags("Archivierung\ARCHIV_COMMDB.Pal_Hoehe"))) 

' Datei wieder schließen
 f.Close
End If

' Verwendeten Speicher wieder freigeben
Set f  = Nothing
Set fs = Nothing

' Prüfen, ob Daten auf den Server geschrieben werden können
Datei_kopieren	'Skript zum Kopieren der Daten vom Stick auf den Server anstoßen
```

3. Prüfen, ob Daten auf den Server kopiert werden können.
Wenn ja: Quelldatei Zeile für Zeile auslesen und in die Zieldatei schreiben.
Anschließend die Quelldatei löschen.

```
'Sub Datei_kopieren

Dim PathSearch, Path, Index, Result, fso, SourceFile, DestFile, f, fs, Header, StartTime, StopTime, DelayTime, i, Laenge, fileContent

Set PathSearch = CreateObject("FileCtl.FileSystem")

'USB-Stick suchen
'--------------------

Path = "Storage Card USB" 
    
SmartTags("Archivierung\Status_kopieren") = 10                'Text: Suche USB-Stick
    
If  PathSearch.Dir("\" & Path) = "" Then
	SmartTags("Archivierung\Status_kopieren") = 20            'Text: USB-Stick nicht gefunden!
    ElseIf PathSearch.Dir("\" & Path) = Path Then     
    SmartTags("Archivierung\Status_kopieren") = 30            'Text: Suche Zielordner
End If

'30: Ziellaufwerk suchen
'-----------------------

If SmartTags("Archivierung\Status_kopieren") = 30 Then
	Path = "\\LAPTOP_WO\Logs"
	
	StartTime = Now
	DelayTime = 500		'Timer 100 endspricht 1sec.
	StopTime = StartTime + DelayTime / 24 / 360000
	Do                        
    	Result = Ziel_pruefen (Path)
    	If Result = "Logs" Then
    		SmartTags("Archivierung\Status_kopieren") = 50      'Text: Schreibfreigabe prüfen
    		Exit Do
    	ElseIf Now >= StopTime Then    
        	SmartTags("Archivierung\Status_kopieren") = 40      'Text: Zielordner nicht gefunden
		End If
	Loop Until Now >= StopTime
End If 


'50: Schreibfreigabe prüfen
'--------------------------
' Die Datei "Write_Access.ok" muss im Zielordner vorhanden sein.
If SmartTags("Archivierung\Status_kopieren") = 50 Then
	Path = "\\LAPTOP_WO\Logs\Write_Access.ok"                  
    Result = Ziel_pruefen (Path)
    If  Result = "Write_Access.ok" Then
    	SmartTags("Archivierung\Status_kopieren") = 70          'Text: Zieldatei suchen
    Else  
        SmartTags("Archivierung\Status_kopieren") = 60          'Text: keine Schreibfreigabe
	End If
End If 

'70: Zieldatei prüfen
'---------------------

If SmartTags("Archivierung\Status_kopieren") = 70 Then
	
	Set f = CreateObject("FileCtl.File")
	Set fs = CreateObject("FileCtl.FileSystem")
	
	Path = "\\LAPTOP_WO\Logs\Archiv.csv"
	'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
	Ordnerpfad_anlegen Path ' Übergabe des Ablagepfades aus dem Script "Script_Storage_Path" / Handing over of the storage path from the script "Script_Storage_Path"
	
	' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
	f.open Path, 8 								

	' Tabellenkopf zusammenstellen 
	Header = "Zeitstempel;Barcode;Hoehe" &  Chr(10)
 
	' Wenn Datei 0 Byte groß, dann ist sie neu
	If fs.FileLen(Path) = 0 Then 					 
   		f.lineprint(Header)
	'=0 => Tabellenkopf muss eingefügt werden
 	End If 
 	' Datei wieder schließen
 	f.Close
 	
	Result = Ziel_pruefen (Path)
    If  Result = "Archiv.csv" Then
    	SmartTags("Archivierung\Status_kopieren") = 90            'Text: Quelldatei einlesen
    Else    
        SmartTags("Archivierung\Status_kopieren") = 80            'Text: Zieldatei nicht gefunden
	End If
End If 

'90: Datei suchen und auslesen
'-----------------------------

If SmartTags("Archivierung\Status_kopieren") = 90 Then
	
	Set fso = CreateObject("FileCtl.FileSystem")
	
	SmartTags("Archivierung\Name") = fso.Dir("\Storage Card USB\*.csv")        'Suchen nach erster csv-Datei auf Stick
	f.open("\Storage Card USB\" & SmartTags("Archivierung\Name")),1,1
	
	' Inhalt der gefundenen Datei Zeile für Zeile einlesen
	Do
		If f.EOF = True Then Exit Do
		fileContent = fileContent & f.LineInputString & vbCrLf
	Loop
	'Leerzeile am Ende abtrennen (sonst wird später mit jedem Kopiervorgang eine Leerzeile eingefügt)
	Laenge = Len (fileContent)
	fileContent = Left(fileContent,(Laenge-2))
	' Datei wieder schließen
 	f.Close
 	SmartTags("Archivierung\Status_kopieren") = 100				'Text: Quelldaten übertragen
End If 


'100: Einträge im Appendmodus anhängen
'-------------------------------------

If SmartTags("Archivierung\Status_kopieren") = 100 Then
	
	' FileObject erstellen
	Set f = CreateObject("FileCtl.File")
 
	' Datei öffnen im Append-Modus
	f.open "\\LAPTOP_WO\Logs\Archiv.csv", 8
	' Einträge anhängen
	f.lineprint fileContent
	' Datei wieder schließen
	f.Close 

	SmartTags("Archivierung\Status_kopieren") = 110			'Text: Quelldatei löschen
End If
	
'110: Quelldatei löschen
'-----------------------

'Damit die übertragenen Daten nicht erneut kopiert werden, soll die Quelldatei gelöscht werden. 
If SmartTags("Archivierung\Status_kopieren") = 110 Then
	 fs.Kill ("\Storage Card USB\" & SmartTags("Archivierung\Name"))
	 SmartTags("Archivierung\Status_kopieren") = 200
End If

' Verwendeten Speicher wieder freigeben
Set f  = Nothing
Set fs = Nothing
Set fso = Nothing
Set PathSearch = Nothing
```

Weiter werden noch diese Skripte verwendet:

Sub-Routine Ordnerpfad anlegen:

```
'Sub Ordnerpfad_anlegen

'Hinweis/Note:
'Wenn der vorgegebene Ablagepfad im "Hauptscript" mehrere Unterordner enthält, 
'müssen diese Unterordner auf der Speicherkate komplett vorhanden sein. 
'Dieses Script dient dazu, dieses automatisch umzusetzen.

Dim fs, strDir, strTemp, arr

' FileSystemObject erstellen
Set fs = CreateObject("FileCtl.FileSystem") 

strTemp = "" ' Hilfsvariable

arr = Split (strPath, "\") ' Ablagepfad in einzelen "\" zerlegen

For Each strDir In arr ' Schleifenzähler 
	If Len(strDir) > 0 Then
		strTemp = strTemp + "\" + strDir
		
		strDir = fs.dir (strTemp)
		
		If strDir = "" Then ' Wenn kein "\" mehr vorhanden ist -> keine weiteren Unterpfade
			fs.mkDir strTemp
		End If	
	End If
Next 

' Verwendeten Speicher wieder freigeben
Set fs = Nothing
```

Funktion Ziel prüfen:

```
'Function Ziel_pruefen(Ziel)

' Die Funktion gibt den Namen des letzten Ordners bzw. der Datei zurück,
' wenn dieser/diese im übergebenen Zielpfad vorhanden ist.
Dim fso
  Set fso = CreateObject("FileCtl.FileSystem")
  Ziel_pruefen = fso.Dir(Ziel)
```


----------



## Tigerente1974 (15 Oktober 2013)

Jetzt muss ich den thread doch nochmal ausgraben...

Bei der Inbetriebnahme ist noch ein neues Problem ausgetaucht.
Der Barcode zu der Palette ist ein String mit 20 Zeichen. Auf der SPS-Seite wurde der String wie gewünscht eingetragen.
Bei der Archivierung des Strings scheint der String als Zahl interpretiert zu werden. Jedenfalls werden die beiden Führungsnullen des Barcodes weggestrichen. "Sehen" kann ich das beim Öfnnen der csv-Datei auf dem USB-Stick. Schlimmer ist aber, dass der Barcode quasi gerundet wird.
Wie kann ich dafür sorgen, dass mein String 1:1 in die csv-Datei eingetragen wird?


----------



## thomass5 (15 Oktober 2013)

Mit was öffnest du die .csv? Texteditor oder Excell? Bei letzterem hatte ich dies auch schon, obwohl es in der csv korrekt war.

Wähle einen Beruf, den du liebst,und du brauchst keinen Tag in deinem Leben mehr zu arbeiten. Konfuzius


----------



## PN/DP (15 Oktober 2013)

Tigerente1974 schrieb:


> Wie kann ich dafür sorgen, dass mein String 1:1 in die csv-Datei eingetragen wird?


Strings in doppelte Hochkomma " CHR(34) setzen:

```
' Write values of variables in archive file (VAR1)
 f.lineprint(CStr(Now) & ";" [COLOR="#FF0000"]& Chr(34)[/COLOR] & SmartTags("PAL_ID") [COLOR="#FF0000"]& Chr(34)[/COLOR] & ";" & CStr(SmartTags("Hoehe")))
```

Statt CStr(Now) würde ich den Zeitstempel unabhängig von den regionalen Einstellungen des Panels/Computers selber formatieren --> "31.12.2008 01:59:59"

```
' Write values of variables in archive file (VAR1)
 ts = Now

 sts = Chr(34)& Right("0" & DatePart("d", ts), 2) & "." & Right("0" & DatePart("m", ts), 2) & "." & DatePart("yyyy", ts) & " " _
              & Right("0" & DatePart("h", ts), 2) & ":" & Right("0" & DatePart("n", ts), 2) & ":" & Right("0" & DatePart("s", ts), 2) &Chr(34)

 f.lineprint(sts & ";" & Chr(34) & SmartTags("PAL_ID") & Chr(34) & ";" & CStr(SmartTags("Hoehe")))
```

Schau Dir auch mal diesen Thread an: Prozessdaten einer CPU 317 auf Speicherkarte eines TP277 6'' speichern

Harald


----------



## Tigerente1974 (16 Oktober 2013)

Die Datei habe ich mit Excel geöffnet. Werde das mit dem Texteditor auch mal probieren.

Danke für den Tip mit den Anführungszeichen.
Über Probleme mit dem Zeitstempel hatte ich schon mal was gelesen. Da ging es glaube ich um Probleme, wenn der Zeitstempel in den Namen der Datei eingetragen werden soll. Welchen Nachteil gibt es noch?
Werde aber auch den Tip mit dem Zeitstempel noch umsetzen.


----------



## PN/DP (16 Oktober 2013)

Hauptproblem bei der Datum/Uhrzeit-Formatierung ist, daß diese bei VBS abhängig von den regionalen Einstellungen im Control Panel (Systemsteuerung) des Erstellsystems ist. Hat das Zielsystem andere Einstellungen, dann gibt es Probleme beim Interpretieren des Zeitstempels.

Datum/Uhrzeit für Dateinamen: die "automatische" Formatierung enthält je nach Einstellung ":" und "/" und diese Zeichen sind in Dateinamen nicht erlaubt.

Es ist also immer besser, die Datumsformatierung (passend zum Zielsystem) selber vorzunehmen.

Ein weiterer Stolperstein ist die Einstellung des Dezimalpunkts bei Gleitkommazahlen - dazu gibt es einen FAQ von JesperMP.

Harald


----------



## Tigerente1974 (17 Oktober 2013)

Hallo Harald.

Danke für Deine Hilfe.
Ich habe beides umgesetzt.
Es funktioniert auch wie es soll. Allerdings ignoriert excel auch die "..." und macht eine gerundete Zahl daraus.
Mit dem Editor geöffnet sieht man, dass in beiden Fällen korrekt archiviert wurde. Einmal mit den Gänsefüßchen, einmal ohne.
Ich lasse die Gänsefüßchen jetzt nochmal weg und bespreche mit dem IT-ler, ob er die Daten korrekt auslesen kann.


----------



## thomass5 (17 Oktober 2013)

Wenn du in Excel die Datei nicht öffnest sondern importierst kannst du glaube ich auch das Format angeben den der Wert darstellt. So solltest du das ev. Hingekommen. ...

Gesendet von meinem GT-N7100 mit Tapatalk 2


----------



## Tigerente1974 (19 Oktober 2013)

Der IT-Mensch kommt mit den Daten zurecht. Ich lass das jetzt so.
Danke an alle für die Hilfen...


----------



## Tigerente1974 (17 Juni 2017)

Hallo Forum.

Ich grabe diesen thread nun noch einmal aus, weil es jetzt ein neues Problem gibt, für das ich noch keine Lösung gefunden habe.
Ich versuche das erstmal so gut wie möglich zu beschreiben. Vor gut einem Monat habe ich die Anlage erweitert. Es gibt nun eine 2. Stelle an der Daten archiviert werden sollen.
Die Skripte sind dazu nicht verändert worden. Es ist alles noch so wie vorher.
Um zu verhindern, dass ein Skript angestoßen wird, während die Skriptbearbeitung läuft habe ich mir ein Sperrbit gesetzt.
Grundsätzlich liegt zwischen den Archivierungen aber ein Zeitraum von mindestens 30s, bedingt durch den Ablauf an der Anlage.
Trotzdem hier die Frage: Angenommen der Code funktioniert nicht wie von mir gedacht, kann so etwas zu dem beschriebenen Verhalten führen?

Das hat bisher fehlerfrei geklappt.
Nun zu meinem Problem:
Ich kann nicht sicher sagen, ob der Auslöser dafür auf der Seite des Kundenservers oder bei mir liegt.
Angefangen hat es damit, dass der Kunde sich gemeldet hat, weil das HMI aus der Bedienoberfläche in die WinCE Oberfläche gesprungen ist.
Dort wurde das Fenster "Transfer" eingeblendet. In dem Fenster stand "Connecting to host". Nach einem Neustart (24V getrennt) fuhr das HMI wieder hoch, sprang aber nach kurzer Zeit von selbst wieder zu der gleichen Anzeige.
Ich habe das HMI per Fernwartung neu übertragen. Das Ergebnis blieb gleich.
Weiter erhielt ich die Info, dass die Archivierung auf dem Kundenserver seit über einem Tag nicht mehr funktioniert hat.

Da der Kunde ein Ersatzgerät zur Verfügung hatte, haben wir das ausgetauscht.
Zunächst hat die Anmeldung am Server nicht geklappt, weil das Passwort für die Anmeldung mal geändert wurde und das nicht gut dokumentiert wurde.
Das HMI hat dann jedes mal den Anmeldedialog zur Passworteingabe gebracht, wenn archiviert werden sollte.
So hat das dann etwa 2 Stunden ohne Archivierung gelaufen, bis das HMI genau wie das erste von allein in die WinCE-Oberfläche gesprungen ist und auf Transfer gewartet hat.
Wir haben dann den Netzwerkstecker zum Kundenserver gezogen. So läuft das wieder fehlerfrei, aber ohne Archivierung auf dem Kundenserver.

Der IT-Mitarbeiter des Kunden hat nun weiter nach der Ursache geforscht. Mittlerweile klappt die Anmeldung mit dem richtigen Passwort wieder.
Der Ablauf im sub-Skript "Datei kopieren" läuft bis Schritt 100. Dort wird die csv-Datei auf dem Kundenserver geöffnet.
Dann steigt das HMI aus. Was der Kunde mit dieser Beschreibung meint, muss ich noch einmal nachfragen.

Info des Mitarbeiters:
- Ich kann vom Panel aus in diesem Ordner Dateien anlegen, umbenennen und löschen (Schreibzugriff vorhanden).
- Ich kann vom Panel aus nicht testen, ob ich auch in den Dateien selbst Änderungen machen kann, da ich sie nicht öffnen kann (Fehlermeldung: Keine geeignete Applikation zum öffnen der Datei gefunden)
- Ich habe mich an meinem Rechner angemeldet und konnte die Dateien auf dem Serverordner öffnen, ändern, speichern (mit Editor geöffnet)

Hat jemand eine Idee?


----------



## Tigerente1974 (17 Juni 2017)

Hier nochmal der Code:


```
Sub Datei_kopieren()
'Sub Datei_kopieren
Dim PathSearch, Path, Index, Result, fso, SourceFile, DestFile, f, fs, Header, StartTime, StopTime, DelayTime, i, Laenge, fileContent
Set PathSearch = CreateObject("FileCtl.FileSystem")
'USB-Stick suchen
'--------------------
Path = "Storage Card USB" 
    
SmartTags("Archivierung\Status_kopieren") = 10                'Text: Suche USB-Stick
    
If  PathSearch.Dir("\" & Path) = "" Then
 SmartTags("Archivierung\Status_kopieren") = 20            'Text: USB-Stick nicht gefunden!
    ElseIf PathSearch.Dir("\" & Path) = Path Then     
    SmartTags("Archivierung\Status_kopieren") = 30            'Text: Suche Zielordner
End If
'30: Ziellaufwerk suchen
'-----------------------
If SmartTags("Archivierung\Status_kopieren") = 30 Then
 Path = "\\DETET1AS0007\transfer$"
 'Path = "\\TGVM239\transfer$"
 'Path = "\\LAPTOP_CHRIS_WO\Logs"
 
 StartTime = Now
 DelayTime = 500  'Timer 100 endspricht 1sec.
 StopTime = StartTime + DelayTime / 24 / 360000
 Do                        
     Result = Ziel_pruefen (Path)
     If  Result = "transfer$" Then
     'If Result = "Logs" Then
      SmartTags("Archivierung\Status_kopieren") = 50      'Text: Schreibfreigabe prüfen
      Exit Do
     ElseIf Now >= StopTime Then    
         SmartTags("Archivierung\Status_kopieren") = 40      'Text: Zielordner nicht gefunden
  End If
 Loop Until Now >= StopTime
End If 

'50: Schreibfreigabe prüfen
'--------------------------
' Die Datei "NVE.ok" muss im Zielordner vorhanden sein.
If SmartTags("Archivierung\Status_kopieren") = 50 Then
 Path = "\\DETET1AS0007\transfer$\NVE.ok"
 'Path = "\\TGVM239\transfer$\NVE.ok"  
 'Path = "\\LAPTOP_CHRIS_WO\Logs\NVE.ok"                  
    Result = Ziel_pruefen (Path)
    If  Result = "NVE.ok" Then
     SmartTags("Archivierung\Status_kopieren") = 70          'Text: Zieldatei suchen
    Else  
        SmartTags("Archivierung\Status_kopieren") = 60          'Text: keine Schreibfreigabe
 End If
End If 
'70: Zieldatei prüfen
'---------------------
If SmartTags("Archivierung\Status_kopieren") = 70 Then
 
 Set f = CreateObject("FileCtl.File")
 Set fs = CreateObject("FileCtl.FileSystem")
 
 Path = "\\DETET1AS0007\transfer$\NVE.csv"
 'Path = "\\TGVM239\transfer$\NVE.csv"
 'Path = "\\LAPTOP_CHRIS_WO\Logs\NVE.csv"
 'Prüfen ob Ablagepfad vorhanden, wenn nicht -> erzeugen.
 Ordnerpfad_anlegen Path ' Übergabe des Ablagepfades aus dem Script "Script_Storage_Path" / Handing over of the storage path from the script "Script_Storage_Path"
 
 ' Datei öffnen bzw. erstellen, wenn sie noch nicht existiert
 f.open Path, 8         
 ' Tabellenkopf zusammenstellen (Anmerkung: Kunde wünscht keinen Header -> ausgeklammert)
 'Header = "Zeitstempel;Barcode;Hoehe" &  Chr(10)
 
 ' Wenn Datei 0 Byte groß, dann ist sie neu
 'If fs.FileLen(Path) = 0 Then       
    ' f.lineprint(Header)
 '=0 => Tabellenkopf muss eingefügt werden
  'End If 
  ' Datei wieder schließen
  f.Close
  
 Result = Ziel_pruefen (Path)
    If  Result = "NVE.csv" Then
     SmartTags("Archivierung\Status_kopieren") = 90            'Text: Quelldatei einlesen
    Else    
        SmartTags("Archivierung\Status_kopieren") = 80            'Text: Zieldatei nicht gefunden
 End If
End If 
'90: Datei suchen und auslesen
'-----------------------------
If SmartTags("Archivierung\Status_kopieren") = 90 Then
 
 Set fso = CreateObject("FileCtl.FileSystem")
 
 SmartTags("Archivierung\Name") = "PAL_ID.csv" ' fso.Dir("\Storage Card USB\*.csv")        'Suchen nach ersten Dateiname auf Stick
 f.open("\Storage Card USB\" & SmartTags("Archivierung\Name")),1,1
 Do
  If f.EOF = True Then Exit Do
  fileContent = fileContent & f.LineInputString & vbCrLf
 Loop
 'Leerzeile am Ende löschen
 Laenge = Len (fileContent)
 fileContent = Left(fileContent,(Laenge-2))
 ' Datei wieder schließen
  f.Close
  SmartTags("Archivierung\Status_kopieren") = 100    'Text: Quelldaten übertragen
End If 

'100: Einträge im Appendmodus anhängen
'-------------------------------------
If SmartTags("Archivierung\Status_kopieren") = 100 Then
 
 ' FileObject erstellen
 Set f = CreateObject("FileCtl.File")
 
 ' Datei öffnen im Append-Modus
 f.open "\\DETET1AS0007\transfer$\NVE.csv", 8 
 'f.open "\\TGVM239\transfer$\NVE.csv", 8 
 'f.open "\\LAPTOP_CHRIS_WO\Logs\NVE.csv", 8
 ' Einträge anhängen
 f.lineprint fileContent
 ' Datei wieder schließen
 f.Close 
 SmartTags("Archivierung\Status_kopieren") = 110   'Text: Quelldatei löschen
End If
 
'110: Quelldatei löschen
'-----------------------
If SmartTags("Archivierung\Status_kopieren") = 110 Then
  fs.Kill ("\Storage Card USB\" & SmartTags("Archivierung\Name"))
  SmartTags("Archivierung\Status_kopieren") = 200
End If
' Verwendeten Speicher wieder freigeben
Set f  = Nothing
Set fs = Nothing
Set fso = Nothing
Set PathSearch = Nothing
End Sub
```


----------



## PN/DP (17 Juni 2017)

Der Transfer-Dialog erscheint? - Die WinCC Runtime ist abgestürzt und meint offenbar, es sei keine gültige Runtime-Projektierung vorhanden. (Es scheint, als ob die Datei PDATA.FWX beschädigt ist oder ein anderes Problem mit dem Filesystem besteht.)

Schritt 100:
Du prüfst nicht, ob das Öffnen der Server-Datei für Append erfolgreich war.
Du prüfst nicht, ob das Anhängen/Schreiben an die Server-Datei erfolgreich war.
Du prüfst nicht, ob da Runtime-Errors aufgetreten sind.
Ich vermute, daß das Skript da durch einen Runtime-Error unsanft abgebrochen wird. Hast Du eine Meldeanzeige für Meldeereignisse in der Runtime drin, welche auch Meldungen der Meldeklasse "System" anzeigt? Darin sollte man Meldungen bezüglich Deines Skript-Problems finden.

Du solltest in dem Skript mit "On Error Resume Next" das Abbrechen des Skriptes verhindern und direkt nach den File-Operationen den Runtime-Error-Status auswerten:

```
Dim fs, f, ...
Const ForAppending = 8
On Error Resume Next
...

' FileSystemObject erstellen
Set fs = CreateObject("FileCtl.FileSystem")
If Err.Number <> 0 Then
  ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
  Err.Clear
  Exit Sub
End If
' FileObject erstellen
Set f = CreateObject("FileCtl.File")
If Err.Number <> 0 Then
  ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
  Err.Clear
  Exit Sub
End If
...

'100: Einträge im Appendmodus anhängen
'-------------------------------------
If SmartTags("Archivierung\Status_kopieren") = 100 Then

  ' Datei öffnen im Append-Modus
  f.open "\\DETET1AS0007\transfer$\NVE.csv", ForAppending
  If Err.Number <> 0 Then
    ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
    Err.Clear
    Exit Sub
  End If

  ' Einträge anhängen
  f.lineprint fileContent
  If Err.Number <> 0 Then
    ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
    Err.Clear
    f.Close
    Exit Sub
  End If

  ' Datei wieder schließen
  f.Close
  SmartTags("Archivierung\Status_kopieren") = 110   'Text: Quelldatei löschen
End If
```

Für mein Gefühl erstellst Du unnötigerweise zu viele File-/Filesystem-Objekte. Im Schritt 100 benutzt Du eine bereits benutzte Objektverweis-Variable (f) für die Erstellung eines neuen File-Objektes ohne das vorhandene File-Objekt freizugeben - dadurch geht der Verweis zum vorherigen Objekt verloren. Möglicherweise geht durch die zu vielen Objekte und die Runtime-Errors der Arbeitsspeicher zur Neige und die WinCC Runtime stürzt deshalb ab?
Ich würde vorschlagen, Du erstellst am Anfang des Skriptes je ein File-Objekt und FileSystem-Objekt und benutzt dann nur noch diese beiden Objekte.


Der Schritt 90 ist nicht gut gelöst - wozu eine komplette Datei in den Arbeitsspeicher einlesen?
Wie groß ist die Datei "\Storage Card USB\PAL_ID.csv"? - eventuell zu groß oder leer?
Kann es vorkommen, daß die Datei leer ist? Ich weiß nicht, was VBS macht, wenn man "fileContent = Left(fileContent,(Laenge-2))" mit einer resultierenden negativen Länge aufruft.
Wozu ungeprüft generell die letzten 2 Zeichen aus der Datei entfernen? Wo kommt die leere Zeile her?

Ich würde anstatt Schritt 90 im Schritt 100 die Datei vom Panel zeilenweise in die Datei auf dem Server kopieren (beide Dateien öffnen, in einer Schleife bis EOF von Quelldatei eine Zeile einlesen, wenn Länge > 0 dann die Zeile in die Zieldatei schreiben).


Ob das Panel alle erforderlichen Rechte auf dem Server hat kannst Du mit dem "Command Prompt" (Pocket CMD) des Panels ausprobieren, da bekommst Du eventuelle Fehlermeldungen auch gleich angezeigt. Etwa so:

```
Pocket CMD
\> [COLOR="#0000FF"]echo blablabla > \temp\test.txt[/COLOR]
\> [COLOR="#0000FF"]copy \temp\test.txt \\DETET1AS0007\transfer$\test.csv[/COLOR]
\> [COLOR="#0000FF"]echo append123 >> \\DETET1AS0007\transfer$\test.csv[/COLOR]
\> [COLOR="#0000FF"]type \\DETET1AS0007\transfer$\test.csv[/COLOR]
blablabla
append123
\>
```
Danach sollte auf dem Server die Datei test.csv den Inhalt 


> blablabla
> append123


haben.

Harald


----------



## PN/DP (18 Juni 2017)

PN/DP schrieb:


> Du solltest in dem Skript mit "On Error Resume Next" das Abbrechen des Skriptes verhindern und direkt nach den File-Operationen den Runtime-Error-Status auswerten:
> 
> ```
> ...
> ...


Der Aufwand macht natürlich nur Sinn, wenn Du in dem Skript auf den Fehler reagieren kannst/willst (z.B. einen Vorgang erneut versuchen oder etwas anderes versuchen). Wenn da nur eine Fehlermeldung ausgegeben und das Skript beendet werden soll, dann kannst Du das "On Error Resume Next" weglassen - das macht VBS sowieso automatisch so.

Harald


----------



## Tigerente1974 (19 Juni 2017)

Hallo Harald,

erst einmal Danke für Deine Antworten.

1. Ich habe das Projekt neu aufgespielt. Damit sollte die PDATA.FWX "frisch" drauf sein.
2. Wenn ich die Fehlerauswertung reinschreibe würde das HMI nicht abschmieren, das wäre ja schon mal ein Schritt weiter.
3. Ich zeige Systemfehler in einem Meldefenster an, dass in der globalen Vorlage liegt.
    Weiter gibt es eine Seite zur Anzeige des Meldearchivs. Dort werden auch die Systemfehler mitgeschrieben.
    Ich hatte die Vermutung, einen Fehler würde man ggf. nicht sehen, wenn das HMI abstürzt.
    Aber auch im Archiv findet sich kein Eintrag.
4. Das skript zum Kopieren habe ich weitestgehend von einem Siemens-Beispiel übernommen. Entweder ist das durch meine Änderung etwas unkonventionell geworden oder die Vorlage war auch schon so.
    Aber nicht zu vergessen hat das schon lange so geklappt. Eine leere Datei ist definitiv kein Problem. Es gibt weitere subskripts, die u.a. die Datei neu anlegen, wenn sie im nicht im Zielverzeichnis liegt.
    Ich habe den IT-Kollegen gebeten die Datei zu löschen. Sie wird auch wieder neu angelegt. Diese Prozedur ist zumindest bei der Inbetriebnahme vor 4 Jahren auch mehrmals durchgespielt worden.
    Das Problem ist damit aber nicht behoben.
5. "fileContent = Left(fileContent,(Laenge-2))" soll eine Leerzeile löschen. Siehe Beitrag #19 in diesem thread

Letztendlich werde ich jetzt zunächst folgendes machen:
Bei der Inbetriebnahme hatte ich damals auf meinem Arbeitsrechner ein Verzeichnis zum Probieren angelegt und dorthin archiviert.
Der IT-Kollege wird das jetzt so nachstellen und sich direkt an der Maschine verbinden. So ist das Firmennetzwerk mit Routern etc. sowie die Freigabe im Netz erst einmal ausgeschlossen.

Ich werde weiter berichten.


----------



## volker (20 Juni 2017)

Noch mal was, was aber nicht direkt mit deinem Problem zu tun hat.
Ich halte es Grundsätzlich für problematisch direkt in Netzwerkdateien zu schreiben.
Was das für Probleme macht siehst du ja gerade. Datenverlust.

Ich halte es so, dass ich immer auf eine Storage-Card archiviere und anschließend die Dateien kopiere/verschiebe.


----------



## Tigerente1974 (21 Juni 2017)

Hallo Volker,

danke für den Hinweis.

Die Archivierung wird auch so gemacht. Erst auf einen USB-Stick schreiben und dann auf den server kopieren.
Die Daten sind auch nicht weg, aber halt auch nicht für den Kunden verfügbar, solange sie auf dem stick liegen.

Gruß

Christian


----------



## Tigerente1974 (21 Juni 2017)

Hallo Harald,

der Fehler ist jetzt aufgedeckt. Du hattest den richtigen Riecher, vielen Dank noch einmal.



PN/DP schrieb:


> Für mein Gefühl erstellst Du unnötigerweise zu viele File-/Filesystem-Objekte. Im Schritt 100 benutzt Du eine bereits benutzte Objektverweis-Variable (f) für die Erstellung eines neuen File-Objektes ohne das vorhandene File-Objekt freizugeben - dadurch geht der Verweis zum vorherigen Objekt verloren. Möglicherweise geht durch die zu vielen Objekte und die Runtime-Errors der Arbeitsspeicher zur Neige und die WinCC Runtime stürzt deshalb ab?
> Ich würde vorschlagen, Du erstellst am Anfang des Skriptes je ein File-Objekt und FileSystem-Objekt und benutzt dann nur noch diese beiden Objekte.
> 
> 
> ...



Der server war mehrere Tage nicht erreichbar und es hatten sich über 5000 Datensätze auf dem stick gesammelt.
Ich hatte den code erstmal so verändert:


```
'90: Datei suchen und öffnen
'-----------------------------

If SmartTags("Archivierung\Status_kopieren") = 90 Then
    
    Set fso = CreateObject("FileCtl.FileSystem")
    
    SmartTags("Archivierung\Name") = "PAL_ID.csv" ' fso.Dir("\Storage Card USB\*.csv")        'Suchen nach ersten Dateiname auf Stick
    f.open("\Storage Card USB\" & SmartTags("Archivierung\Name")),1,1
    If Err.Number <> 0 Then
        ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
        Err.Clear
        Exit Sub
    Else
        SmartTags("Archivierung\Status_kopieren") = 91
    End If
End If

'91: Datei auslesen
'-----------------------------    
If SmartTags("Archivierung\Status_kopieren") = 91 Then
    Do
        If f.EOF = True Then Exit Do
        fileContent = fileContent & f.LineInputString & vbCrLf
    Loop
    
    'Leerzeile am Ende löschen
    Laenge = Len (fileContent)
    If Laenge > 2 Then
        fileContent = Left(fileContent,(Laenge-2))
        SmartTags("Archivierung\Status_kopieren") = 100                'Text: Quelldaten übertragen
    Else
        SmartTags("Archivierung\Status_kopieren") = 99            'Text: Fehler Datenlänge
    End If
    
    ' Datei wieder schließen
     f.Close     
End If 


'100: Zieldatei öffnen
'-------------------------------------

If SmartTags("Archivierung\Status_kopieren") = 100 Then
    
    ' FileObject erstellen
    Set f = CreateObject("FileCtl.File")
 
    ' Datei öffnen im Append-Modus
    f.open "\\DETET1AS0007\transfer$\NVE.csv", 8 
    'f.open  "\\DETET1CN1380\Logs\NVE.csv", 8 
    'f.open "\\LAPTOP_CHRIS_WO\Logs\NVE.csv", 8
    If Err.Number <> 0 Then
        ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
        Err.Clear
        Exit Sub
    Else
        SmartTags("Archivierung\Status_kopieren") = 101
    End If
End If

'101: Einträge im Appendmodus anhängen
'-------------------------------------
If SmartTags("Archivierung\Status_kopieren") = 101 Then

    ' Einträge anhängen
    f.lineprint fileContent
    If Err.Number <> 0 Then
        ShowSystemAlarm "Error #" & Err.Number & " " & Err.Description
        Err.Clear
        f.Close
        Exit Sub
    Else
        ' Datei wieder schließen
        f.Close 
        SmartTags("Archivierung\Status_kopieren") = 110            'Text: Quelldatei löschen
      End If    
End If
```

Das skript war dann recht lang im Schritt 91 und in Schritt 101 ist das Panel wieder abgestürzt.
Nachdem die Datei auf dem stick dann umbenannt war, wurde eine neue (leere) Datei angelegt.
Das hat dann wieder funktioniert. Also war der Arbeitsspeicher ausgeschöpft.

Ich werde Deine Vorschläge umsetzen, sobald ich das nächste Mal bei dem Kunden bin.
Hast Du einen Codeschnipsel für mich, um das zeilenweise kopieren durchzuführen wie Du es vorgeschlagen hast?

Gruß

Christian


----------

