TIA TIA WinCC VBS-Script - Ausführungsreihenfolge Systemfunktionen einhalten

Botimperator

Level-2
Beiträge
466
Reaktionspunkte
194
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

ich hab hier ein Kopfschmerz-Problem mit VBS & das allwissende G hat mir nicht wirklich eine Lösung geliefert....
TIA V16, TP1200 Comfort

Aus verschiedenen Gründen kann ich bei einem Projekt die Standard-Rezepturanzeige vom Panel nicht nutzen.
Also war meine Idee über EA-Felder die Rezepturvariablen zu Manipulieren & dann den Rezepturdatensatz mit
"SetDataRecordToPLC" bzw. "GetDataRecordFromPLC" auszutauschen.

Variablensynchronisation ist entsprechend gesetzt, damit ich die SPS-Variablen erst beim Drücken der "SetDataRecordToPLC"-Taste
konsistent auf die SPS geschrieben wird.
1683534657717.png

Ich hab mir dazu ein kleines Testbild geschieben um die Funktionen prinzipiell zu testen.
An die türkiesen EA-Felder sind die Rezepturvariablen angebunden, die gelben auf der rechten Seite
enthalten die aktuellen Werte des zugehörigen DBs auf der SPS.
1683535201456.png

Zum Laden der Werte von der SPS in den Datensatz muss ich also:
- laden der Werte von der SPS per "GetDataRecordFromPLC" in den Rezepturdatensatz laden
- warten bis die Daten fertig geladen sind
- Rezepturdatensatz in die Rezepturvariablen laden (ansonsten werden die aktualisierten Werte erst korrekt angezeigt wenn der Datensatz über das Dropdown-Menü neu ausgewählt wird.

Hab mir dazu mal folgenden Schnellschuss gestrickt:
'Daten von SPS-DB in Rezepturdatensatz übertragen
GetDataRecordFromPLC SmartTags("Rezeptnummer"), SmartTags("Datensatznummer"), hmiOverwriteWithConfirmation, hmiOn, "hRezeptReadState"
'Eben geladenen Rezepturdatensatz in Rezepturvariablen laden, sobald GetData fertig ist
Do
Loop Until SmartTags("hRezeptReadState") <> 2
LoadDataRecord SmartTags("Rezeptnummer"), SmartTags("Datensatznummer"), Null

Das funktioniert auch prinzipiell, also "Daten holen" => "Warten bis fertig" => "Rezepturvariablen aktualisieren"

Beim Schreiben der Werte muss ein ähnlicher Ablauf eingehalten werden:
- gegebenenfalls geänderte Werte in den Rezepturvariablen in den Rezepturdatensatz speichern (also wenn jemand das EA-Feld geändert hat ohne zwischenzeitlich auf "Speichern" zu drücken)
- warten bis fertig gespeichert / "Datensatz wirklich überschreiben"-Popup bestätigt
- Rezepturdatensatz per "SetDataRecordToPLC" auf die SPS schreiben

Dazu folgendes Script:
Dim hExecutionState

hExecutionState = 0

'gegebenenfalls geänderte Werte des Datensatzes müssen zuerst von den Rezepturvariablen in den Rezepturdatensatz gesichert werden
SaveDataRecord SmartTags("Rezeptnummer"), SmartTags("Datensatznummer"), hmiOverwriteWithConfirmation, hmiOn, hExecutionState
'Warten bis Datensatz gesichert & dann schreiben des Rezepturdatensatzes in Steuerung
Do
Loop Until hExecutionState <> 2

SetDataRecordToPLC SmartTags("Rezeptnummer"), SmartTags("Datensatznummer"), hmiOn, "hRezeptWriteState"

Dieses Script funktioniert nicht wie gewünscht.
Der Ablauf übergeht die Loop-Bedingung & löst "SetDataRecordToPLC" sofort aus, bevor der Datensatz mit geänderten Werten der Rezepturvariablen aktualisiert werden kann.
Einziger unterschied (soweit ich das bisher verstehe):
Das erste Script verwendet für die "Loop" Bedingung eine HMI-Variable, das zweite eine interne Scriptvariable.


Frage:
- Wieso macht es hier einen Unterschied ob ich eine HMI- oder Scriptvariable verwende?
- Gibt es eine bessere Möglichkeit auf das beenden einer Systemfunktion zu warten?

mfg Tobias
 
Hallo Tobias,

zu deiner ersten Funktion:
Es gibt eine Systemfunktion "GetDataRecordTagsFromPLC", damit kannst du dir das "LoadDataRecord" sparen, da die Werte direkt in die Variablen geladen werden.

Grundsätzlich:
Bei den Rückgabewerten von Systemfunktionen sind oftmals nur HMI-Variablen erlaubt (Warum aúch immer).
Siehe dazu auch die TIA-Hilfe:
SpeichereDatensatz (Basic Panels, Panels, Comfort Panels, RT Advanced)
Bearbeitungsstatus
Gibt den Bearbeitungsstatus der Systemfunktion zurück. Verwenden Sie den Rückgabewert, um z. B. andere Systemfunktionen erst dann auszuführen, wenn diese Systemfunktion erfolgreich beendet wurde:
2 = Systemfunktion wird gerade ausgeführt.
4 = Systemfunktion wurde erfolgreich beendet.
12 = Systemfunktion wurde nicht ausgeführt, weil ein Fehler aufgetreten ist.
Für den Parameter können Sie nur eine HMI-Variable verwenden.
Eigentlich wundert es mich fast (Aber nur fast, Siemens halt ...), dass beim Übersetzen kein Fehler oder zumindest eine Warnung auftritt.


Gruß, Fred
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo faust,

danke für die schnelle Antwort :]
Das mit den HMI-Variablen läuft dann wohl unter "Hilft manchmal die Hilfe auch bis zum Ende zu lesen" :poop:
Zumindest mal gut zu wissen, dass das mit Scriptvariablen nicht funktioniert, aber auch keinen Fehler / keine Warnung schmeißt...

