TIA SCL-Konvertierung von Classic nach TIA mit TON / WRREC / RDREC

Draco Malfoy

Level-1
Beiträge
1.168
Reaktionspunkte
82
Zuviel Werbung?
-> Hier kostenlos registrieren
Moin zusammen.

Frage: beim Versuch, ein absolut funktionsfähiges Projekt von Classic nach TIA zu migrieren, was ausschließlich in SCL verfasst ist, und wo alle Quellen vorhanden sind, erscheinen Fehlermeldungen, die etwas mit dem Aufruf von SFBs 53, 54 und TON zu tun hat. Im Original wurden z.B. beim Timer nicht im jeden Aufruf alle Variablen versorgt. TIA meckert darüber.

Mir scheint, als gebe es diskrepanzen bei dem Originalprojekt und bei dem migrierten Projekt hinsichtlich des Aufrufes von "TON" als lokale Instanz und als Einzelinstanz (s. 417 im Berger-Buch).
Desweiteren werden im Projekt ja wie bereits erwähnt, die WRREC und RDREC genutzt. Allerdings unterscheidet sich deren Schnittstelle und Verwendung von Classic zu TIA ja erheblich. Im klassischen musste ich die E/A-Adressen der Subslots übergeben, während TIA ja mit sogenannter "Hardwarekennung", und zwar intern vergeben arbeitet. Diese unterscheidet sich in der Regel von den E/A Adressen. Es gibt ja jetzt sogar speziellen Datentyp für diese "Hardwarekennung". Inwieweit müsste ich hier von Hand also tätig werden, um das Projekt wieder lauffähig zu machen ?
 
Also ich wiederhole die Frage mit einem konkreten Beispiel. Wie muss ich hier vorgehen, damit mein Code wieder funktioniert und TIA nicht mehr meckert ?
Ich habe neuerdings das hier gelesen, denke das hat damit unmittelbar zu tun:
Bausteinaufrufe in S7-SCL-Quellen
In den S7-SCL-Quellen bei STEP 7 V5.x konnten Sie die Bausteinparameter vor oder nach dem eigentlichen Aufruf des Bausteins setzen. Das heißt, der Baustein konnte mit unvollständigen Parametern aufgerufen und die In/Out-Parameter der Bausteine an einer anderen Stelle innerhalb der S7-SCL-Quelle mit Variablen oder Werten versorgt werden. Diese Art der Programmierung ist in STEP 7 (TIA Portal) systembedingt nicht möglich und wird auch in Zukunft nicht unterstützt. Sie können das STEP 7 V5.x Projekt zwar in TIA Portal fehlerfrei migrieren und übersetzen, doch ist die Funktion dieses Bausteinaufrufes innerhalb des S7-SCL-Programmes nicht mehr möglich. Bevor Sie Ihre S7-SCL-Quelle in STEP 7 V5.x übersetzen und das Projekt in TIA Portal migrieren, beachten Sie die folgenden Punkte:
  • Alle Parameter des aufgerufenen Bausteins müssen innerhalb der Aufrufanweisung versorgt werden.
  • Es dürfen keine Parameter des aufgerufenen Bausteins gelöscht und an einer anderer Stelle der S7-SCL-Quelle eingefügt und parametriert werden.

Code:
                #ISTATE_START:
                        (* reset timer *)
                        #timeout(IN:= FALSE);
                        (* start timer *)
                        #timeout(IN:= TRUE, PT:= #TIMEOUT_60_SECONDS);
                        
                        (* first check PDU size *)
                        #BUF_MAX:= 240;
                        #BUF_MAXDATA:= #BUF_MAX - #BUF_HEADER;
                        #pdu_detection:= TRUE;
                        
                        #state:= #ISTATE_SET_INIT;
                        
                #ISTATE_SET_INIT:
                        IF #init_active = TRUE THEN
                                (* initialize some variables *)
                                #old_CC_H:= FALSE;
                                #old_CC_L:= FALSE;
                                #req:= TRUE;
                                
                                (* remember AC_H/AC_L *)
                                #old_AC_H:= #AC_H;
                                #old_AC_L:= #AC_L;
                                
                                (* drop INIT *)
                                #INIT_cyc:= FALSE;
                                
                                (* and wait until init_active is dropped *)
                                #state:= #ISTATE_INIT_ACTIVE;
                        ELSE
                                (* check for timeout *)
                                #timeout();
                                IF #timeout.Q = TRUE THEN
                                        #state:= #ISTATE_ERROR;
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Na ja, ich fand diese Art des Aufrufs, die du da gepflegt hast schon immer ein wenig abenteuerlich, da recht unübersichtlich, aber das darf jeder für sich entscheiden.
Ich denke, du hast zwei Möglichkeiten:

