TC3; Zykluszeit - Problem -> Fehlersuche - Aber wie?

mgl

Level-2
Beiträge
69
Reaktionspunkte
8
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Zusammen,

ich habe eine Warnmeldung dessen Ursache vermutlich eine Zykluszeitüberschreitung ist.

'I/O Idle Task' (340): CTask::CycleTask - unexpected result 0x98110719 returned from pDelayUntilNextActivate.

Das Diagramm vom I/O Idle Task sieht in der Online-Ansicht aus wie ein Signal von einem Sägezahn-Geber und der Überschreitungszähler zählt unaufhörlich nach oben.

Wie kann ich herausfinden wieso die Zykluszeit überschritten wird. Im Grunde liegt es an einem FB. Wenn ich den komplett auskommentiere tut es, wenn ich nur Teile auskommentiere, komme ich auf keinen grünen Zweig. (Endlos- bzw.) Schleifen habe ich im Baustein keine programmiert. Ich vermute nun, dass ich irgendwo aus versehen eine Rekursion drin habe.

Welche Mittel kann ich noch zur Fehlersuche einsetzen?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was mich an der Sache stutzig macht ist, warum der I/O Idle Task aussteigt. Dem ist eigentlich kein Programm zugewiesen und sollte es meine ich auch nicht.
Nachtrag: Ich muss mich korrigieren, das kann in bestimmten Situationen passieren. Habe gerade hier etwas über diese Task gelesen. Habt Ihr eventuell viel azyklische Kommunikation oder eine leistungsschwache CPU?
 
Zuletzt bearbeitet:
Welche Mittel kann ich noch zur Fehlersuche einsetzen?
Also ich denke, das Auskommentieren einzelner Programmteile ist schon der beste Weg.
Ich folge da immer der 1/2 Regel, das geht am schnellsten, um die verursachende Zeile zu finden. Das heißt, Du kommentierst erst mal genau die Hälfte aus. Von der verursachenden Hälfte dann wieder die Hälfte und so weiter.

Ich würde aber auch mal darüber nachdenken, ob der PLC-Task so schnell sein muss, wie der ist und mindestens für die Fehlersuche mal hochsetzen.
Vielleicht ist das ein Buffer-Überlauf bei der IO-Aktualisierung, weshalb der IO-Idle-Task so aussieht.

Eventuell musst Du auch mal die Task-Priorisierung überprüfen und auf die Default-Reihenfolge einstellen. Wie es aussehen muss, kannst Du Dir in einem neu erzeugten Projekt ansehen.
 
Habt Ihr eventuell viel azyklische Kommunikation oder eine leistungsschwache CPU?

Kurz zu meinem Hintergrund:
Ich komme eigentlich aus der SIemens-Ecke und soll für Beckhoff (wo ich jetzt seit ca. 2 Wochen dran bin) einen Softwarestandart entwickeln.

Das heißt ich habe eigentlich noch fast nichts. Ich habe angefahren einen FB_General zu bauen, der mir die Uhrzeit, Datum, Ticks, Blinker usw. zur Verfügung stellt, Einen einfachen Betriebsartenbaustein, der u.a. Fehlerbits auswertet und dementsprechend die Betriebsart setzt und zurücksetzt. Und nun angefangen einen Aktor-Baustein (Zylinder) zu bauen, und an dem hängt es momentan. Hardware habe ich soweit noch keine.

Ich verwende jedoch im Aktorbaustein die AT %I*, die ich dann auf nicht existierende Ein- / Ausgänge gemappt habe. Also IX0.0 QX0.0 und so. Ich vermute dass dann hier der Hund begraben ist.

