# Codesys Objektorientiertes Programmieren



## toto45 (6 März 2014)

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?_


----------



## StructuredTrash (6 März 2014)

toto45 schrieb:


> 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.


----------



## Werner29 (10 März 2014)

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


----------



## StructuredTrash (10 März 2014)

Werner29 schrieb:


> 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.


----------



## Werner29 (10 März 2014)

StructuredTrash schrieb:


> 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.


----------



## StructuredTrash (10 März 2014)

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.


----------



## Interface (10 März 2014)

toto45 schrieb:


> 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).




Werner29 schrieb:


> 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.




StructuredTrash schrieb:


> 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?


----------



## Werner29 (10 März 2014)

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.


----------



## Morymmus (23 Februar 2015)

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


----------



## StructuredTrash (23 Februar 2015)

Morymmus schrieb:


> 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.


----------



## Werner29 (24 Februar 2015)

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._


----------



## StructuredTrash (24 Februar 2015)

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.


----------



## Larry Laffer (24 Februar 2015)

@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


----------



## Morymmus (24 Februar 2015)

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.


----------



## Morymmus (24 Februar 2015)

@ 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.


----------



## Werner29 (24 Februar 2015)

StructuredTrash schrieb:


> 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:


```
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:


```
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:


```
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.


----------



## Morymmus (24 Februar 2015)

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!


----------



## StructuredTrash (5 März 2015)

Larry Laffer schrieb:


> @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).
> ...


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.


----------



## RobiHerb (5 März 2015)

*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?


----------



## Werner29 (6 März 2015)

RobiHerb schrieb:


> 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




RobiHerb schrieb:


> 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.


----------



## RobiHerb (6 März 2015)

@Werner29

Danke für die Klärung,

die REF Lösung ist schon verstanden, aber ich habe eine Safety Anwendung (SIL 2 in ST) zu programmieren und da sind mir mit internen Vorschriften Referenzen (auch als "expert") verboten. Spätestens von meinem Review Partner bekomme ich dann "geht nicht" beschieden. Sonst würde ich durchweg VAR_IN_OUT verwenden.

Das Performance Problem ist hier auf mich den Applikations Programmierer verschoben, denn dann muss ich alles temporär kopieren oder für jedes benötigte Element der struct Property eine eigene Get Property anlegen.
In C# ist die Dereferenzierung einer Property Struct erlaubt. Das ist eine Frage, wie tief (intelligent) der Compiler den Ausdruck analysiert, denn er weiss ja, dass ich nur das eine Element der Struct an dieser Stelle benötige.

Noch ein Hinweis allgemein zur OOP: Wenn ich Methoden etc. anlege, sind sie aus "Gründen der Kompatibilität" automatisch PUBLIC, es wäre defensiver sie PROTECTED oder PRIVATE zu machen, ggf. könnte man so etwas in den Options einstellen, aber zumindest vom Editor automatisch das Schlüsselwort PUBLIC einfügen.

Bitte das nicht als Verriss ansehen, ich muss auch TIA manchmal einsetzen und da hat Codesys Lichtjahre Vorsprung.


----------



## Ralle (6 März 2015)

@Werner

Das liest sich nun aber, als ob man die erste beschriebene Methode auch gleich ganz weglassen könnte?
Gibt es einen Fall, für den die dann besser geeignet ist?

Eigentlich sind das die Sachen, über die ich mir gerade bei einer SPS nicht den Kopf zerbrechen möchte. Ich hätte da Angst, durch eine fasche Verwendung eine Maschine zu zerstören.
Vielleicht bin ich da zu konservativ, aber ich halte solche Dinge in einer Maschine per se für unsicher, da muß man schon ganz genau wissen, was man macht oder?


----------



## cas (9 Juli 2015)

Hallo,

in Thread #8 steht das:
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.

Wie macht man das in der Praxis?

MfG

CAS


----------



## Daxgehtsteil (14 Juli 2015)

Werner29 schrieb:


> 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.
> ...



Hallo Bernhard, 

von welchen Seiteneffekten ist hier zum Beispiel die Rede? Das Einzige, das mich stört an den Properties ist, dass sie nicht gemonitort werden. Ich möchte sie aber auch ungern durch attribute aktivieren, wenn es
triftige Gründe gibt, das nicht zu tun. 

Viele Grüße

Dax


----------



## Werner29 (14 Juli 2015)

Hallo Dax,

das Property wird dann tatsächlich ausgeführt, um den Wert zu ermitteln.
Nehmen wir an du hast ein Property NextId, das folgendermassen implementiert ist:

NextId := _locVar;
_locVar := _locVar + 1;

dann wird der Member _locVar in der Instanz deines FBs allein dadurch hochzählen, dass du ihn im WatchFenster anschaust.
Das würde dann tatsächlich alle 200 ms hochgezählt werden, das ist der typische Zyklus für das Variablen-Monitoring.
Wenn du weist, dass du keinen Seiteneffekt in dem Property hast, dann kannst du das Attribut ohne Probleme setzen.


----------



## cas (15 Juli 2015)

Hallo Werner,

 hattest du meine Frage gelesen in #23 ?


----------



## Werner29 (15 Juli 2015)

Das Demo Beispiel zeigt genau das. Darin kommt die folgende Deklaration vor:

m_units : ARRAY [0..10] OF IUnit;

die Elemente von m_units werden zur Laufzeit mit konkreten Instanzen belegt die jeweils einen unterschiedlichen Typ haben, aber gleichartig behandelt werden über die gemeinsame Schnittstelle.
Mit herkömmlichen Mitteln ist sowas nicht möglich.


----------

