Codesys Objektorientiertes Programmieren

toto45

Level-1
Beiträge
65
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,
ich arbeite mich gerade ins objektorientierte Programmieren ein.

Ich möchte z.B. ein FB fbAuto mit verschiedenen Methoden einfügen.
Im FB Auto deklariere ich:

VAR
xAutoFahrberereit : BOOL;
iAnzahlTueren : INT;
rLeistung : REAL;
END_VAR


Im MAIN-Programm möchte ich darauf zugreifen.
Ich lege als globale Variablen an
Autos : Array[0..50] of fbAuto;


Im Main Programm möchte ich nun die fahrbereiten Autos ermitteln:

for i:=0 to 50 do

IF Autos.xAutoFahrbereit then
iAnzahlfahrbereiterAutos := iAnzahlfahrbereiterAutos +1;
END_IF
END_FOR



Man kann jedoch nur auf die MEthoden von Autos zugreifen. Nicht aber auf die Variablen. Ich habe gelesen das es Eigenschaften (Properties) gibt. Müsste ich dann für jede Variable auf die ich von außen zugreifen möchte eine Propertie
bzw. eine Methode schreiben?
 
Müsste ich dann für jede Variable auf die ich von außen zugreifen möchte eine Propertie
bzw. eine Methode schreiben?
Das ist im Hochsprachenbereich sicher guter OOP-Stil, für Automationsprogramme aber weniger geeignet. Dort kann man bei laufender Maschine meistens keine Breakpoints setzen, sondern ist auf das Beobachten von Variablenwerten bei laufendem Programm angewiesen. Die Ergebnisse von Property Get-Methoden werden allerdings temporär über den Stack zurückgegeben und lassen sich nicht beobachten. Ich bevorzuge deshalb die Standard-IO-Schnittstelle des FB's mit ihren statischen VAR_INPUT/VAR_OUTPUT-Variablen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo toto45,

1. Variablen in VAR .. END_VAR sind grundsätzlich nicht von aussen zugreifbar. Deswegen wie schon von StructuredTrash geschrieben VAR_INPUT oder VAR_OUTPUT-Variablen verwenden.
2. Properties werden spätestens dann notwendig, sobald man mit Interfaces arbeitet. Properties sind aber Code und per default werden diese nicht gemonitort, denn wenn der Code beim Monitoring einen Seiteneffekt produziert dann könnte das sehr unangenehm sein.
Wenn man sich sicher ist, dass durch die Ausführung des Properties kein Unsinn passieren kann, dann kann man das Monitoring mit folgendem Attribut einschalten:

{attribute 'monitoring':= 'call'}
PROPERTY Dingsbums: INT


Bernhard
 
Properties werden spätestens dann notwendig, sobald man mit Interfaces arbeitet. Properties sind aber Code und per default werden diese nicht gemonitort, denn wenn der Code beim Monitoring einen Seiteneffekt produziert dann könnte das sehr unangenehm sein.

Tja, und an der Stelle kann ich eine gewisse Enttäuschung über die OOP-Erweiterungen der V3 nicht verbergen. Der IEC-FB als Basis aller OOP geht mit seinen unterschiedlichen Zugriffsrechten auf seine Variablen (VAR, VAR_INPUT, VAR_OUTPUT) ja schon einen etwas anderen Weg als die Hochsprachen mit ihren von Strukturen abgeleiteten Objekten, und dieser Weg ist in meinen Augen für Automationsaufgaben auch hervorragend geeignet. Ich hätte mir gewünscht, dass dieser Weg bei der Einführung weiterer OOP-Mechanismen fortgesetzt würde. Stattdessen wurden die Erweiterungen nahezu unverändert aus der Hochsprachenwelt übernommen. Wirklich brauchbar ist für mich nur die Vererbung.
 
