TIA VB-Skript für Rezept-Datenbank

markus.dietschi

Level-2
Beiträge
21
Reaktionspunkte
2
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo zusammen

Ich bin noch Anfänger mit Siemens TIA und erst recht im VB-Skripting.
Situation:
- Eine Rezeptur-Datenbank in einem TP700-Comfort
- Eigenes Rezepturbild und nicht die Original-Siemens-Rezepturanzeige
- Ein Rezept-Datensatz besteht aus ca. 8 Array's mit je 8 Einträgen (String & Integer) - Datenlänge eines einzelnen Datensatzes ca. 240 Byte
- TIA-Portal V15.1
- TP700-Comfort-Panel
- Software derzeit rein in der Simulation am Laufen

Problemstellung:
Da ich ein eigenes Rezepturbild kreiert habe, habe ich bekanntermassen keinen direkten Zugriff auf die Rezept-Datenbank bzw. deren Struktur und Aufbau.
Wenn ich einen neuen Datensatz im Panel erzeuge, kriegt dieser im Rezeptur-Speicher einfach die nächst freie "Datensatznummer". Somit habe ich keine Ahnung wieviele Datensatzeinträge mein Rezepturspeicher eigentlich hat. Dieses Problem habe ich mittlerweile mit einem Skript gelöst, in welchem ich mit der Systemfunktion "GetDataRecordName" jeden Datensatzeintag aus dem Speicher auslese. Je nachdem ob als Statuswert 4 (erfolgreich) oder 12 (Fehler) rauskommt, weiss ich nun ob ein Eintrag mit dieser "Datensatznummer" existiert oder nicht, bzw. wievile Einträge mein Rezepturspeicher eigenlich hat. Soweit so gut.
Wenn ich jetzt aber einen Datensatz lösche, klafft im Rezepturspeicher im Hintergrund ein Loch in der Datensatz-Nummerierung. Und damit ich nicht jedesmal wenn ich mich durch den Datenspeicher klicke (Plus-Minus-Zählung ebenfalls über Skript gelöst) eine Systemfehlermeldung erscheint, weil er dabei über dieses "Loch" stolpert, wollte ich ein Skript schreiben, das ebenfalls mittels "GetDataRecordName" jeden Datensatz ausliest, und dabei solange weiterläuft bis keine "Status-Fehlermeldung-12" mehr auftaucht, und ich somit weiss, welche "Datensatz-Nummer" als nächstes wieder existiert. Funktioniert ebenfalls......ABER;
Wenn das Skript mehr als ca. 70 Fehlermeldung in seiner FOR-Schlaufe durchläuft bevor dieses wieder eine gültige Datensatznummer findet, kratzt mir das Skript ab, und gibt mir als Fehlermeldung "Überlast" in der Variable an, in welcher das HMI den Status (0,2,4,12) dieser Systemfunktion speichert und an welcher die ganze Skriptfunktion aufgehängt ist.
Alle Variablen, die aus der Funktion "GetDataRecordName" beschrieben werden, sind PLC-Variablen, werden also ständig aktualisiert. Dachte zuerst, dass die Runtime ein Problem damit hat, das die Variable in so kurzer Zeit so oft überschrieben und ausgelesen werden. Habe dann lediglich aus der Statusvariable eine lokale HMI-Variable gemacht. Ergebnis ist, dass das Skript nun erst ab ca.170 Durchläufen diese "Überlast"-Fehlermeldung bringt.

-> WARUM?

zu erwähnen ist ebenfalls, dass die Skriptausführung in der Simulation keine Sekunde dauert, bis diese Fehlermeldung erscheint.

Gibt es hierzu eine logische Erklärung, bzw. einen Lösungsansatz oder mache ich da komplett was verkehrt mit diesem Rezeptur-Handling.

vielen Dank für blickerweiternde Hinweise
 