In meinem Fall funktioniert das aber mit "GetDataRecordTagsFromPLC" nicht direkt.
Ich müsste erst mit "GetDataRecordTagsFromPLC" die Werte aus dem SPS-DB in die Rezepturvariablen laden & dann mit "SaveDataRecord" die Werte der Rezepturvariablen in den Rezepturdatensatz der Rezeptverwaltung speichern.
Lässt man das "SaveDataRecord" weg, werden die geänderten Werte nicht im Datensatz gespeichert & wenn man den Rezeptdatensatz wechselt fragt die Runtime nicht ob man den geänderten Datensatz speichern möchte (da nur die Rezepturvariablen geändert wurden, nicht der Datensatz).

In dem Script aus dem Eröffnungspost gehe ich her & speichere die Werte per "GetDataRecordFromPLC" aus dem SPS-DB zuerst in den Rezepturdatensatz & lade dann den aktualisierten Datensatz per "LoadDataRecord" aus der Rezeptur in die Rezepturvariablen, damit dieser korrekt angezeigt wird.

Denke das ist Geschmackssache wie rum man die Daten dahin bringt wo sie hin sollen.
Oder hat das irgendeinen Performance-Vorteil?

mfg Tobias
 
Hallo Tobias.

Ist eine Frage der Reihenfolge:
Entweder erst den gewünschten Datensatz laden und dann die Werte aktualisieren
oder den Datensatz direkt aktualisieren und dann für die Anzeige laden.

Wie stellst du in deinem Fall sicher, dass der Bediener die richtige Datensatznummer einstellt, bevor die Aktualisierung angestoßen wird?
Weitere Frage/Anmerkung: Bei meinen HMIs werden Rezepturvariablenwerte niemals in der Steuerung geändert. Wofür benötigst du also den Aktualisierungsweg "Zum Panel"?

Gruß, Fred
 
Bei den Rückgabewerten von Systemfunktionen sind oftmals nur HMI-Variablen erlaubt (Warum aúch immer).
Das hat damit zu tun, daß die Datensatz-Systemfunktionen nicht warten, bis die Funktion beendet ist, sondern nur die asynchron arbeitende Systemfunktion anstossen und sofort ins Skript zurückkehren. Wenn nun der Programmierer absichtlich oder z.B. aus Dusseligkeit nicht im Skript auf das Ende der angestossenen Funktion wartet und das Skrift vor deren Ende beendet (oder es durch einen Runtime Error unsanft beendet wird), dann werden alle Pointer auf Skript-interne lokale Variablen ungültig, weil es die Skript-Variablen dann nicht mehr gibt. Nur Pointer auf globale HMI-Variablen (SmartTags) überleben das Ende des Skriptes. Deshalb schreibt Siemens halt vorsichtshalber für die Dummies, daß nur HMI-Variablen erlaubt sind... Ich glaube, in WinCC flexible haben die Systemfunktionen auch noch mit Skript-internen Variablen funktioniert, bin aber nicht sicher. In TIA habe ich noch nicht ausprobiert, ob es auch mit den nicht angemeckerten Skriptvariablen funktioniert, wenn man auf das Ende der Funktionen wartet.