Tja, und an der Stelle kann ich eine gewisse Enttäuschung über die OOP-Erweiterungen der V3 nicht verbergen. Der IEC-FB als Basis aller OOP geht mit seinen unterschiedlichen Zugriffsrechten auf seine Variablen (VAR, VAR_INPUT, VAR_OUTPUT) ja schon einen etwas anderen Weg als die Hochsprachen mit ihren von Strukturen abgeleiteten Objekten, und dieser Weg ist in meinen Augen für Automationsaufgaben auch hervorragend geeignet. Ich hätte mir gewünscht, dass dieser Weg bei der Einführung weiterer OOP-Mechanismen fortgesetzt würde. Stattdessen wurden die Erweiterungen nahezu unverändert aus der Hochsprachenwelt übernommen. Wirklich brauchbar ist für mich nur die Vererbung.

Was hättest du dir denn vorgestellt? Ich finde unsere Erweiterungen des FB sehr natürlich. Aber ich bin auch immer offen für Ideen.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ich hätte es besser gefunden, wenn Methoden und Properties ebenfalls statische Variablen hätten (oder alternativ haben könnten). Sozusagen eine Erweiterung der FB-Datenstruktur, die aber nur in den Methoden sichtbar ist, in denen sie deklariert ist.
 
Man kann jedoch nur auf die MEthoden von Autos zugreifen. Nicht aber auf die Variablen. Ich habe gelesen das es Eigenschaften (Properties) gibt. Müsste ich dann für jede Variable auf die ich von außen zugreifen möchte eine Propertie
bzw. eine Methode schreiben?
Ja, du müsstest für jede Variable ein Property anlegen.
Wie schon beschrieben, eigenet sich für POUs ohne Interfaces VAR_INPUT und VAR_OUTPUT meistens besser (es sei denn es muss beim Setzen / bei der Abfrage noch Code ausgeführt werden, z.B. eine Umrechnung).


Was hättest du dir denn vorgestellt? Ich finde unsere Erweiterungen des FB sehr natürlich. Aber ich bin auch immer offen für Ideen.
Ohne über genaue Auswirkungen nachzudenken:
In Interfaces soll man VAR_INPUT und VAR_OUTPUT angeben können, die die implementierende POU dann besitzen muss.


Ich hätte es besser gefunden, wenn Methoden und Properties ebenfalls statische Variablen hätten (oder alternativ haben könnten). Sozusagen eine Erweiterung der FB-Datenstruktur, die aber nur in den Methoden sichtbar ist, in denen sie deklariert ist.
Für Sonderfälle mit nur einer Instanz: Kennst du VAR_STAT?
 
Zuletzt bearbeitet:
Vorsicht: VAR_STAT ist eine global statische Variable, keine Instanzvariable.
Wir planen allerdings VAR_INST-Variablen für die nächste Version 3.5 SP 5. Sowas wird es aber nie für Inputs von Methoden geben.
Also die wird man weiterhin in eine Instanzvariable kopieren müssen, wenn es notwendig ist dass die erhalten bleiben.

Grundsätzlich gilt natürlich, Datenkapselung ist ein wesentliches OO-Prinzip. Man kann nicht sagen wir machen OO, aber ohne Datenkapselung.
Man arbeitet eben eher mit Methoden und Properties als mit INPUTS und OUTPUTS.
Das mag zunächst umständlicher wirken, aber nur dadurch kann man zur Laufzeit das eine Objekt gegen ein anderes austauschen,
weil es eben dieselbe Schnittstelle bietet.

>Ohne über genaue Auswirkungen nachzudenken:
>In Interfaces soll man VAR_INPUT und VAR_OUTPUT angeben können, die die implementierende POU dann besitzen muss.

Es ist tatsächlich technisch bedingt, dass das nicht geht. Wer C++ programmiert, der wird wissen, dass Mehrfachvererbung bei Daten Ärger bedeutet.
Ich will da nicht auf die Details eingehen, wen's interessiert der findet jede Menge Literatur dazu.
Modernere OO-Sprachen haben deswegen diese Mehrfachvererbung aufgegeben und stattdessen Interfaces eingeführt.
Und Interfaces dürfen eben nur Methoden definieren. Man kann daher jetzt von einem FB ableiten, aber beliebig viele Interfaces implementieren.
Datenzugriffe werden über Properties festgelegt.


 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo,

