Sporadischer Laufzeitfehler wenn Interfaces und Pointer gemischt werden

wonderfulworld

Level-1
Beiträge
114
Reaktionspunkte
10
Zuviel Werbung?
-> Hier kostenlos registrieren
Hi ich spiel mal wieder mit CoDeSys V3 (alias SoMachine Motion) rum und da kann ich mir momentan folgende Beobachtung nicht erklären.

Ich hab ein Interface definert:

Code:
INTERFACE IElement :

 METHOD getOtherElement : IElement
VAR_INPUT
    index : UINT;
END_VAR

METHOD getTrue: BOOL
 VAR_INPUT
END_VAR

Davon hab ich ein FB Element erstellt, das sich von diesem IElement ableitet. Die implementierte Methode sieht so aus:

Code:
METHOD getOtherElement : IElement
VAR_INPUT
    index    : UINT;
END_VAR

VAR
    element : Element;
    test : BOOL;
END_VAR

element := THIS[index];
test := __QUERYINTERFACE(element, getOtherElement);
getOtherElement.getTrue(); (*der Aufruf funktioniert immer, hier gibt es keinen Laufzeitfehler*)


METHOD getTrue: BOOL
 VAR_INPUT
END_VAR

getTrue := TRUE;

Dann hab ich ein Array aus Element erstellt und so probiert auf die Elemente zuzugreifen.
Code:
VAR
   elements : ARRAY[0..10];
   iElement : IElement;
   newiElement : REFERENCE TO IElement; (*eigentlich müsste hier ja IElement langen, aber der Compiler schimpft wenn es nicht da steht*)
END_VAR
iElement := elements[0];
newiElement REF= iElement.getOtherElement(1);
newiElement.getTrue(); (*hier gibt es manchmal einen Laufzeitfehler*)

Das seltsame ist jetzt, ich dass ich die Methode iElement.getOhterElement(index) mit einem gültigen Index immer aufrufen kann und keinen Laufzeitfehler bekomme, obwohl ich in der Methode getOtherElement das Element dereferenziere. Der Laufzeitfehler kommt erst, wenn ich im Programm newiElement.getTrue() aufrufe. Aber dort kommt es auch nicht immer zu einem Laufzeitfehler. Wenn ich mit dem Debugger durchsteppe, kann es sein das erst beim dritten oder vierten Aufruf der Fehler kommt. Könnt ihr mir sagen warum das so ist? Ich kann mir das garnicht erklären.

P.S. Dass das was ich hier beschrieben habe, ist nicht sauber und wahrscheinlich auch nicht zu empfehlen. (Element weiß ja nicht wieviele Teile sich im Array befinden.) Ich wollte eigentlich nur schauen ob es geht. Und das es jetzt manchmal geht und manchmal nicht irritiert mich doch ein bisschen, zumal ich eigentlich keine Erklärung habe warum es nicht geht. Meiner Meinung nach sollte es immer gehen.
 
Was willst du hiermit erreichen:

Code:
THIS[index];

THIS ist ein Pointer auf die eigene Klasse. Man darf auf Pointer mit index zugreifen. Der Compiler wird das deswegen nicht anmeckern.
Aber das solltest du nicht so machen, wenn der übergebene Index zu gross ist, oder das Element am Ende des Array, dann wird der Zugriff
daneben gehen.

Wenn du das so definierst:
Code:
element : Element;

und dann diese Zuweisung ausführst:

Code:
element := THIS[index];

dann kopierst du den Inhalt aus dem Array in die lokale Variable. Bist du dir sicher dass du das willst?
Die Variable ist eine lokale Variable der Methode? Dann befindet sich diese Variable flüchtig auf dem Stack. Nach Beendigung der Methode
wird der Speicher freigegeben.
Das hier sollte eigentlich nicht notwendig sein:

Code:
test := __QUERYINTERFACE(element, getOtherElement);
so sollte es reichen:
Code:
getOtherElement := element;

Aber anschliessend verweist die Interface-Referenz auf den Stack, also auf den flüchtigen Speicher in der Methode.
Die Referenz zeigt nicht in das Array, wie du wahrscheinlich erwartest. Wenn nun ausserhalb der Methode der Aufruf erfolgt:
Code:
newiElement.getTrue(); (*hier gibt es manchmal einen Laufzeitfehler*)
Dann kann das gut gehen, aber wenn der lokale Speicher der Methode schon wieder verwendet worden ist (wenn du durchsteppst wird das
immer der Fall sein) dann wirds krachen.

Die Deklaration von newiElement als REFERENCE sollte nicht nötig sein, das folgende sollte in jedem Fall funktionieren:
Code:
   newiElement : IElement; (*eigentlich müsste hier ja IElement langen, aber der Compiler schimpft wenn es nicht da steht*)
   newiElement := iElement.getOtherElement(1);

Was für einen Fehler meldet denn der Compiler?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Was willst du hiermit erreichen:

Code:
THIS[index];