Die Überlast-Meldung besagt eigentlich, dass noch (Sub-)Scripte laufen, die noch nicht abgearbeitet sind. Ich weiß jetzt nicht, wieviele Sub-Scripte aktiviert sein dürfen - aber irgendwo hört es dann auf.
Ich denke mal, dass du bei asynchronen Funktionen nicht wirklich auf deren Beendigung wartest.
Vielleicht suchst du mal so etwas - etwas anderes kann ich dir pauschal nicht sagen - sonst müßtest du dein Werk mal posten (ist bestimmt interessant)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die Skripts werden nacheinander ausgeführt (Funktionsliste beim entspechenden E/A-Button). Und selbst wenn wären nicht mehr als 2 Skripts gleichzeitig am laufen. Subsricpts sind keine vorhanden. Hatte auch daran gedacht, das Skript asynchron ablaufen zu lassen, um nicht die Systemleistung des HMI zu belasten. Aber keine Ahnung wie das gehen soll.
-> Variable "i" hat zu Anfangs den Wert dessen Datensatz aktuell im HMI gerade angezeigt wird. Und von dieser aus möchte ich nun den nächst höheren Datensatz anzeigen lassen. Daher übergebe ich "i" mal zu Anfang die gerade aktuell angezeigte Datensatz-Nummer
Anbei mal das Meisterwerk :LOL:

Code:
Sub VB_DatabaseCountUp()
'Ermittelt den naechst vorhandenen Datensatz um diesen anzuwaehlen (um Luecken im Datenspeicher zu ueberspringen)
' -> Funktion wird mittels des Statuswertes der Abfrage gesteuert
'
'
Dim i
Dim transferRun
Dim transferFinished
Dim transferError

transferRun = 2
transferFinished = 4
transferError = 12
i = SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber")

For i = i To 100
    Database_ScriptStatus = 0
    SmartTags("Database_Datanumber") = i + 1
    GetDataRecordName _
        1, _
        SmartTags("Database_Datanumber"), _
        "Database_ReceptName", _
        "Database_DataName", _
        "Database_ScriptStatus"
    Do
        If Database_ScriptStatus = transferFinished Then
            SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber") = Database_Datanumber
            Exit For
        End If
    Loop Until Database_ScriptStatus = transferError
Next

End Sub
 
Zuletzt bearbeitet:
Das mit dem Loop in der Schleife will mir nicht so recht gefallen ... du hältst in der Zeit die ganze andere Bearbeitung der Visu an ...
 
Ja das mag stimmen. Aber in der Simulation dauert es selbst bis zum Fehlerfall im 170 Durchlauf (Ja For-Bedingung ist dann höher gewählt) keine Sekunde. Und ne andere Lösung ist mir bis dato noch nicht über den Weg gelaufen. :cool:
Zudem soll ja im HMI durch Drücken des Buttons, hinter welchem dieses Skript ausgeführt wird, mir den nächsten Rezeptur-Datensatz in meinem Rezepturbild anzeigen. Und wenn in dieser Zeit die Visu nicht weitergeht ist das nicht weiter schlimm....Hast es ja so gewollt wenn du den Button drückst :)
Wenn ich die For-Schlaufe auf 50 runtersetze (wenn alle Variablen mit de PLC synchronisiert werden) geht es. Bei ca. 70 stirbt das Ganze.
Wenn ich die For-Schlaufe auf 100 setze (wenn die Statusvariable lokal im HMI bleibt und nur die anderen PLC-Variablen sind) geht es. Bei ca.170 stirbt es dann aber auch.
 
Zuletzt bearbeitet:
Was wäre wenn du daraus einen Quasi-Hintergrundprozess machst (sofern das sinnvoll ist) ?
Mit deinem Tastendruck gibst du von der SPS her einen Blink-Merker frei, der jeweils bei Änderung das Script aufruft, dieses aber jeweils nur ein paar Durchläufe macht ...
 