Harald
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin,

...
Wie stellst du in deinem Fall sicher, dass der Bediener die richtige Datensatznummer einstellt, bevor die Aktualisierung angestoßen wird?
Weitere Frage/Anmerkung: Bei meinen HMIs werden Rezepturvariablenwerte niemals in der Steuerung geändert. Wofür benötigst du also den Aktualisierungsweg "Zum Panel"?

Bei der betreffenden Anlage werden viele Rezepte praktisch identisch / nur mit kleineren Änderungen vorkommen.
Mit dem Zurückladen soll das Anlagen neuer Rezepte vereinfacht werden, da der bediener schonmal alle Parameter laden kann & dann nur noch seine Anpassungen einpflegen muss.
Ich hätte dafür zwar die "Speichern unter" Funktion benutzt, aber.....Kunde will, Kunde bekommt ¯\_(ツ)_/¯

Nur Pointer auf globale HMI-Variablen (SmartTags) überleben das Ende des Skriptes. Deshalb schreibt Siemens halt vorsichtshalber für die Dummies, daß nur HMI-Variablen erlaubt sind...

Der Sinn/Hintergrund erschließt sich mir jetzt auch, hätte trozdem eine Warnung beim Übersetzen erwartet.

Hast du irgendwelche Tipps wie man solche Wartekonstrukte innerhalb von Bibliotheksscripten am sinnvollsten handhabt?
Die "loop" Funktion muss ja per SmartTags() an der Schnittstelle vorbei irgendwo in den globalen Speicher greifen.
Ich meine mich dunkel zu erinnern, dass in den Comfort-Panels nur ein Script gleichzeitig laufen kann.
Funktioniert das wenn ich in allen Bibliotheksscripten die eine Wartefunktion beinhalten auf eine einzelne, interne HMI-Variable beziehe, quasi einen "dummyINT", um den Wert für den Bearbeitungsstatus zwischenzuspeichern?
Oder macht es mehr Sinn mir für jedes Script einen eigenen "dummyINT" in meiner Panel-Kopiervorlage anzulegen?
 
Hallo Tobias.

Ich verstehe deinen bzw. des Kunden Ansatz des Rezepthandlings zwar nicht so ganz, aber das soll deine Sache sein und bleiben ;)

Du erinnerst dich richtig, es wird immer nur eine Script gleichzeitig ausgeführt.

Ich habe mir für Systemfunktions-Statuswerte EINE globale Variable "fctStatus_wcc" (INT) angelegt; dies reicht aus, da ja entsprechende Systemfunktionen strenggenommen nicht parallel ausgeführt werden (sollen).

Bei meinen HMIs gehe ich noch einen Schritt weiter und habe mir Hilfsscripte angelegt, die mir eine Status- bzw. Fehlerbehandlung nach dem "Try/Catch-Prinzip" (bekannt z.B. von Java) ermöglichen, d.h. ich werte die o.g. Variable sowohl innerhalb eines Scriptes als auch quasi außerhalb aus. Sieht dann z.B. so aus:
C++:
' Transfer
Try(Try_FIRST)
    If Not(SmartTags("hmiSimulationIsActive")) _
    Then
        SetDataRecordTagsToPLC wccRecipeName, "fctStatus_wcc" : While SmartTags("fctStatus_wcc") = FId_RUNNING : Wend
    End If
' Transfer- und Änderungs-Flags behandeln
If Try(Try_NEXT) _
Then
    SmartTags("recipe_recipeNeedsTransfer") = False
    SmartTags(recipeTagBaseName & "_recipeNeedsTransfer") = False
    SmartTags("hmiActionFlags_machine_" & wccRecipeName & "WasModified") = True
End If
If Catch(EId_ALL) _
Then
    fctStatus = 11
    If showStatus _
    Then
        OMSG_showMessage 30211, OMSGType_ERROR, OMSGDelay_DONT_WAIT
    Else
        ShowSystemAlarm ("InternalActionFailed: Recipe_Param_Transfer")
    End If
End If
C++:
' ##################################################
' Variablenreferenzen holen, Funktion initialisieren
' ##################################################
@param 'tryStep' -> Schrittnummer des Blocks, unterschieden wird TRY_FIRST und TRY_NEXT