Ich wollte einen FB schreiben, der ein beliebig großes Array bearbeiten kann (Das geht soweit ich weiß nur mit Pointern). Die Arrayelemente des Arrays sollen dabei Objekte von einem konkreten FB sein, der ein Interface implementiert, dass dem FB bestimmt Methoden zur Bearbeitung bereitstellt. Ich habe jetzt viel rumprobiert und die einzige Möglichkeit, die ich gesehen habe sowas zu bewerkstelligen, war über das Element. Das kennt seine Implementierung und damit auch seine Größe und seine Adresse. Zudem kommt das nächste Arrayelement genau nach ihm. Dass das nicht besonders sicher ist, war mir klar, aber wenn mein FB die Größe des Arrays und ein Interface bestizt, dass auf das erste Arrayelement verweißt, und ich die Methode getOtherElement() nur für diesen Zweck benutze, ist das Risiko vielleicht doch überschaubar. (Allerdings glaub ich nicht, dass mir das meine Kollegen erlauben werden. :) Deshalb wird es wahrscheinlich nur Spielerei bleiben. )

Aber anschliessend verweist die Interface-Referenz auf den Stack, also auf den flüchtigen Speicher in der Methode.
OK, das ist die Erklärung. Das ich da nicht selber drauf gekommen bin. :-(
So tut es:
Code:
METHOD getOtherElement : IElement
VAR_INPUT
    index    : UINT;
END_VAR

VAR
    element : REFERENCE TO Element;
END_VAR

element REF= THIS[index];
getOtherElement := element;

Was für einen Fehler meldet denn der Compiler?

Der Fehler lässt sich nicht mehr reproduzieren, tut mir leid. Vielleicht lang es auch an etwas anderem und ich habe den Fehler missverstanden.

Vielen Dank für die ausführliche Antwort
wonderfulworld
 
Zuletzt bearbeitet:
@Werner29

Erinnerst du dich an unsere letzte kleine Diskussion???
Genau davor habe ich echt Angst im Bereich der Automatisierungstechnik.
Und wonderfulworld scheint doch recht genau zu wissen, was er da macht, aber es gibt auch Kollegen mit wesentlich mehr Selbstbewußtsein und gleichzeitig weniger fundiertem Wissen. Vielleicht sollte man uns SPS-Programmierern (mich eingaschlossen) einfach nicht alle Möglichkeiten in die Hand geben?
 
Ja mei...
wonderfulworld hat ein bisschen rumgespielt und einen Fehler gemacht. Das scheint mir völlig normal zu sein.
Der eigentliche Fehler in dem Programm hat mit Objektorientierung aber nur am Rande zu tun.
Ich habe auch schon ohne Objektorientierung gesehen, dass jemand einen Pointer auf eine lokale Variable in einer Funktion gelegt hat.
Klar kann man daraus die Lehre ziehen, Pointer sind böse und schwierig zu beherrschen und man sollte sie verbieten.
Das ist eine durchaus nachvollziehbare Position.

Man kann den Sprachumfang so einschränken, dass überhaupt keine Laufzeitfehler möglich sind.
ZB muss man das für Safety-Anwendungen tun.

Bei CODESYS ist die Philosophie eine andere. Wir erlauben dem Programmierer vieles. Und damit auch viele Möglichkeiten für Fehler.
Und das betrifft nicht nur die Objektorientierung. (siehe Pointer).

Mit dem Vorwurf zu innovativ zu sein, kann ich im übrigen leben. Was wäre für eine unabhängige Softwarefirma wie uns denn eigentlich die Alternative?
Wenn wir Siemens oder Rockwell oder Mitsubishi hinterherprogrammieren und keinen Schritt nach vorne wagen, wieso sollte sich dann irgendjemand für unsere Software interessieren?
Wenn du als Programmierer weniger Möglichkeiten haben willst als mit CODESYS, dann ist es einfach für dich ein Produkt zu finden.
Aber wer mehr Möglichkeiten haben will als mit Siemens, der muss zu uns kommen.
 
Let's agree to disagree. Ein schöner Beitrag zur Diskussionskultur in diesem Forum.
Wir haben im übrigen jede Menge anderer Features in CODESYS V3. Lass dich von der Objektorientierung nicht abschrecken.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@Werner29

Erinnerst du dich an unsere letzte kleine Diskussion???
Genau davor habe ich echt Angst im Bereich der Automatisierungstechnik.
Und wonderfulworld scheint doch recht genau zu wissen, was er da macht, aber es gibt auch Kollegen mit wesentlich mehr Selbstbewußtsein und gleichzeitig weniger fundiertem Wissen. Vielleicht sollte man uns SPS-Programmierern (mich eingaschlossen) einfach nicht alle Möglichkeiten in die Hand geben?

Hallo Ralle,

darf ich mich in die Diskussion mit einklinken? (Auch wenn es jetzt total OffTopic ist. Sorry) Ich kann dich sehr gut verstehen. Mir geht es manchmal auch so, dass ich angst bekomme, wenn ich die neuen (Fehler-)Möglichkeiten sehe. Aber auf der anderen Seite ist es halt auch so, dass wir bei uns in der Firma viele Stellen in unserm Template haben, die Redundanzen aufweisen, unflexible sind und deshalb oft sehr aufwendig zum Warten sind. Es ist häufig auch schwer einen möglichst universellen Quellcode zu schreiben. Immer mehr, was früher die Mechanik erledigt hat, macht heute Software, zudem werden die Anforderungen an Flexibilität immer größer und die Inbetriebnahmezeiten immer kleiner. Deshalb brauchen wir einfach Hilfsmittel, mit denen wir unseren Code flexibler und einfacher wartbar machen können und die OOP, die neuen Referenzen etc. sind solche Mittel. Das Problem was ich jetzt eher sehe, ist das wir nicht wissen wie man diese neuen Sachen einsetzt. Es gibt keine Erfahrung/Literatur für OOP-SPS-Programmierung, allenfalls werden die Grundlagen beschrieben. Vielleicht gibt es auch das ein oder andere OOP-Element, was man besser nicht einsetzt, oder das von CODESYS anders implementiert werden sollte. Aber wie soll man das herausfinden, wenn man nicht damit arbeitet.
Gruß wonderfulworld
 
Zuletzt bearbeitet:
Hi,

Wir (und unsere Kunden) bieten natürlich auch Trainings an.
Ich habe schon an anderer Stelle darauf hingewiesen: Man darf nicht vergessen, mit CODESYS schreibt man nicht nur Anwendungen, sondern auch Bibliotheken.
Ich habe schon vor ein paar Jahren für das IEEE-Magazine einen Artikel geschrieben zu dem Thema und darin folgendes Beispiel vorgelegt.
Leider wurde dieses Beispiel damals von meinen Peer-Reviewern bemängelt, die offensichtlich gar nichts verstanden haben.
Es geht darum sicherzustellen, dass sich eine bestimmte Klasse von FBs gleich verhält, ohne dass man Code kopieren muss.
Der Artikel ist leider in englisch, aber das sollte kein Problem sein:

The following example illustrates one possible usage of the new OOP functionalities. The task is simple: We have identified that function blocks of a certain class have an identical behaviour; they start execution on a rising edge of an input, run asynchronously and when finished, successfully produce a rising edge at an output.
Now, we want to implement this behaviour in a function block, so that derived function blocks may use this implementation.

The following screenshot shows the interface of the base function block ETrig:

etrigItf.png
Figure 1. Base function block for behaviour model

Besides the input and output variables, the base function block offers some protected methods (i.e.: methods that can only be accessed from the function block itself and from derived function blocks):

EtrigMethods.png
Figure 2: protected methods of ETrig

These protected methods are only called from within the body of the function block itself.

The following code fragment shows parts of the base implementation of the behaviour model:

ImplEtrig.png
Figure 3: implementation of the behaviour model

Now we can easily create function blocks which make use of this base implementation. All we have to do is to override and implement the protected methods of the base implementation. The behaviour model will work identically for all of them.
An example for function blocks which are derived from this model is file accessing. Without observing the code of these function blocks, let us look at a code example, in which they are used:

usageEtrig.png
Figure 4: usage of File Access function blocks, the inherited inputs and outputs are highlighted

The exciting thing in this example is the fact that the usage of the function blocks did not change at all. The new functionality is only included within the internal interface of the function block. The external interface stays the same. Thus, a redesign of the function blocks is possible without changing the projects referring to it.
In other words: The implementer of a library can use all new functionality without forcing the user of a library to change his projects or his habitual way of programming.
 
Hi,
Wir (und unsere Kunden) bieten natürlich auch Trainings an.
Ich hab auch bei einem solchen Training teilgenommen und das Training war auch gut. Ich weiß jetzt wie Vererbung, Schnittstellen und Methoden in CODESYS implementiert sind. Hier im Forum hast du mir dann noch erklärt wie Eigenschaften funktionieren. Danke dafür. Aber die eigentlichen Fragen die ich hatte, sind eher die, wie schaffen wir als Maschinenbauer unser Template so umzustricken, dass wir die Vorteile(Wiederverwendbarkeit, Testbarkeit...) nutzen und die Nachteile(Unverständlichkeit für OOP-Unerfahrene, schwierigeres Überwachen von Funktionen... ) vermeiden. Und da wird es schwerer. Für konventionelle Programmiersprachen kann ich auf unzählige Bücher über Software-Design-Pattern, Antipattern , Smells... zurückgreifen, bei der SPS-OOP gibt es diese Bücher noch nicht. Da fehlt einfach noch die Erfahrung. Das konnte unser Schulungsleiter uns leider auch nicht sagen.
Das ist auch so in Ordnung. In zwei drei Jahren werden wir wissen, was wir heute alles anders machen sollen. :) So ging es uns ja auch mit anderen neuen Sachen.
Gruß wonderfulworld
 
Zurück
Oben