Sorry, wenn ich das Thread-Thema ein bisschen biege, aber ich habe das Gefühl hier genau die richtigen Ansprechpartner gefunden zu haben.
Also: Ich komme aus der "klassischen" SPS-Ecke und habe bisher nur wenige Berührungspunkte mit Hochsprachen gehabt.

Ich habe bisher viel in AWL, später in FUP programmiert. Dabei wurden mehrfach auftretende Aufgaben einfach in Funktionen gepackt und dann an der entsprechenden Stelle aufgerufen.

Nun versuche ich mich in V3.5 einzuarbeiten und stoße auf den Begriff Objektorientierung.
Soweit ich das verstanden habe, würde man nun eine Funktion z.B. Heizkessel definieren und z.B. die Temperaturregelung als Methode dieser Funktion - das ist im Grunde nicht so anders als die Programmierung mit Funktionen früher, nur das nun unterschiedliche Aufgaben, die physikalisch zusammengehören (weil es immer um den selben Heizkessel geht) gruppiert.

Nun zu meiner Frage:
Welchen Vorteil bringt es, diese Methoden nun in Schnittstellen nochmal auszugliedern? Ich sehe irgendwie noch nicht, welchen Vorteil dieser Schritt bringt.

Sorry fürs "auf dem Schlauch stehen", aber ich bin wie schon gesagt blutiger Anfänger in objektorientiertem Programmieren.

Danke fürs erleuchten!

Gruß

Christian
 
Welchen Vorteil bringt es, diese Methoden nun in Schnittstellen nochmal auszugliedern? Ich sehe irgendwie noch nicht, welchen Vorteil dieser Schritt bringt.
Wenn Du z. B. mehrere Baugruppen-FBs hast, die bislang über eine einheitliche Datenschnittstelle zum Programm verfügen, könntest Du statt dessen ein einheitliches Interface mit Methoden und Eigenschaften für diese FBs definieren. Dann könntest Du ein Array of Baustein-Interface deklarieren, um die FBs, obwohl sie von unterschiedlichem Typ sind, in einer Schleife aufrufen zu können.
Ob sich das lohnt, hängt von der Anzahl der FBs ab. Bei meinen Programmen (allgemeiner Maschinenbau) wird die Anzahl der FBs, die ich in einem PRG oder einem anderen FB instanziiere, nur selten zweistellig. Da sehe ich noch keinen Bedarf für Interfaces, sondern schätze beim Debuggen den Vorteil, mit jedem FB per Du zu sein. Bei der Automatisierung grosser Gebäude mit drei- oder gar vierstelliger Anzahl von Räumen kann das aber anders aussehen.
 
Der Hauptvorteil von Interfaces ist es, verschiedenartige FBs, die gleiche Eigenschaften haben, gleichartig behandeln zu können.

Zum Beispiel könnte dein Heizkessel einen Nachtbetrieb und einen Tagbetrieb haben, so wie viele ganz anders aufgebaute Objekte auch.
Dann kannst du diese Eigenschaft in ein Interface stecken:

INTERFACE INachtbetrieb
{
PROPERTY Nachtbetrieb {get; set;}
}

Wenn sich ein anderes Objekt nun nur für diese Eigenschaft von Objekten interessiert, dann muss es auch nichts anderes von diesem Objekt wissen.
Beispielsweise kann man alle Objekte mit der Nachtbetriebseigenschaft in ein ARRAY stecken, und alle über dieses Array verwalten:

nachtbetriebsdinger : ARRAY [0..Num-1] OF INachtbetrieb;

IF Tageszeit >= TOD#20:00 THEN
FOR i := 0 TO Num - 1 DO
nachtbetriebsdinger.Nachtbetrieb := TRUE;
END_FOR
END_IF