Dim wccFctItems, wccEId, wccFctStatus
Dim result
  
result = False




' ##############
' Funktionslogik
' ##############
  
' Fehlernummervariable(n) löschen
If tryStep = Try_FIRST _
Then
    SmartTags("fctStatus_global") = EId_NO_ERROR
    SmartTags("fctStatus_wcc") = FId_IDLE
    SmartTags("fctStatus_eId") = EId_NO_ERROR
    SmartTags("fctStatus_wId") = WId_NO_WARNING
End If

' WinCC-Exception dekodieren
wccFctItems = Split(Replace(CStr(SmartTags("fctStatus_wcc") / 100), ".", ","), ",")
If UBound(wccFctItems) = 1 _
Then
    wccEId = CInt(wccFctItems(0))
    wccFctStatus = CInt(wccFctItems(1))
Else
    wccEId = EId_NO_ERROR
    wccFctStatus = CInt(wccFctItems(0))
End If
 

' Auswertung
If (SmartTags("fctStatus_global") = EId_NO_ERROR Or SmartTags("fctStatus_global") >= WId_NO_WARNING) _
        And (wccFctStatus = FId_IDLE Or wccFctStatus = FId_EXECUTED_WO_ERRORS) _
Then
    result = True
End If 
  
  
  
  
' ################
' Funktionsausgang
' ################
  
' Rückgabewert übergeben
Try = result
C++:
    ' ##################################################
    ' Variablenreferenzen holen, Funktion initialisieren
    ' ##################################################
    @param 'exceptionToCatch' -> Fehlercode, der geprüft werden soll als INT

    Dim wccFctItems, wccEId, wccFctStatus
    Dim result
  
    result = False




    ' ##############
    ' Funktionslogik
    ' ##############

    ' WinCC-Exception dekodieren
    wccFctItems = Split(Replace(CStr(SmartTags("fctStatus_wcc") / 100), ".", ","), ",")
    If UBound(wccFctItems) = 1 _
    Then
        wccEId = CInt(wccFctItems(0))
        wccFctStatus = CInt(wccFctItems(1))
    Else
        wccEId = EId_NO_ERROR
        wccFctStatus = CInt(wccFctItems(0))
    End If
  

    ' Auswertung je nach gegebener Exception
    If exceptionToCatch = EId_ALL _
    Then    ' alle Exceptions (Warnungen werden ignoriert)
        SmartTags("fctStatus_eId") = -CInt(SmartTags("fctStatus_global") > EId_NO_ERROR) * SmartTags("fctStatus_global") +  -CInt(wccFctStatus = FId_EXECUTED_W_ERRORS) * wccEId
        result = ((SmartTags("fctStatus_global") <> EId_NO_ERROR And SmartTags("fctStatus_global") < WId_NO_WARNING) Or SmartTags("fctStatus_wcc") = FId_EXECUTED_W_ERRORS)
    ElseIf exceptionToCatch > WId_NO_WARNING _
    Then    ' Warnungen
        SmartTags("fctStatus_wId") = -CInt(SmartTags("fctStatus_global") > EId_NO_ERROR) * SmartTags("fctStatus_global") +  -CInt(wccFctStatus = FId_EXECUTED_W_ERRORS) * wccEId
        result = (SmartTags("fctStatus_global") = exceptionToCatch Or (wccEId = exceptionToCatch And wccFctStatus = FId_EXECUTED_W_ERRORS))
    Else    ' Fehler
        SmartTags("fctStatus_eId") = -CInt(SmartTags("fctStatus_global") > EId_NO_ERROR) * SmartTags("fctStatus_global") +  -CInt(wccFctStatus = FId_EXECUTED_W_ERRORS) * wccEId
        result = (SmartTags("fctStatus_global") = exceptionToCatch Or (wccEId = exceptionToCatch And wccFctStatus = FId_EXECUTED_W_ERRORS))
    End If


    ' Rücksetzung der Fehlernummernvariable bzw. des WinCC-Funktionsrückgabewertes
    If result _
    Then
        SmartTags("fctStatus_global") = EId_NO_ERROR
        SmartTags("fctStatus_wcc") = FId_IDLE
    End If
  
  
  
  
    ' ################
    ' Funktionsausgang
    ' ################
  
    ' Rückgabewert übergeben
    Catch = result


Gruß, Fred
 
Zurück
Oben