Ich habe zum Testen momentan ein CP2600 - 0000 Panel zum testen meiner Software.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich verwende jedoch im Aktorbaustein die AT %I*, die ich dann auf nicht existierende Ein- / Ausgänge gemappt habe. Also I0.0 Q0.0 und so. Ich vermute dass dann hier der Hund begraben ist.
Das glaube ich eher nicht, das ist Standard. Diese Möglichkeit überhaupt zu haben, ist einer der Gründe, warum ich nur noch Beckhoff-Steuerungen nutze.
Wenn Du auf nicht existierende EA gemappt hast, über was für einen Knoten gehst Du da. Nomalerweise wird nicht existierende EA auch nicht aktualisiert, sondern ignoriert. Dass kann aber von der Art des EA-Gerätes abhängig sein.

Kleiner Hack von mir: Die Ergebnisse des FB_General würde ich nicht über die Bausteinschnittstelle verteilen. Da bist Du nur am mappen. Erzeuge eine Basisklasse mit VAR_STAT von der der FB_General erbt. Dann haben alle anderen Bausteine, die von der gleichen Basisklasse erben automatisch Zugriff auf die Variablen, die in VAR_STAT deklariert wurden. Du sparst Dir einen Haufen Arbeit.

Code:
FUNCTION_BLOCK ABSTRACT FB_SystemBase
VAR_STAT
    stSystemState                        : ST_SystemState;                        (* System state *)
END_VAR

Code:
TYPE ST_SystemState :
STRUCT
    bBusStateFullOn            : BOOL;                                                        (* Bus state full on *)
    bBusStateRdy            : BOOL;                                                        (* Bus state ready *)
    bCtrlV24Rdy                : BOOL;                                                        (* Control voltage 24V ready *)
    bCtrlV230400Rdy            : BOOL;                                                        (* Control voltage 230V / 400V ready *)
    bLoadV48Rdy                : BOOL;                                                        (* Load voltage for all servo drives ready *)
 
 
    USW....
 
        (* sync pulses *)
    bFlash1Hz                : BOOL;                                                        (* 500ms on, 500ms off *)
    bFlash2Hz_2to3            : BOOL;                                                        (* 300ms on, 200ms off *)
    bFlash1Hz_8to2            : BOOL;                                                        (* 200ms on, 800ms off *)
 
    USW....
 
 
END_STRUCT
END_TYPE

Und Fehlerbits.... Mein Tipp: Schau Dir mal den TC-Eventlogger an. Lass das mit den Fehlerbits.

Wenn Du die Stärken des TwinCAT-Systems nutzen willst, musst Du erst mal alles vergessen, was in der Siemens-Welt üblich ist. Du kannst zwar TwinCAT Siemens-Like programmieren. Empfehlen würde ich es nicht. Man kann vieles einfacher gestalten. Dann kannst Du später Dein Siemens-Wissen zurückholen und schauen, was Du davon noch brauchst.

Das sage ich als Ex-Siemensianer.
 
Zuletzt bearbeitet:
Kleiner Hack von mir: Die Ergebnisse des FB_General würde ich nicht über die Bausteinschnittstelle verteilen. Da bist Du nur am mappen. Erzeuge eine Basisklasse mit VAR_STAT von der der FB_General erbt. Dann haben alle anderen Bausteine, die von der gleichen Basisklasse erben automatisch Zugriff auf die Variablen, die in VAR_STAT deklariert wurden. Du sparst Dir einen Haufen Arbeit.

Um beim Aufruf der Klasse nicht immer die Selben Methoden die die VAR_STAT Variablen beschreiben, wiederholt auszuführen müsste man einen Instanzen Zähler implementiert,

so dass diese nur beim Aufruf der ersten Instanz ausgeführt werden. Nun ist das Problem, dass jemand quasi in anderen Instanzen der Klasse die VAR_STAT Variablen "verpfuschen" kann und alle folgenden Instanzen dann Falsche Werte erhalten würden.

Das ist dann halt zwecks Daten-Kapselung nicht optimal. Vielleicht sehe ich das auch zu eng.
 