Und der Gag ist eben nun, dass in diesem Array nicht nur der Heizkessel drinsteckt, sondern auch die Jalousien, oder was sonst auch immer einen Nachtbetrieb haben kann.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Früher hätte man jedem FB mit Nachtbetrieb eine entsprechende INPUT-Variable gegeben, anhand der Uhrzeit eine zentrale Variable "Nachtbetrieb" verwaltet und diese an die INPUT-Variablen der FBs übergeben. Wo ist nun der grosse Vorteil der Interfaces? Schliesslich ist es mit der Deklaration des Interface-Arrays ja nicht getan. Ich muss doch alle FBs statisch instanziieren und dann ihre Adressen den Interface-Variablen zuweisen.
 
@ST:
Mir hat sich das mit den Interfaces auch noch nicht so ganz richtig erschlossen - ich verstehe es aber so :
Wenn du eine Collection mit unterschiedlichen Objekt-Typen hast dann kannst du natürlich alle über das Ableitungs-Objekt, dass sie alle gemeinsam haben, ansprechen wenn du auf dem Wege die von dir benötigten Eigenschaften erreichen kannst. Beispiel aus Visual-Studio : Du hast eine Collection mit Labels, Buttons und TextBoxen darin - die kannst du nun alle als Control ansprechen wenn du z.B. nur die BackColor zuweisen möchtest.
Jetzt hast du aber kein gemeinsames Ableitungs-Objekt aber dafür ein gemeinsames Interface - dann ginge es genauso - nur halt über das Interface (und an der Stelle fehlt mir etwas der Bezug - aber vielleicht nimmt hier einer mal den Faden auf).

Gruß
Larry
 
Hmm, also erstmal vielen Dank für die schnellen Antworten.
So langsam lichtet sich der Nebel ein bisschen, was man mit diesen Schnittstellen machen kann - ich kann gerade aber noch nicht abschätzen, ob ich das jemals in meinem Job brauchen werde.
Als Sondermaschinenbauer sind identische Anlagen eher die Seltenheit und bei unseren Kunden sind Produktions-Einzelarbeitsplätze die Regel, also ohne Verkettung von X Maschinen oder gar Hallen-weite Vernetzung.

So wie ich das jetzt verstehe, ist die Definition der Schnittstellen lediglich ein paralleler Lösungsweg, ich sehe gerade noch keine Anwendung, die durch die Verwendung von Schnittstellen lösbar ist, mit den "klassischen" FBs jedoch nicht.
Auch die Lesbarkeit der Programme wird - zumindest für mich - nicht entscheidend verbessert.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@ Larry

also was mir gedanklich da noch im Weg steht ist, das ich eine Schnittstelle "importiere", diese bei X Bausteinen gleich heißt, aber völlig unterschiedlich arbeiten kann.
Natürlich kann man das mit den "klassischen" FBs auch machen, aber da ist klar, das das alles nur aus diesem FB kommt. Wenn ich etwas einbinde fühlt sich das für mich wie ein weiterer FB-Aufruf an und daran gemessen verhalten sich die Schnittstellen einfach "falsch".

Ich kanns grad nicht besser erklären, sorry.
 
Früher hätte man jedem FB mit Nachtbetrieb eine entsprechende INPUT-Variable gegeben, anhand der Uhrzeit eine zentrale Variable "Nachtbetrieb" verwaltet und diese an die INPUT-Variablen der FBs übergeben. Wo ist nun der grosse Vorteil der Interfaces? Schliesslich ist es mit der Deklaration des Interface-Arrays ja nicht getan. Ich muss doch alle FBs statisch instanziieren und dann ihre Adressen den Interface-Variablen zuweisen.

Der grosse Vorteil ist, dass man das Array verwalten kann, ohne zu wissen was für Objekte sich darin befinden.
Beim traditionellen Ansatz bleibt einem nichts anderes übrig, als mit den konkreten Objekten zu arbeiten:

also in etwa so:
heizkessel1.Nachtbetrieb := global_nachtbetrieb;
heizkessel2.Nachtbetrieb := global_nachtbetrieb;
jalousie1.Nachtbetrieb :=

...

wenn man jetzt ein neues Objekt hinzufügt, muss man die Liste ändern. Das heisst zum Beispiel, dass man auch Zugriff auf diesen Code haben muss.
Bei meinem Beispiel oben kann man eine ganz allgemeine Verwaltung aufbauen, die kann in einer Bibliothek stecken, und muss nicht mehr
angefasst werden, nur weil man ein neues Objekt hinzufügt.