Was wäre wenn du daraus einen Quasi-Hintergrundprozess machst (sofern das sinnvoll ist) ?
Mit deinem Tastendruck gibst du von der SPS her einen Blink-Merker frei, der jeweils bei Änderung das Script aufruft, dieses aber jeweils nur ein paar Durchläufe macht ...
Ja wäre auch machbar, dann muss ich mir aber wieder merken, bei welcher Durchlaufnummer ich das letzte Mal aufgehört habe, und diesen Wert wieder neu in das Skript reingeben. Klingt mir jetzt nicht gerade edel. Vorallem weiss ich ja nicht einmal ob in Real das HMI dann gleich reagiert wie die Simulation. Somit bin ich dann immer irgendwo in der Grauzone, wann wie lange dieses Skript stabil läuft. Oder sehe ich das falsch?
 
Code:
Dim transferRun
Dim transferFinished
Dim transferError

transferRun = 2
transferFinished = 4
transferError = 12
...
Hat nix mit Deinem eigentlichen Problem zu tun, aber trotzdem der Tip:

diese 3 Variablen von Dir sind ja eigentlich Konstanten und Du solltest sie daher auch so nutzen:
Code:
Const TRANSFER_RUN = 2, TRANSFER_FINISHED = 4, TRANSFER_ERROR = 12
So können sie nicht z.B. durch Tippfehler u.ä. ungewollt verstellt werden.
 
Hat nix mit Deinem eigentlichen Problem zu tun, aber trotzdem der Tip:

diese 3 Variablen von Dir sind ja eigentlich Konstanten und Du solltest sie daher auch so nutzen:
Code:
Const TRANSFER_RUN = 2, TRANSFER_FINISHED = 4, TRANSFER_ERROR = 12
So können sie nicht z.B. durch Tippfehler u.ä. ungewollt verstellt werden.
Vielen Dank für den Input. Einen Schritt näher zum saubern Coding :) . Werde ich so anpassen
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Wenn das Skript mehr als ca. 70 Fehlermeldung in seiner FOR-Schlaufe durchläuft bevor dieses wieder eine gültige Datensatznummer findet, kratzt mir das Skript ab, und gibt mir als Fehlermeldung "Überlast" in der Variable an, in welcher das HMI den Status (0,2,4,12) dieser Systemfunktion speichert und an welcher die ganze Skriptfunktion aufgehängt ist.
Wenn ich das richtig sehe, speicherst Du jetzt den Status immer in der HMI-Variablen "Database_ScriptStatus".

Brauchst Du das Ergebnis unbedingt dort?
Versuche mal alternativ "nur" eine Script-interne Variable, ob Du dann diese Überlast immer noch bekommst:
Code:
Sub VB_DatabaseCountUp()
...

Dim ScriptStatus
...

        GetDataRecordName _
        1, _
        SmartTags("Database_Datanumber"), _
        "Database_ReceptName", _
        "Database_DataName", _
        ScriptStatus
    Do
        If ScriptStatus = TRANSFER_FINISHED Then
            ...

End Sub

:unsure:
Ach, ist vielleicht Blödsinn, weil dann das mit dem Do vermutlich nicht mehr richtig funktioniert, da das GetDataRecordName ja im Hintergrund abläuft.
 
Zuletzt bearbeitet:
Danke für die Idee. Werde ich morgen im Geschäft versuchen, obwohl ich mir fast sicher bin, das ich das heute schon mal versucht hatte und darauf hin mir das Skript nicht mehr funktionierte, da er an dieser Stelle einen anderen Variablentyp verlangt hatte. Vermutlich weil die Funktion ausserhalb des Skripts abläuft und dann keinen Zugang mehr zur Variable hat.......???
 
Danke für die Idee. Werde ich morgen im Geschäft versuchen, obwohl ich mir fast sicher bin, das ich das heute schon mal versucht hatte und darauf hin mir das Skript nicht mehr funktionierte, da er an dieser Stelle einen anderen Variablentyp verlangt hatte. Vermutlich weil die Funktion ausserhalb des Skripts abläuft und dann keinen Zugang mehr zur Variable hat.......???
Vermutlich hast Du da Recht:
:unsure:
Ach, ist vielleicht Blödsinn, weil dann das mit dem Do vermutlich nicht mehr richtig funktioniert, da das GetDataRecordName ja im Hintergrund abläuft.
Manchmal ist man doch etwas vorschnell, nur weil es syntaktisch funktioniert.
😔

