TIA Fehlermeldung OB80 "Cycle Time" überschritten

Vorarlberger

Member
Beiträge
5
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Sehr geehrte Damen und Herren,

Ich arbeite gerade an einem Projekt und habe folgendes Problem "Maximum program cycle time exceeded - Time error, OB80 start requested".

1651151788588.png

in meinem Projekt geht es darum, geschickte Byte-Arrays aus einem Plugin auf die SPS zu übertragen und verarbeiten. Die geschickten Byte-Arrays sollen auf der SPS in den jeweilige Datentyp umgewandelt werden. Jedoch habe ich feststellen können, dass meine SPS (Software SPS 1507S) die Byte-Arrays in einem Zyklus nicht abarbeiten kann. Darum wollte ich fragen, ob es eine Möglichkeit gibt die Abarbeitung so zu gestallten, dass die Byte-Arrays in mehreren Zyklen abgearbeitet werden können.
Hinweis: Programm ist in SCL geschrieben!

Hier ist auch ein Ausschnitt aus meinem Programm:

Code:
                    #InternalPosition := 1;
                    #NumberOfSteps := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
                
                    FOR #Index_I := 0 TO #NumberOfSteps DO
                        #LengthOfStep := BYTE_TO_INT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
                        #Bewegung[#Index_I].ID := BYTE_TO_USINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 1]);
                        #StringMaxLength := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 2]);
                        #StringActuallLength := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 3]);
                        FOR #TempIndex := 0 TO #StringActuallLength DO
                            #InternalString := BYTE_TO_CHAR(#Command[#CommandIndex].Out.Data[#TempIndex]);
                            #Bewegung[#Index_I].Name := CONCAT_STRING(IN1 := #Step[#Index_I].Name, IN2 := #InternalString);
                        END_FOR;
                    
                        #Bewegung[#Index_I].MoveLinear := BYTE_TO_BOOL(#Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 2]);
                    
                        FOR #IndexOfPositionNumber := 0 TO 3 DO
                            #Temp_DWORD_Variable_1.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 3];
                            #Temp_DWORD_Variable_1.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 4];
                            #Temp_DWORD_Variable_1.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 5];
                            #Temp_DWORD_Variable_1.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 6];
                            #Bewegung[#Index_I].PositionNumber := DWORD_TO_DINT(#Temp_DWORD_Variable_1);
                        END_FOR;
                    
                        FOR #IndexOfSpeed := 0 TO 3 DO
                            #Temp_DWORD_Variable_2.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 7];
                            #Temp_DWORD_Variable_2.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 8];
                            #Temp_DWORD_Variable_2.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 9];
                            #Temp_DWORD_Variable_2.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 10];
                            #Bewegung[#Index_I].Speed := DWORD_TO_REAL(#Temp_DWORD_Variable_2);
                        END_FOR;
                    
                        #Bewegung[#Index_I].Acceleration := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 11]);
                        #Bewegung[#Index_I].Deceleration := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 12]);
                        #Count := #LengthOfStep;
                        #InternalPosition += #Count;
                    END_FOR;

Noch ein paar Worte zum Code. Die geschickten Byte-Arrays sind Daten für einen Roboter. Hier werden die geschickten Byte-Arrays in den jeweiligen Datentyp umgewandelt. Da es keine direkte Umwandlung von BYTE_TO_REAL und BYTE_TO_DINT gibt, wurde eine Lösung mit einer For-Schleife ausgedacht. Ich gebe im Programm immer an, an welcher Stelle die gewünschten Informationen sind/stehen.
 
Zuletzt bearbeitet:

Blockmove

Supermoderator und User des Jahres 2019
Teammitglied
Beiträge
9.625
Punkte Reaktionen
2.613
Anstelle von For-Next die Variable #Index_I in jedem Zyklus erhöhen bis NumberOfSteps erreicht ist
 
OP
V

Vorarlberger

Member
Beiträge
5
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Anstelle von For-Next die Variable #Index_I in jedem Zyklus erhöhen bis NumberOfSteps erreicht ist
Vielen Dank für die schnelle Rückmeldung @Blockmove, jedoch wollte ich nochmals nachfragen, was Sie mit NEXT meinen? (In welchem Teil vom Code einfügen, geht das im TIA-Portal, wie kann ich mir das vorstellen) habe es noch nie verwendet. Zweite Frage, an welcher Stelle im Code soll der Index_I erhöht werden? (In welcher For-Schleife).
 
Zuletzt bearbeitet:

Blockmove

Supermoderator und User des Jahres 2019
Teammitglied
Beiträge
9.625
Punkte Reaktionen
2.613
Vielen Dank für die schnelle Rückmeldung @Blockmove, jedoch wollte ich nochmals nachfragen, was Sie mit NEXT meinen? (In welchem Teil vom Code einfügen, geht das im TIA-Portal, wie kann ich mir das vorstellen) habe es noch nie verwendet. Zweite Frage, an welcher Stelle im Code soll der Index_I erhöht werden? (In welcher For-Schleife).