Und das gilt für alle Stellen an denen man das Objekt verwenden will.

Mit Hilfe von Interfaces kann man seinen Code einfacher modularisieren und einfacher erweitern.

Anderes Beispiel aus einem uralten Projekt aus den Anfangszeiten der V3. Ich habe das Projektarchiv im Anhang angehängt. Ich hoffe ihr könnt was damit anfangen.
Ich habe damals folgendes gemacht, ein Kollege von mir hat auf einem CODESYS-Usergroup Treffen ein Projekt vorgestellt, in dem er gezeigt hat, wie er arbeitet, wie
er typischerweise seine Projekt aufbaut.
Das war noch aus Vor-OO Zeiten und der Kollege ist Applikateur und hatte mit Objektorientierung erstmal nichts am Hut.

Die Grundidee ist nicht schwierig, er hat eine Zustandsmaschine die er in SFC programmiert hat (bei mir ist das ein ST-Switch Case geworden) und die alle Einheiten einer Maschine verwaltet:

Code:
CASE state OF
    Init:
        // initialize all units
        digPick(bEnable := TRUE, bHome := TRUE, bManual := FALSE);
        digPlace(bEnable := TRUE, bHome := TRUE, bManual := FALSE);
        axisPress(bEnable := TRUE, bHome := TRUE, bManual := FALSE);
        axisPress2(bEnable := TRUE, bHome := TRUE, bManual := FALSE);
        IF digPick.bDone AND digPlace.bDone AND axisPress.bDone THEN
            IF bManualMode THEN
                state := Manual;
            ELSIF bAutoMode THEN
                state := Auto;
            END_IF
        END_IF
    Error:
        // should never happen
        // but if it does, all units are switched off
        digPick(bEnable := FALSE);
        digPlace(bEnable := FALSE);
        axisPress(bEnable := FALSE);
        axisPress2(bEnable := FALSE);
        IF bQuitError THEN
            state := Init;
        END_IF
    Manual:
        // manual mode 
        digPick(bEnable := TRUE, bHome := FALSE, bManual := TRUE);
        digPlace(bEnable := TRUE, bHome := FALSE, bManual := TRUE);
        axisPress(bEnable := TRUE, bHome := FALSE, bManual := TRUE);
        axisPress2(bEnable := TRUE, bHome := FALSE, bManual := TRUE);
        IF bReset THEN
            state := Init;
        ELSIF bAutoMode THEN
            state := Auto;
        END_IF
    Auto:
        // auto mode 
        digPick(bEnable := TRUE, bHome := FALSE, bManual := FALSE);
        digPlace(bEnable := TRUE, bHome := FALSE, bManual := FALSE);
        axisPress(bEnable := TRUE, bHome := FALSE, bManual := FALSE, diSetPos := 1200);
        axisPress2(bEnable := TRUE, bHome := FALSE, bManual := FALSE, diSetPos := 1200);
        IF bReset THEN
            state := Init;
        ELSIF bManualMode THEN
            state := Manual;
        END_IF
    ELSE
        state := Error;
END_CASE


Und ich habe mir das jetzt vorgenommen, und nach meinen Vorstellungen umgebaut. Was man zunächst sieht: alle Units haben das gleiche Interface nach aussen,
und bieten Funktionen für jeden Zustand an.
Also mache ich daraus erstmal ein Interface IUnit, dass alle Objekte die in der Zustandsmaschine verwaltet werden wollen zu implementieren haben:

Code:
INTERFACE IUnit
Method Auto : BOOL;
Method Done : BOOL;
Method Enable : BOOL;
Method Home : BOOL;
Method Manual : BOOL;
Method Simulation : BOOL;
END_INTERFACE

Die neue Zustandsmaschine verwaltet jetzt ganz allgemein eine Liste von solchen Units:

Code:
FUNCTION_BLOCK Sequence
VAR
    m_units : ARRAY [0..10] OF IUnit;
    m_nNumUnits : INT := 0;