Um beim Aufruf der Klasse nicht immer die Selben Methoden die die VAR_STAT Variablen beschreiben, wiederholt auszuführen müsste man einen Instanzen Zähler implementiert,
Die Basisklasse soll gar nicht aufgerufen werden. Die soll auch nicht die Variablen beschreiben. Die kapselt lediglich die Daten. Mehr nicht. Die Daten werden in der Vererbten Klasse FB_General beschrieben. Alle anderen Erben sollen / dürfen nur lesend zugreifen.
so dass diese nur beim Aufruf der ersten Instanz ausgeführt werden. Nun ist das Problem, dass jemand quasi in anderen Instanzen der Klasse die VAR_STAT Variablen "verpfuschen" kann und alle folgenden Instanzen dann Falsche Werte erhalten würden.
Das wäre ein Programmierfehler und er ist an jeder Stelle, egal wie die Architektur aussieht, möglich. Die Gefahr besteht halt immer, wenn sich viele Teilnehmer (Instanzen) an der gleichen Variablen orientieren. Ist beim klassischen Modell nicht anders. Man muss halt ein paar Konventionen einführen.

Mir drängt sich gerade eine Frage auf: Ist das Konzept der Vererbung schon geläufig? Das würde ich bei einen Siemens-Welt-Programmierer jetzt nicht unbedingt voraussetzen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Die Basisklasse soll gar nicht aufgerufen werden. Die soll auch nicht die Variablen beschreiben. Die kapselt lediglich die Daten. Mehr nicht. Die Daten werden in der Vererbten Klasse FB_General beschrieben. Alle anderen Erben sollen / dürfen nur lesend zugreifen.

Ok. Ich denke jetzt habe ich es verstanden. Wobei mir meine Lösung etwas besser gefallen würde, da der FB_General dann nur noch aufgerufen wird wenn man ihn wirklich brauch und ihn nicht "extra" aufrufen müsste. An der Kapselung ändert sich ja soweit nichts.

Code:
FUNCTION_BLOCK ABSTRACT FB_GeneralData

VAR_STAT
    bBlink0Hz5    : BOOL;
END_VAR


Code:
FUNCTION_BLOCK FB_General EXTENDS FB_GeneralData
// -> Beschreibt die VAR_STAT Variablen von FB_GeneralData

VAR
    fbBlink0Hz5 : BLINK;
END_VAR