Diese Form der Schleifen heißt im allgemeinen Gebrauch For-Next-Schleife.
Je nach Programmiersprache gibt's dann halt ein End_For oder Klammern oder sonstwas.

Index_I kannst du z.B. an der Stelle erhöhen, an der jetzt das End_For ist.
 

JesperMP

Well-known member
Beiträge
7.017
Punkte Reaktionen
1.415
Das ist alles recht einfache Typumwandlungen und einmal STRING Zusammenfügung.
Das sollte ein 1507S problemlos leisten können, auch wenn durch die 2 verschachtelte FOR Schleifen die Anweisungen vielmal wiederholt werden müssen.

Du kannst diese Variabeln in ein Variabeltabelle beobacten
#Index_I
#NumberOfSteps
#TempIndex
#StringActuallLength

Wenn die CPU stoppt mit OB80, welche Werte stehen dann in diese Variabeln ?
Das wurde vielleicht etwas erklären.
 

Heinileini

Well-known member
Beiträge
4.984
Punkte Reaktionen
1.066
Zuviel Werbung?
->Hier kostenlos registrieren
Ich verstehe die ProgrammZeile ...
Code:
#NumberOfSteps := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
... nicht.
Ist es Absicht, dass Du für NumberOfSteps Werte im Bereich von -128..0..127 erhältst?
Wieviele SchleifenDurchläufe kommen zustande, wenn Du mit ...
Code:
FOR #Index_I := 0 TO #NumberOfSteps DO
... die Indizes 0, 1, 2, ..., 127, u.s.w. bis zum Erreichen eines negativen Wertes im Bereich von -128..-1 ausführst?
Wäre nicht ...
Code:
#NumberOfSteps := BYTE_TO_USINT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
... angemessener (Werte 0..255)?
 
Zuletzt bearbeitet:

Thomas_v2.1

Well-known member
Beiträge
8.430
Punkte Reaktionen
2.551
Ich würde als erstes mal die Schleifen entfernen die immer das gleiche machen:
FOR #IndexOfPositionNumber := 0 TO 3 DO
#Temp_DWORD_Variable_1.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 3];
#Temp_DWORD_Variable_1.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 4];
#Temp_DWORD_Variable_1.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 5];
#Temp_DWORD_Variable_1.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 6];
#Bewegung[#Index_I].PositionNumber := DWORD_TO_DINT(#Temp_DWORD_Variable_1);
END_FOR;

FOR #IndexOfSpeed := 0 TO 3 DO
#Temp_DWORD_Variable_2.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 7];
#Temp_DWORD_Variable_2.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 8];
#Temp_DWORD_Variable_2.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 9];
#Temp_DWORD_Variable_2.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + #StringActuallLength + 10];
#Bewegung[#Index_I].Speed := DWORD_TO_REAL(#Temp_DWORD_Variable_2);
END_FOR;

Oder das ist mal wieder nur die Hälfte des wirklichen Programms.
 

Heinileini