1. Alle Timeout-Aufrufe vollständig machen und mit Zwischenvariablen versorgen. Diese Zwischenvariablen kannst du dann ruhig an den "alten Stellen" befüllen.
Risiko: Falsche Werte für den Timeout; Code, in dem Timeout aufgerufen wird, wird zu früh verlassen, so dass du bei einem weiteren Aufruf keine Flanke erkennst (Das hattest du ja früher schon, daher der Timeout-Aufruf mit "IN := False".

2. Nur ein Timeout-Aufruf, der auch immer bearbeitet, also nie umsprungen wird, dieser mit Variablen versorgt.

#timeout(IN:= #TimeoutStart, PT:= #TIMEOUT_SECONDS);


Die Variablen werden im Code belegt, Dann startet der Timer, wenn #TimeoutStart --> True wird.

Das Ergebnis kannst du immer noch überall abfragen (300-er im TIA) mit

IF #timeout.Q THEN

END_IF;

Du kannst natürlich auch #Timout.Q auf eine Variable schreiben und dann damit arbeiten.

Auch hier ein Problem: Benötigst du #timeout exakt zweimal hintereinander hast du auch hier ein Problem mit der Flanke. Entweder du hast dann einen Zwischenschritt, in dem TimoutStart = False erkannt werden kann oder, wenn das nicht geht, dann nutzt du 2 TimoutTimer abwechselnd.
 
Hi

einen Timer mehrfach im Programm zu verwenden war schon immer sehr problematisch.
Und dann auch noch um die Timer Befehle herum zu springen sorgt dafür, dass außer dir erstmal jeder 10 min braucht um alle Pfade zu analysieren.

Es sollt immer genau eine Stelle geben in der der Timer kontrolliert wird. Es mag viele Stellen geben, an der man den Timer fragt ob er abgelaufen ist, aber bitte nur an einer Stelle starten und stoppen (falls) nötig.

Bei der 300/400 hat man auch noch ein Problem, wenn der SFB nicht aufgerufen wird, denn dann läuft auch die Zeit nicht ab, d.h. es nutzt nix wenn du auf #timer.Q zugreift ohne auch #timer(...) zu durchlaufen. Denn nur im SFB Aufruf, findet eine Zeitmessung statt. Danach steht er wieder.
Bei der 1200/1500 ist das anders, da läuft die Zeit auch wenn du #timer() nicht mehr aufrufst. Hier wird auch beim Zugriff mit #timer.Q oder #timer.ET gemessen. Der #timer wird bei der 1200/1500 nicht durch einen SFB implementiert. Deswegen muss dort auch PT versorgt werden.

Vermutlich unterscheidet der Editor aber nicht zwischen 300/400 und 1200/1500 und verlangt jetzt immer die PT Versorgung -- soll man das als Fehler werten?
Unversorgte Parameter an FB waren aber immer schon Quell vieler Fehler die ich suchen durfte.

Deswegen: Schreib wie Ralle vorschlägt den Timer vor den Switch-Case, so dass dieser immer aufgerufen wird.
Alles andere, die Zeit auswählen und das Start-Flag setzten, darf in deinem Zustandsautomaten bleiben. Und wenn du den Timer mehrfach brauchst, dann immer über Zwischenschritt.
Ich würde das als gute Praxis bezeichnen, spart sehr viel Zeit bei der Fehlersuche!

'n schön' Tag auch
HB


oh Gott, was machen denn diese Holländer?
 
Moin. Vielen Dank schon ma für die gründlichen Erklärungen.
Ich habe den Code leider nicht selber geschrieben, sonst würde ich vermutlich einiges mehr wissen was und wie dort zu funktionieren hat. Um mal auf der sicheren Seite zu gehen, ich habe jetzt den Timer "nach Außen" verlegt und versorge ihn erst mal außerhalb von den CAS und IF..THEN Anweisungen mit leeren Variablen, die innerhalb der Schleife beschrieben und abgefragt werden. Ist es einigermaßen korrekt so ? Was muss ich noch befürchten ?
An anderen Stellen wird der Timer nicht beschrieben, sondern nur abgefragt mittels IF #timeout.Q = TRUE THEN.

Code:
Ganz zu Anfang:

#timeout(IN := #TimeOut_Start, PT := #timeout_Seconds);   //Ab hier Code von Draco Malfoy 
                            
<..dann irgendwann im Code...>

                (* substate machine -initialization- *)
                #ISTATE_START:
                        (* reset timer *)
                    #TimeOut_Start:= FALSE;
                        (* start timer *)
                    #TimeOut_Start := TRUE;
                    #timeout_Seconds := #TIMEOUT_60_SECONDS;
                        
                        (* first check PDU size *)
                        #BUF_MAX:= 240;
                        #BUF_MAXDATA:= #BUF_MAX - #BUF_HEADER;
                        #pdu_detection:= TRUE;
                        
                        #state:= #ISTATE_SET_INIT;
                        
                #ISTATE_SET_INIT:
                        IF #init_active = TRUE THEN
                                (* initialize some variables *)
                                #old_CC_H:= FALSE;
                                #old_CC_L:= FALSE;
                                #req:= TRUE;
                                
                                (* remember AC_H/AC_L *)
                                #old_AC_H:= #AC_H;
                                #old_AC_L:= #AC_L;
                                
                                (* drop INIT *)
                                #INIT_cyc:= FALSE;
                                
                                (* and wait until init_active is dropped *)
                                #state:= #ISTATE_INIT_ACTIVE;
                        ELSE
                                (* check for timeout *)
[COLOR=#ff0000]                               // #timeout(); -->> Aufruf nicht mehr benötigt ??
                                IF #timeout.Q = TRUE THEN // Kann man so lassen ??[/COLOR]
                                        #state:= #ISTATE_ERROR;

An anderer Stelle meckert wiederholt mein Compiler, daß eine Variable angeblich "möglicherweise nicht initialisiert wird". Was soll ich damit tun, sie im Anlauf null setzen ?
Meint der Compiler hier, daß ich sie abfrage, bevor ich sie beschrieben habe ? Mir ist nicht verständich der Grund dieser Warnung, es kann ja sein daß die Variable im vorherigen Zyklus in einer IF oder CASE Anweisung beschrieben wurde.
 
Zuletzt bearbeitet:
Ich denke mal, das geht schon, hat halt jeder so seine Vorlieben. :)
Abfragen kann man den Timer offensichtlich überall, dass läßt Siemens zu. Insofern kannst du, wie ich schon schrieb, entweder #timeout.Q abfragen oder direkt hinter den Aufruf von Timeout #timeout_output := #timeout.Q schreiben.

PS: Lt. HelleBarde läuft ja so eine Timer bei einer 1200/1500-er weiter, also verhält er sich dort so wie die Hardwaretimer der 300-er. Das heißt, ein Timer kann jederzeit im Zyklus ablaufen. Fragst du ihn in einem Zyklus an 2 Stellen im Code ab, kann er einmal False und ein anderes Mal True sein, was zu Problemen führen kann. Mit der Zuweisung an eine Variable direkt nach dem Aufruf passiert das nicht, die Variable #timeout_output wird sich nur einmal im Zyklus ändern können, genau dann, wenn sie zugewiesen wird!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ok alles klärchen ;-)
Dann mache ich das besser so, wie von Ralle angeraten wurde.
Diese Warnung mit der fehlenden Initialisierung, muss ich da noch tätig werden oder einfach ignorieren ?
 
Zurück
Oben