fbBlink0Hz5(Enable := TRUE, TIMELOW := T#1S, TIMEHIGH := T#1S, OUT => SUPER^.bBlink0Hz5);


Code:
FUNCTION_BLOCK ABSTRACT FB_ActuatorBasic EXTENDS FB_GeneralData IMPLEMENTS I_Cmd, I_ActuatorBasic_ExOp, I_ActuatorBasic_Settings

Code:
FUNCTION_BLOCK FB_ActuatorCylinder EXTENDS FB_ActuatorBasic IMPLEMENTS I_ActuatorCylinder_ExOp

VAR
    bFault : BOOL := TRUE; // Test Variable
    bSignalLamp : BOOL;
END_VAR

bSignalLamp := bFault AND SUPER^.bBlink0Hz5;

So würde das dann bei mir aussehen. Der Vollständigkeit halber,
Code:
INTERFACE I_ActuatorBasic_ExOp EXTENDS I_Cmd, I_ActuatorBasic_Settings

INTERFACE I_ActuatorCylinder_ExOp EXTENDS I_ActuatorBasic_ExOp

INTERFACE I_ProcessCmd EXTENDS I_Cmd

Zurück zum Ursprünglichen Problem: Ich implementiere ja mehrmals das selbe Interface. Also I_CMD ist Beispielsweise im I_ActuatorBasic_ExOp und später dann auch im I_ProcessCmd ... Ich möchte eben Teilmengen der Schnittstelle an entsprechende Schnittstellen weitergeben. I_ProcessCmd wäre für den Automatik-Betrieb und I_ActuatorBasic_ExOp für den Hand / Externen Betrieb, der mehr Informationen benötigt um z.B. Statutsinformationen zur Fehler-Ursachen suche oder Einstellungen zu optimieren.

Die I_CMD ist dabei eine Schnittmenge der Schnittstelle. Funktioniert das so oder wäre das vielleicht der Grund für die Taktzeitüberschreitung?

Mir drängt sich gerade eine Frage auf: Ist das Konzept der Vererbung schon geläufig? Das würde ich bei einen Siemens-Welt-Programmierer jetzt nicht unbedingt voraussetzen.

Na ja, ich habe mal Java gelernt. Aber über das Schul-Java ging es halt nicht hinaus.

Das heißt verstanden habe ich das denke schon. Nur mir fehlt die Praxis das auch anzuwenden.
 
Zuletzt bearbeitet:
Ja, genau. Den Super-Zeiger kannst Du übrigens weglassen. Die Variable ist in der Vererbungshierarchie auch direkt zugreifbar.

Code:
FUNCTION_BLOCK ABSTRACT FB_ActuatorBasic EXTENDS FB_GeneralData IMPLEMENTS I_Cmd, I_ActuatorBasic_ExOp, I_ActuatorBasic_Settings
Das ist mir neu. Habe ich da was nicht mitbekommen? Bisher konnte man nur ein Interface implementieren... Da muss ich mich mal wieder weiterbilden.
 
Dass auch der I/O Idle Task auch Überschreitungen hat, wenn der PLC Task Überschreitungen hat, war bei mir genauso.
Das problem wenn der I/O task Überschreitungen hat, setzt er alle Ausgänge zurück.

Hast du irgendwo Divisionen durch null oder so?
hier wird eine Fehlerbehandlung gestartet die die Zykluszeit vom PLC Task erheblich anhebt.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich verwende jedoch im Aktorbaustein die AT %I*, die ich dann auf nicht existierende Ein- / Ausgänge gemappt habe. Also IX0.0 QX0.0 und so. Ich vermute dass dann hier der Hund begraben ist.
Das verstehe ich jetzt nicht so ganz.
Bei Beckhoff erstellt man Variablen die mit Hardware verknüpft werden mit AT %I* oder AT %Q*, nachdem dann das Projekt erfolgreich übersetzt wurde kann man Kanäle im E/A-Knoten mit diesen Variablen verknüpfen, aber da muss man keine Adressen mehr angeben.
Wo gibst Du denn noch Adressen an?
 
Hast du irgendwo Divisionen durch null oder so?
hier wird eine Fehlerbehandlung gestartet die die Zykluszeit vom PLC Task erheblich anhebt.
Vielleicht gibt es da eine Option bei der das so ist, aber bei einer Division by 0 schmeißt TwinCAT eigentlich eine Exception und das war es dann.
Oder meinst Du mit Fehlerbehandlung die POU für implizite Prüfung, in dem Fall Divisionsprüfung?
 
Das verstehe ich jetzt nicht so ganz.
Bei Beckhoff erstellt man Variablen die mit Hardware verknüpft werden mit AT %I* oder AT %Q*, nachdem dann das Projekt erfolgreich übersetzt wurde kann man Kanäle im E/A-Knoten mit diesen Variablen verknüpfen, aber da muss man keine Adressen mehr angeben.
Wo gibst Du denn noch Adressen an?
Ich hatte das tatsächlich so verstanden, das die Variablen im System-Manager direkt auf die Hardware verlinkt werden und dabei (früheres Verhalten) Hardware-Adressen, wie beim TC2 oder frühen TC3 Versionen automatisch erzeugt werden.
Nach Deinem Einwand und auf Grund der Tatsache, dass der TE noch keine TwinCAT-Berührungen hatte, bin ich jetzt auch stutzig geworden.

@mgl: Was sind das für Adressen, die Du da erwähnst: I0.0 und Q0.0. Ist das was abstraktes oder hast Du das irgendwo wirklich so in die Deklaration geschrieben? IO-Anbindung fällt nämlich unter den Part der Siemens-Programmierung, den Du schnell vergessen solltest.
 
@mgl: Was sind das für Adressen, die Du da erwähnst: I0.0 und Q0.0. Ist das was abstraktes oder hast Du das irgendwo wirklich so in die Deklaration geschrieben? IO-Anbindung fällt nämlich unter den Part der Siemens-Programmierung, den Du schnell vergessen solltest.

Ich habe ja keine Hardware außer dem Panel-PC.

Ich habe die Hardware auf folgende weise gemappt:
Code:
FUNCTION_BLOCK FB_ActuatorCylinder EXTENDS FB_ActuatorBasic IMPLEMENTS I_ActuatorCylinder_ExOp

VAR_INPUT
	i_bInPos1 AT %I* 	: BOOL;						// Feedback Cylinder in position 1
	i_bInPos2 AT %I* 	: BOOL;						// Feedback Cylinder in position 2
END_VAR

VAR_OUTPUT
	o_bOutPos1 AT %Q* 	: BOOL;						// Set output Cylinder to position 1
	o_bOutPos2 AT %Q*	: BOOL;						// Set output Cylinder to position 2	
END_VAR

GVL_HardwareMapping
Code:
{attribute 'qualified_only'}
VAR_CONFIG
	Main.fbActuatorCylinder.i_bInPos1 AT %IX0.0 : BOOL;
	Main.fbActuatorCylinder.i_bInPos2 AT %IX0.1 : BOOL;
	
	Main.fbActuatorCylinder.o_bOutPos1 AT %QX0.0 : BOOL;
	Main.fbActuatorCylinder.o_bOutPos2 AT %QX0.1 : BOOL;
END_VAR

Muss ich das Eventuell als "BIT" deklarieren, da ein BOOL ja eigentlich ein BYTE-groß ist?

----

Daran liegt der Fehler aber nicht. Ich schaue mal weiter. Ich denke ich bin gerade auf einem guten Weg. Es liegt möglicherweise doch nicht direkt am FB_ActuatorCylinder
 
VAR_INPUT und VAR_OUTPUT sind nicht die richtigen Deklarationsbereiche, das ist die programminterne Bausteinschnittstelle. Hardware-Variablen deklarierst Du einfach im Bereich VAR / END_VAR.
Und den Bereich VAR_CONFIG kannst Du gleich wieder löschen. Die Verbindung auf die reale Hardware macht der System-Manager, Knotenpunkt EA-Geräte.

Hardwareanbindung über eine GVL macht Dir das Leben nur unnötig schwer. Das ist fast immer unnötig. Ich habe es überhaupt gar nicht erst angefangen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Muss ich das Eventuell als "BIT" deklarieren, da ein BOOL ja eigentlich ein BYTE-groß ist?
Nur am Rande bemerkt, den Datentyp BIT kann man nur bei Strukturen und, was mir neu war, in FBs verwenden, allerdings sollte die Verwendung aus geschwindigkeitsgründen vermieden werden.
 
Nur am Rande bemerkt, den Datentyp BIT kann man nur bei Strukturen und, was mir neu war, in FBs verwenden, allerdings sollte die Verwendung aus geschwindigkeitsgründen vermieden werden.

Mir ist der Datentyp BIT beim Herumprobieren mit der Hardware aufgefallen. Ich hatte Testweise mal einen Ethercat-Koppler mit IO-Klemmen konfiguriert und die jeweiligen Eingänge / Ausgänge waren dann als BIT deklariert. Daher die Überlegung. Da ich aber momentan keine Ein-/ Ausgänge konfiguriert habe, habe ich BOOL verwendet da mir das mit der Performance bewusst ist.
 
Mir ist der Datentyp BIT beim Herumprobieren mit der Hardware aufgefallen. Ich hatte Testweise mal einen Ethercat-Koppler mit IO-Klemmen konfiguriert und die jeweiligen Eingänge / Ausgänge waren dann als BIT deklariert. Daher die Überlegung.
Ja, im E/A-Knoten steht BIT, aber gemappt werden Sie auf ein Bool. Man kann auch mehrere I/Os einer Klemme auf ein BYTE oder WORD mappen.
 
Zurück
Oben