PS:
steht auch so in der TIA-Hilfe:
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:
...
Für den Parameter können Sie nur eine HMI-Variable verwenden.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
:unsure:
Wie reagiert eigentlich so 'ne Do_Loop-Schleife darauf, wenn man aus ihr mit EXIT_FOR heraus springt?
Wird sie dann wirklich beendet?

Und i sowohl als Startwert als auch als Zählvariable zu verwenden, ist IMHO auch nicht ganz sauber.


Versuch also mal, ob es hilft, das Script etwas umzustellen:
Code:
Sub VB_DatabaseCountUp()
'Ermittelt den naechst vorhandenen Datensatz um diesen anzuwaehlen (um Luecken im Datenspeicher zu ueberspringen)
' -> Funktion wird mittels des Statuswertes der Abfrage gesteuert
'
'

    Const TRANSFER_CLEAR = 0, TRANSFER_RUN = 2, TRANSFER_FINISHED = 4, TRANSFER_ERROR = 12
    
    Dim Start_number, Data_record_number
    
    
    Start_number = SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber") + 1
    
    For Data_record_number = Start_number To 100
        
        Database_ScriptStatus = TRANSFER_CLEAR
        GetDataRecordName 1, Data_record_number, "Database_ReceptName", "Database_DataName", "Database_ScriptStatus"

        ' wait for execution
        Do
        Loop Until Database_ScriptStatus > TRANSFER_RUN
        
        ' evaluation
        If Database_ScriptStatus = TRANSFER_FINISHED Then
            SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber") = Data_record_number
            Exit For
        End If
        
    Next

End Sub
 
@hucki : das wird so nicht funktionieren weil der Loop so ggf. nicht mehr abgebrochen wird ... (Edit : habe das ">" nicht gesehen)
mit dem "for i=i to 100" gebe ich dir aber Recht - das ist wenigstens unsauber
 
Zuletzt bearbeitet:
@hucki :
Vielen lieben Dank für dein Feedback. Habe den Code gemäss deinen Vorgaben angepasst. Habe am Schluss einfach noch eine Variable dazugefügt, damit ich in der PLC unten sehe, wie weit das er bei der Zählung kommt. Abbrechen tut das Ganze immer noch, und zwar unabhängig davon wie die Aktualisierung der Variable "DB600_HMI_StateWord_DataNumber" eingestellt ist.
Fehlermeldung:
- $190010 : "DB600_HMI_StateWord_DataName": Überlast, Werte gehen verloren
- $190010 : "DB600_HMI_StateWord_DataNumber": Überlast, Werte gehen verloren
- $20010 : Fehler 'Überlauf' in Skript <VB_DatabaseNextPost()> in Zeile 30
Alarmeintrag dieser 3 Meldungen einmal mit gleichem Zeitstempel
Wenn ich die Zeile 30 (Beobachtungsvariable PLC) lösche, dann sieht die Fehlermeldung aber so aus:
- $190010 : "DB600_HMI_StateWord_ReceptName": Überlast, Werte gehen verloren
- $190010 : "DB600_HMI_StateWord_DataName": Überlast, Werte gehen verloren
Alarmeintrag dieser Doppeleintrag zwischen 10-30 Mal mit dem gleichen Zeitstempel ?????? WTF ????

Ebenfalls ist die Überlebensdauer des Skripts extrem unterschiedlich....zwischen 110 bis 200 Durchläufe. Mit der vorherigen Codierung war die Absturzstelle immer auf +/- 10 Durchläufe dieselbe.
Der Code sieht nun so aus:
Code:
Sub VB_DatabaseNextPost()
'Ermittelt den naechst vorhandenen Datensatz um diesen anzuwaehlen (um Luecken im Datenspeicher zu ueberspringen)
' -> Funktion wird mittels des Statuswertes der Abfrage gesteuert
'
'
Const TRANSFER_CLEAR = 0, TRANSFER_RUN = 2, TRANSFER_FINISHED = 4, TRANSFER_ERROR = 12            'Statusvariablen: Uebertragung; "Bereit" / "Laeuft" / "Beendet" / "Fehlerhaft"