END_VAR

Und bietet eine Methode an um eine neue Unit zu registrieren. Die Zustandsmaschine sieht dann nicht so viel anders aus, nur iteriert man in jedem
Zustand über die Liste der Units und ruft die entsprechende Methode auf.

Was ist nun der Vorteil der OO-Lösung gegenüber der traditionellen Lösung? Man sieht das sofort wenn man an diesem Programm etwas ändern will.
Nehmen wir an, es kommt eine neue Einheit hinzu, dann genügt es diese in die Sequenz einzutragen und fertig:

seq.RegisterUnit(axisPress2);

Im alten Code muss man dagegen an vier Stellen editieren, und zwar direkt in der Zustandsmaschine.
Im OO-Code muss man die Zustandsmaschine nicht anfassen, die kann man auch in eine Bibliothek auslagern.
Noch ein grosser Vorteil: die Zustandsmaschine kann sogar im Laufenden Betrieb geändert werden
(wenn vom Programmierer vorgesehen), man könnte eine Funktion einbauen um eine Einheit aus der Zustandsmaschine zu entfernen und dann beispielsweise
unabhängig von den anderen Einheiten im Manualmodus betrieben zu werden.
Sowas ist mit dem herkömmlichen Verfahren überhaupt nicht möglich.
Was auch ein grosser Vorteil ist, durch das Interface wird eine Implementierung vorgegeben, man kann nicht einfach bei der Implementierung einer neuen Unit
einen Zustand vergessen.
 

Anhänge

  • DemoV30x.zip
    1,1 MB · Aufrufe: 133
Ok, ich glaube ich habe es jetzt so langsam begriffen...

mal sehen, ob ich das irgendwann mal beruflich einsetzen kann.

Danke für die ausführliche Erklärung!
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@ST:
Mir hat sich das mit den Interfaces auch noch nicht so ganz richtig erschlossen - ich verstehe es aber so :
Wenn du eine Collection mit unterschiedlichen Objekt-Typen hast dann kannst du natürlich alle über das Ableitungs-Objekt, dass sie alle gemeinsam haben, ansprechen wenn du auf dem Wege die von dir benötigten Eigenschaften erreichen kannst. Beispiel aus Visual-Studio : Du hast eine Collection mit Labels, Buttons und TextBoxen darin - die kannst du nun alle als Control ansprechen wenn du z.B. nur die BackColor zuweisen möchtest.
Jetzt hast du aber kein gemeinsames Ableitungs-Objekt aber dafür ein gemeinsames Interface - dann ginge es genauso - nur halt über das Interface (und an der Stelle fehlt mir etwas der Bezug - aber vielleicht nimmt hier einer mal den Faden auf).

Gruß
Larry
Ich bin zwar auch ein Freund der guten alten Vererbung, muss aber zugeben, dass Interfaces flexibler sind. Ich nehme mal das Beispiel mit dem Heizkessel und der Jalousie. Wenn ich die gemeinsame Eigenschaft "Nachtbetrieb" per Vererbung ausdrücken wollte, müsste ich beide von einer Basisklasse "Gerät mit Nachtbetrieb" ableiten. Ob mir das aber in den Sinn kommen würde, bezweifle ich. Der Nachtbetrieb ist sicher nicht das wesentliche Merkmal und auch sonst wird es kaum Gemeinsamkeiten geben. Heizkessel und Jalousie wären in anderen Zweigen des Objektstammbaums wohl besser aufgehoben. Wenn ich dagegen ein Interface "Gerät mit Nachtbetrieb" deklariere, kann ich es jedem FB mitgeben, der das braucht.
Genauso, wie ich früher jedem dieser FBs eine entsprechende Input-Variable gegeben hätte.
 
Ein Beispiel

Für mich war bisher das beste Beispiel, mit dem wir immer wieder konfrontiert sind, die Status Maschine.

Im Prinzip als Interface:

bekomme einen Event (mit Parametern),
behandle diesen Event,
setze den (neuen) Status.

Die verschiedenen Status Instanzen reagieren dann ganz übersichtlich,