Well-known member
Beiträge
4.984
Punkte Reaktionen
1.066
Ich würde als erstes mal die Schleifen entfernen die immer das gleiche machen:
Und ich würde erstmal klären, wieviele Durchläufe maximal für die äussere Schleife gewünscht/gefordert sind!
Falls mehr als 128, aber nicht mehr als 256 in Frage kommen, würde ich unbedingt BYTE_TO_SINT in BYTE_TO_USINT ändern, da sonst die unnötigen und störenden und unerwünschten und zeitfressenden Durchläufe (bei einem #Index_I vom Typ INT) für die Indizes 128..32767 und -32768..-129 stattfinden.
 
Zuletzt bearbeitet:

Heinileini

Well-known member
Beiträge
4.984
Punkte Reaktionen
1.066
Wenn #NumberOfSteps < 0, dann wird die alles nach dem DO genau Null mal durchlaufen. Ohne BY n mit n<0 läuft die Schleife nicht rückwärts.
Du magst Recht haben, Thomas. Ich kann es leider nicht testen. Kommt drauf an, ob das EndeKriterium per = oder >= bei positiver ZählRichtung verglichen wird.
Habe schon FOR-Schleifen erlebt, die erst dann aufhören zu "schleifen", wenn das EndeKriterium exakt erreicht wird.
FehlerMeldungen bei einem Über- oder UnterLauf der ZählVariablen sind auch in vielen ProgrammierSprachen nicht realisiert.

In VBA ist es tatsächlich so, dass die Schleifen
Code:
For ix& = 0 To 10 Step -1
Debug.Print ix&
Next ix&
oder
Code:
For ix& = 0 To -10
Debug.Print ix&
Next ix&
gar nicht erst durchlaufen werden!
 
Zuletzt bearbeitet:

Oberchefe

Well-known member
Beiträge
2.425
Punkte Reaktionen
405
wenn ich von einer Stringlänge von 1 ausgehe (also 1 Zeichen), dann durchläuft die zweite FOR-Schleife aber 2 mal und schreibt demzufolge 2 Zeichen?

Code:
                        FOR #TempIndex := 0 TO #StringActuallLength DO
                            #InternalString := BYTE_TO_CHAR(#Command[#CommandIndex].Out.Data[#TempIndex]);
                            #Bewegung[#Index_I].Name := CONCAT_STRING(IN1 := #Step[#Index_I].Name, IN2 := #InternalString);
                        END_FOR;



#InternalPosition kann in der kompletten Schleife ersetzt werden durch #Index_I +1, (erste Verwendung durch 1 ersetzen, Zeile mit der Inkrementierung entfällt), wird dann evtl. etwas verständlicher

Aber zum Thema: Rein vom Datentyp ergeben die beiden ersten FOR-Scheifen maximal 128*128 -> 16384 Durchläufe
 
OP
V

Vorarlberger

Member
Beiträge
5
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Vielen Dank für die ganzen Rückmeldungen. Ich habe mal alles umgesetzt, was ich umsetzen konnte. Jedoch bekomme ich immer noch die gleiche Fehlermeldung.
Der Code schaut gerade folgendermaßen aus:
Code:
                    #InternalPosition := 1;
                    #NumberOfSteps := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition]);

                    #LengthOfStep := BYTE_TO_INT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
                    #Bewegung[#Index_I].ID := BYTE_TO_USINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 1]);
                    #StringMaxLength := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 2]);
                    #StringActuallLength := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 3]);
                    FOR #TempIndex := 0 TO #StringActuallLength DO
                        #InternalString := BYTE_TO_CHAR(#Command[#CommandIndex].Out.Data[#TempIndex]);
                        #Bewegung[#Index_I].Name := CONCAT_STRING(IN1 := #Step[#Index_I].Name, IN2 := #InternalString);
                    END_FOR;
                      
                    #Bewegung[#Index_I].MoveLinear := BYTE_TO_BOOL(#Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 2]);
                      
                    #Temp_DWORD_Variable_1.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 3];
                    #Temp_DWORD_Variable_1.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 4];
                    #Temp_DWORD_Variable_1.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 5];
                    #Temp_DWORD_Variable_1.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 6];
                    #Bewegung[#Index_I].PositionNumber := DWORD_TO_DINT(#Temp_DWORD_Variable_1);
                      
                    #Temp_DWORD_Variable_2.%B3 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 7];
                    #Temp_DWORD_Variable_2.%B2 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 8];
                    #Temp_DWORD_Variable_2.%B1 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 9];
                    #Temp_DWORD_Variable_2.%B0 := #Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 10];
                    #Bewegung[#Index_I].Speed := DWORD_TO_REAL(#Temp_DWORD_Variable_2);
                  
                    #Bewegung[#Index_I].Acceleration := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 11]);
                    #Bewegung[#Index_I].Deceleration := BYTE_TO_SINT(#Command[#CommandIndex].Out.Data[#InternalPosition + 2 + #StringActuallLength + 12]);
                    #Count := #LengthOfStep;
                    #InternalPosition += #Count;
                  
                    #Index_I := #Index_I + 1;

Index_I wurde am Anfang auf 0 gesetzt!
 
Zuletzt bearbeitet:

PN/DP

User des Jahres 2011-2013; 2015-2017; 2020-2021
Beiträge
18.455
Punkte Reaktionen
5.490
Die vielen kryptischen Programmzeilen verwirren mich und verwischen die Übersicht über das Ganze. Kannst Du vielleicht mit Klartext-Worten beschreiben, was der Programmcode tun soll? Dann könnten wir über das grundsätzliche Vorgehen auf übersichtlicherem Level diskutieren und Du kannst das dann im Detail durch die nötigen Anweisungen implementieren.

Harald
 
OP
V

Vorarlberger

Member
Beiträge
5
Punkte Reaktionen
0
Die vielen kryptischen Programmzeilen verwirren mich und verwischen die Übersicht über das Ganze. Kannst Du vielleicht mit Klartext-Worten beschreiben, was der Programmcode tun soll? Dann könnten wir über das grundsätzliche Vorgehen auf übersichtlicherem Level diskutieren und Du kannst das dann im Detail durch die nötigen Anweisungen implementieren.

Harald
Hallo @PN/DP Harald, ich bin derzeit an einem Projekt dran. In meinem Projekt geht es darum, dass Daten (Byte-Arrays) aus einem Plugin auf die SPS geschickt werden. Die Daten sollen dann auf der folgenden SPS überarbeitet werden. Die Daten werden in Byte-Arrays geschickt (ASCII), nun möchte ich jetzt die verschickten Byte-Arrays in den jeweiligen Datentyp umschreiben/umwandeln (siehe Bild). Jedoch habe ich mit meiner jetzigen Logik festgestellt, dass meine SPS (Software SPS 1507S) die Byte-Arrays in einem Zyklus nicht abarbeiten kann. Darum wollte ich fragen, ob es eine Möglichkeit gibt die Abarbeitung so zu gestallten, dass die Byte-Arrays in mehreren Zyklen abgearbeitet werden können.
Hinweis: Programm ist in SCL geschrieben!
Hier noch ein Bild dazu, wie eine Bewegung (ein Step) ausschaut!
1651223961606.png
 
Zuletzt bearbeitet:

JesperMP

Well-known member
Beiträge
7.017
Punkte Reaktionen
1.415
Hier ist Irgendetwas faules los.
Also, es gibts nicht bemerkungswertes. Meistens sind Typkonvertierungen.
Nur die STRING ist ein bisschen speziell. Die einzelne CHARS werden mttels ein FOR Schleife in einen STRING CONCAT'et.
Das ist alles. Wie kann das Zukluszeitüberschreitung auslösen in ein 1507S ?

Ich glaube dass das ganze etwas eleganter mit ein AT-Sicht gelöst werden kann.
Edit: Wegen die dynamische Länge von den STRING, leider nicht.
 
Zuletzt bearbeitet:

escride1

Well-known member
Beiträge
912
Punkte Reaktionen
191
Kannst Du Deinen Baustein einfach als SCL-Quelle exportieren, Identitätsnamen entfernen und hier einstellen? Das wäre echt um einiges hilfreicher zumal dann die Experten die bereits geantwortet haben binnen wenigen Minuten den Fehler finden können.
 
OP
V

Vorarlberger

Member
Beiträge
5
Punkte Reaktionen
0
Zuviel Werbung?
->Hier kostenlos registrieren
Also kurz beschrieben. Wie man auch im Codeauszug sehen kann, habe ich am Anfang des Codes die Variable #InternalPosition auf 1 gesetzt. Ich habe es mit dem Gedanken auf 1 gesetzt, damit ich zum Schluss immer sagen kann wie viele Bytes bei dem jeweiligen Step abgearbeitet worden sind, weil alle Bewegungen nicht gleich lang sind. Im weitern Verlauf des Codes bin ich folgendermaßen vorangegangen:
Wie man auch im Bild sehen kann wird am Anfang des Ablaufes die Anzahl an Bewegungen definiert "erste stelle --> USInt = 1 Byte". Somit habe ich im Programm programmiert:
Code:
#NumberOfSteps := BYTE_TO_USINT(#Command[#CommandIndex].Out.Data[#InternalPosition]);
Hier fülle ich den #ComandBuffer und definiere, dass an dieser Stelle die "Anzahl an Bewegungen" definiert ist. Das gleiche Prinzip mache ich auch zum Beispiel für die #LengthOfStep nur, dass ich hier die #InternalPosition Variable um eins erhöhe. Den Vorgang habe ich für alle Daten gemacht.
Da es keine direkte Funktion für BYTE_TO_DINT und BYTE_TO_REAL gibt, habe ich zunächst die 4 Byte in ein DWORD geformt, und dann das Bitmuster des DWORD 1:1 ohne Veränderung in eine Variable des Ziel-Datentyps kopiert --> das macht DWORD_TO_DINT bzw. DWORD_TO_REAL.

@PN/DP und @ducati

Ich hoffe das hilft mal auf die schnelle. Wenn es noch Fragen gibt, beantworte ich sie gerne.
 
Zuletzt bearbeitet:

escride1

Well-known member
Beiträge
912
Punkte Reaktionen
191
Du hast Deinen Code in #12 geändert und nur noch eine einzige Schleife in dem Ausschnitt.
Diese kann aufgrund Deines kleinen Strings ja nur 40 Durchgänge haben.
Dies bringt die CPU also eigentlich nicht in eine Zykluszeitüberschreitung, das macht selbst die kleinste 1200er mit.

Was aber geschieht mit #CommandBufferLoopIndex? Ist da noch eine Schleife die außerhalb des Ausschnitts liegt? Ist da nochmal was verschachtelt?

@JesperMP hat hiernach gefragt:
Wenn die CPU stoppt mit OB80, welche Werte stehen dann in diese Variabeln ?
Kannst Du das mitteilen oder eben den gesamten Baustein veröffentlichen?
 
Oben