Dim startNumber, loopNumber

startNumber = SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber") + 1                    'Wertuebergabe an die Variable

For loopNumber = startNumber To 200
    Database_ScriptStatus = TRANSFER_CLEAR
    GetDataRecordName _
        1, _
        loopNumber, _
        "DB600_HMI_StateWord_ReceptName", _
        "DB600_HMI_StateWord_DataName", _
        "Database_ScriptStatus"                                                                    'Ausfuehrungsbefehl "LeseDatensatzname"
       
    ' wait for execution
    Do
    Loop Until Database_ScriptStatus > TRANSFER_RUN
   
    ' evaluation
    If Database_ScriptStatus = TRANSFER_FINISHED Then
        SmartTags("DB820_RezeptHMI_strRecept.Rezeptinfo.iDatanumber") = loopNumber
        Exit For
    End If
    SmartTags("DB600_HMI_StateWord_DataNumber") = loopNumber
Next

End Sub

Kann man sich aufgrund diesem Verhalten einen Reim daraus machen?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo.

Die Meldung "Script Überlast" wird getriggert, wenn die interne Script-Queue (also die Warteschlange von Scripten, die zur Bearbeitung anstehen) überläuft; dies passiert bei 20 Scripten.

Ein Script wird von der Runtime zur Bearbeitung an das Betriebssystem übergeben, es kann/wird also immer nur ein Script gleichzeitig behandelt.
Für mich sieht es also so aus, als wenn ein oder mehrere Scripte nahezu gleichzeitig und vor allem wiederkehrend getriggert werden.

Gruß, Fred
 
Hallo.

Die Meldung "Script Überlast" wird getriggert, wenn die interne Script-Queue (also die Warteschlange von Scripten, die zur Bearbeitung anstehen) überläuft; dies passiert bei 20 Scripten.

Ein Script wird von der Runtime zur Bearbeitung an das Betriebssystem übergeben, es kann/wird also immer nur ein Script gleichzeitig behandelt.
Für mich sieht es also so aus, als wenn ein oder mehrere Scripte nahezu gleichzeitig und vor allem wiederkehrend getriggert werden.

Gruß, Fred
Es läuft nur dieses eine Skript. Und ausgelöst wird es durch einen E/A-Button unter dem Ausführungsbefehl "Drücken". Wenn ich das richtig interpretiere, wird das Skript somit nur ein Einiges Mal ausgelöst. Oder stimmt das so nicht?
 
Die Meldung "Script Überlast" wird getriggert, wenn die interne Script-Queue (also die Warteschlange von Scripten, die zur Bearbeitung anstehen) überläuft; dies passiert bei 20 Scripten.

Ein Script wird von der Runtime zur Bearbeitung an das Betriebssystem übergeben, es kann/wird also immer nur ein Script gleichzeitig behandelt.
Richtig!

Hallo markus
Hast Du noch Skripte, die z.B. bei Wertänderung von Variablen aufgerufen werden?
Solange dein Skript VB_DatabaseNextPost ausgeführt wird (insbesondere solange es in der Do..Loop-Schleife hängt), werden keine weiteren Skripte ausgeführt sondern nur in die Skript-Warteschlange eingetragen. Und die läuft über, wenn mehr als 20 Skripte eingetragen werden müssen.

Man muss nicht im Skript warten, bis GetDataRecordName fertig ist. Man kann auch im Skript nur GetDataRecordName anstossen und das Skript beenden, und bei Änderung der Status-Variable "Database_ScriptStatus" ein Skript aufrufen und da weitermachen. Deshalb darf die Status-Variable keine Skript-interne Variable sein, sondern muss eine globale HMI-Variable (SmartTag) sein. Allerdings lösen HMI-interne Variablen bei Wertänderung durch Zuweisung keine Ereignisse "Wertänderung" aus. Deshalb muß die Status-Variable in einer HMI-Verbindung liegen.
 
Zurück
Oben