Statusinstanz "InitState" z.B. reagiert ausschliesslich auf den Event "Init()",
Statusinstanz "RunState" z.B. reagiert auf "Schneller()", "Langsamer()", "Stop()" etc. aber z.B. nicht auf "Reset()" ...
Statusinstanz "StopState" z.B. reagiert auf "Schneller()", "Reset()"

Mit Vererbung und BasisKlassen bekommt man super sichere und übersichtliche Anlagen.
Nicht reagieren heisst dann einfach auf die leere Methode der Basis Klasse zurückfallen.

Ich empfehle dazu das Buch Design Pattern von Gamma e.a.

Fragen an Werner29.
VAR_INST, kann man das noch einmal ausführlich darlegen.

Problem mit einer public Property, die eine Struct liefert. Warum kann man nicht auf einzelne Elemente der struct zugreifen?
 
Fragen an Werner29.
VAR_INST, kann man das noch einmal ausführlich darlegen.

Das ist ein recht spezielles Sprachmittel das wir irgendwann mal eingeführt haben.
Damit kann man im Kontext einer Methode eine Variable deklarieren, die zur Instanz des Funktionsblocks gehört.
Also wenn man eine Variable im FB hat, die nur in einer Methode verwendet wird und auch nur dort verwendet werden soll,
dann kann man die als VAR_INST in der Methode anlegen.


METHOD m : INT
VAR
temp : INT; // eine temporäre Variable, wird bei jedem Aufruf von m neu initialisiert
END_VAR
VAR_INST
inst : INT; // eine Instanzbezogene Variable, ist für jede Instanz des FB unterschiedlich
END_VAR
VAR_STAT
glob : INT; // eine global-statische Variable, ist für alle Instanzen des FB gleich
END_VAR


Problem mit einer public Property, die eine Struct liefert. Warum kann man nicht auf einzelne Elemente der struct zugreifen?

Das Problem ist ein technisches:
Ich will zunächst mal erklären was intern abläuft wenn man eine Struktur als Wert eines Property zurückliefert:

PROPERTY Prop
GET
Prop := inst_var;
END_GET
END_PROPERTY

in dem Fall wird für das Property eine implizite Methode angelegt und diese Methode hat die implizite Output-Variable Prop.
Diese Rückgabevariable liegt auf dem Stack der Methode. Inst_var liegt in der Instanz.
Wir haben an der Stelle also zunächst mal eine vollständige Kopie der Instanzvariable auf den Stack.
Das heisst jetzt aber auch dass dieser Zugriff

inst.Prop

nicht auf ein Objekt zugreift, dass dauerhaft im Speicher rumliegt, sondern das Objekt liegt auf dem Stack. Das ist ein ganz kurzlebiges
Ding, das nach dem Aufruf des Property sofort wieder zerstört wird.
Das einzige was man nun mit diesem kurzlebigen Objekt machen kann, ist es in ein längerlebiges Objekt umzukopieren und mit dem kann
man dann wieder weiterarbeiten.

es gibt aber auch ein Performance-Argument: selbst wenn der Compiler irgendwas tricksen würde um ein implizites-Objekt für diesen
Zugriff zu erzeugen:

x := inst.Prop.Komponente;

dann könnte er es nicht anders machen als die Struktur zwei oder dreimal zu kopieren, und am Ende interessiert einen nur eine einzige Komponente aus der Struktur.
Besser ist es, das Property liefert eine Referenz auf die Struktur zurück:

PROPERTY REFERENCE TO Prop
GET
Prop REF= inst_var;
END_GET
END_PROPERTY

Was ist jetzt anders? Die Implizite Rückgabevariable für das Property enthält jetzt nur einen Pointer auf die Interne Struktur.
Der Zugriff

inst.Prop

sieht zwar gleich aus, zeigt aber in die Instanz inst rein. Und dann ist auch der Zugriff auf die Komponente kein Problem mehr:
x := inst.Prop.Komponente;

sieht völlig gleich aus, es steckt aber was ganz anderes dahinter.
 
Zurück
Oben