# Wie kann ich in einem Array of FB erfahren, welche Array-Nummer aktiv ist?



## Chräshe (19 Februar 2010)

Hallo Allerseits,
 
zur Vereinfachung will ich Bausteine, welche sehr oft aufgerufen werden, gleich als ARRAY definieren. Das hab ich bereits getestet und geht auch ganz gut. 

Beispiel:
VAR
_   Antrieb : ARRAY[1..200] OF Motor_RL;
END_VAR
 

(* Aufrufe der Antriebseinheiten     *)
Antrieb[1](
_   AntriebNr:=    '001',
_   sName:=        'Steigband Süd ',
_   FRG:=            I_NOT_AUS_iO,
_   VWrechts:=   I_M1_Rechts_123S1,
_   VWlinks:=      I_M1_Links_123S2,
_   tRW:=             t#1s500ms,
_   Q_rechts=>   Q_M1_Rechts,
_   Q_links=>      Q_M1_Links,
_   sStatus=>      sTemp_001);        (* => AntriebNr & sName & Info-Intern *)


Gibt es hier eine Möglichkeit, im FB auszuwerten, welche Array-Nummer gerade aufgerufen ist?

Hintergrund ist, die Eingangs-Variable „AntriebNr“ wegzulassen und die Nummer von „Antrieb[i]“ direkt zu verwenden. Somit wird eine Falscheingabe verhindert und es müssen nicht so viele Parameter übergeben werden.

Bin für jeden Tipp dankbar. Im Moment dreh ich mich nur im Kreis… 

Gruß
Chräshe


----------



## trinitaucher (19 Februar 2010)

Deklarier im FB doch nen Eingang, dem du später dann die Nummer des Array-Elements übergibst.


----------



## ybbs (19 Februar 2010)

Chräshe schrieb:


> Gibt es hier eine Möglichkeit, im FB auszuwerten, welche Array-Nummer gerade aufgerufen ist?



Darf ich fragen wozu die Instanz den ArrayIndex wissen muss? Im Moment habe ich keine Vorstellung wozu das benötigt werden könnte. 

Damit ich nicht nur dumm herumfrage noch ein Beispiel wie vom trinitaucher vorgeschlagen:

```
(* -------------------------------------- *)
(* Init Drive 1 *)
Idx := 1
Antrieb[Idx].sName    := 'Steigband Süd ';
Antrieb[Idx].FRG      := I_NOT_AUS_iO;
Antrieb[Idx].VWrechts := I_M1_Rechts_123S1;
Antrieb[Idx].VWlinks  := I_M1_Links_123S2;
Antrieb[Idx].tRW      := t#1s500ms;

Q_M1_Rechts := Antrieb[Idx].Q_rechts;
Q_M1_Links  := Antrieb[Idx].Q_links;
sTemp_001   := Antrieb[Idx].sStatus;

(* -------------------------------------- *)
(* Init Drive 2 *)
Idx := 2
Antrieb[Idx].sName    := 'Steigband Nord ';
Antrieb[Idx].FRG      := I_NOT_AUS_iO;
Antrieb[Idx].VWrechts := I_M2_Rechts_122S1;
Antrieb[Idx].VWlinks  := I_M2_Links_122S2;
Antrieb[Idx].tRW      := t#1s500ms;

Q_M2_Rechts := Antrieb[Idx].Q_rechts;
Q_M2_Links  := Antrieb[Idx].Q_links;
sTemp_002   := Antrieb[Idx].sStatus;

(* -------------------------------------- *)
(* Call instances *)
For Idx := 1 to DRIVE_MAX
do
  Antrieb[Idx] ( AntriebNr := Idx );
end_if
```


----------



## trinitaucher (19 Februar 2010)

ybbs schrieb:


> Darf ich fragen wozu die Instanz den ArrayIndex wissen muss? Im Moment habe ich keine Vorstellung wozu das benötigt werden könnte.


Wenn er z.B. mehrere Antriebe mit gleicher Ansteuerung hat, aber je nach Position des Antriebs in der Maschine unterschiedliche Antriebsparameter verwendet werden sollen.
Wenn der Parameter nicht im aufrufenden Programm, sondern im Antriebs-FB gehaltern werden soll, ist das sehr praktisch. Es vereinfacht den Code.

Ich hab sowas schon häufiger programmiert.


----------



## Chräshe (19 Februar 2010)

trinitaucher schrieb:


> Deklarier im FB doch nen Eingang, dem du später dann die Nummer des Array-Elements übergibst.



 Hallo trinitaucher,
meinst du etwa so...

(* Aufrufe der Antriebseinheiten *)
Antrieb*[1]*(
_ AntriebNr:= '001',         (* hier zufällig als String, damit intern keine Wandlung notwendig wird ;-) *)
_ sName:= 'Steigband Süd ', 
 _...
Ja, das könnte funktionieren. 
 
Meine Frage ist: Wie kann ich innerhalb vom FB Antrieb*[x]* erfahren, welches Array- Element gerade aktiv ist, ohne dass ich zusätzlich eine Nummer übergebe?
Die Nummer soll immer mit *[x] *identisch sein. Bei Tippfehler käme es in diesem Fall zu falschen Statusmeldungen.


ybbs schrieb:


> Darf ich fragen wozu die Instanz den ArrayIndex  wissen muss? Im Moment habe ich keine Vorstellung wozu das benötigt  werden könnte. [/CODE]


 @ybbs  
Um einem String eine eindeutige Meldenummer mitzugeben, für eine Statusanzeige in der Visu oder eventuell eine Protokollierung...

OK, so sollte auch schon gut sein. 

(* Aufrufe der Antriebseinheiten *)
*Idx* := 1
Antrieb*[Idx]*(
_ AntriebNr:= *Idx*,     
_ sName:= 'Steigband Süd ',  
_ …

Wenn jemand was eleganteres weiß, bin ich immer noch interessiert. :icon_rolleyes:

Gruß
Chräshe


----------



## Flo (20 Februar 2010)

Chräshe schrieb:


> Meine Frage ist: Wie kann ich innerhalb vom FB Antrieb*[x]* erfahren, welches Array- Element gerade aktiv ist, ohne dass ich zusätzlich eine Nummer übergebe?
> 
> Gruß
> Chräshe


geht nicht. habe das vor ein paar monaten auch schon mal probiert ( in meinem Fall habe ich das mit Fehler FB's versucht). bin dann auch auf die von trinitaucher erklärte Lösung gegangen. (ich kann damit leben, denn von nem Programmierer kann man schon erwarten das er 2mal die selbe Zahl an einem FB angibt)
mfg,
Flo


----------



## Thomas_v2.1 (20 Februar 2010)

Ich habe in dem Zuge mal was mit der Berechnung der Speicheradressen probiert, um daraus den Index zu berechnen. Aber bei meiner Codesys Version (und Steuerung) kommt nichts sinnvolles heraus. Laut Dokumantation soll Pointerarithmetik im C-Stil möglich sein.

Ich habe einen fbTEST:

```
FUNCTION_BLOCK fbTEST
VAR_INPUT
	pArrStart : POINTER TO BYTE;	(* Startadresse des Array *)
	pArrAkt : POINTER TO BYTE;		(* Meine Adresse im Array *)
	size : INT;
END_VAR
VAR_OUTPUT
	dif : DWORD;
	pos : DINT;
END_VAR
VAR
	aAkt : DINT;
	aStart : DINT;
	dw : DWORD;
END_VAR

aAkt 	:= DWORD_TO_DINT(ADR(pArrAkt));
aStart 	:= DWORD_TO_DINT(ADR(pArrStart));
dw 		:= ADR(pArrAkt) - ADR(pArrStart);

dif 	:= aAkt - aStart;
pos 	:= dif / size;
```

Diesen rufe ich als Array auf;

```
fbt : ARRAY[0..5] OF fbTEST;

(************************************)
aStart := ADR(fbt);

fbt[0](
	pArrStart:= ADR(fbt),
	pArrAkt:= ADR(fbt[0]),
	size:= SIZEOF(fbt[0]) ,
	dif => d,
	pos => p
);

fbt[1](
	pArrStart:= ADR(fbt),
	pArrAkt:= ADR(fbt[1]),
	size:= SIZEOF(fbt[0]) ,
	dif => d,
	pos => p
);

fbt[2](
	pArrStart:= ADR(fbt),
	pArrAkt:= ADR(fbt[2]),
	size:= SIZEOF(fbt[0]) ,
	dif => d,
	pos => p
);
```

Theoretisch sollte an "pos" die Position in Array herauskommen, tut es aber nicht.
Für das was ich in der Onlinesicht sehe habe ich mal einen Screenshot gemacht. Es ist schon komisch dass bei der Adress-Onlinesicht an "aStart" jedes mal ein anderer Wert berechnet (oder zumindest angezeigt) wird.


----------



## Thomas_v2.1 (20 Februar 2010)

Hach, direkt nach dem Absenden sieht man den Fehler. Im Baustein darf der Adressoperator natürlich nicht stehen:

```
aAkt   := DWORD_TO_DINT(pArrAkt);
aStart := DWORD_TO_DINT(pArrStart);
dw     := pArrAkt - pArrStart;

dif 	:= aAkt - aStart;
pos 	:= dif / size;
```
Dann kommt an "pos" auch die passende Position heraus.

Nur ganz ohne zusätzlichen Parameter scheint es so auch nicht zu gehen, da z.B. ein sizeof() vom eigenen Baustein wegen Rekursion nicht möglich ist.


----------



## Oberchefe (20 Februar 2010)

das geht unter Umständen schon, erfordert aber drei Dinge:
-die erste Variable im FB muß gleich bleiben (es darf also nachträglich keine weitere davor gesetzt werden)
-eine Arrayposition muß bekannt sein (also beispielsweise die [0])
-das Array von FBs muß entweder Global definiert sein oder aber muß bekannt sein, in welchem Programm es angelegt ist. Im Beispiel unten wurde letzteres gemacht.

Im FB sieht es dann so aus:


```
FUNCTION_BLOCK fbTest
VAR_INPUT
    MeinEingangsparameter1:    INT;
END_VAR
VAR_OUTPUT
    Index:    DINT;
END_VAR
VAR
    Test1:    DINT;
    Test2:    DINT;
    Test3:    INT;
END_VAR
```


```
Index:=(ADR(MeinEingangsparameter1)-ADR(PLC_PRG.fbt))/SIZEOF(PLC_PRG.fbt[0]);
Test1:=Test1+1;
Test2:=Test2-1;
Test3:=DINT_TO_INT(Test1+Test2);
```
Test1, Test2 und Test3 dienen nur zur Verdeutlichung wo das eigentliche Programm stehen kann


----------



## Thomas_v2.1 (20 Februar 2010)

Oberchefe schrieb:


> das geht unter Umständen schon, erfordert aber drei Dinge:
> -die erste Variable im FB muß gleich bleiben (es darf also nachträglich keine weitere davor gesetzt werden)
> -eine Arrayposition muß bekannt sein (also beispielsweise die [0])
> -das Array von FBs muß entweder Global definiert sein oder aber muß bekannt sein, in welchem Programm es angelegt ist. Im Beispiel unten wurde letzteres gemacht.



Die erste Variable, in dem Fall ADR(MeinEingangsparameter1), ist dann gleichbedeutend mit dem in der Objektorientierung üblichen this-Zeiger (oder self-Zeiger, je nach Sprache).
Bei ST kann ich den Startindex im Array aber auch bei != 0 beginnen lassen, dann hat die Zahl die ich berechne keine direkte Bedeutung mehr.


----------



## Oberchefe (20 Februar 2010)

> Bei ST kann ich den Startindex im Array aber auch bei != 0 beginnen lassen, dann hat die Zahl die ich berechne keine direkte Bedeutung mehr.



Dann ändere ich eben SIZEOF(PLC_PRG.fbt[0] in SIZEOF(PLC_PRG.fbt[1].


----------



## trinitaucher (20 Februar 2010)

Mein Vorschlag bezog sich eher auf vereinfachte FB-Aufrufe durch Schleifen. ZB so:


```
FOR i := 0 TO 10 DO
  myFb[i](iParameter := i);
END_FOR
```
11 FB-Aufrufe mit 3 Zeilen Code 

Und im FB steht zB:

```
CASE iParameter OF
0,1,2: lrSpeed := 100;
3,4,5:  lrSpeed := 150;
6,7,8,9,10:  lrSpeed := 200;
END_CASE
```

Vorteil:
FB wird einmal programmiert mit den vorab festen Parametern. Man muss die Parameter dann nicht nochmal im FB-Aufruf übergeben, was den Code übersichtlich macht. Denn die FBs mit zugehörigen Parametern sind schön vom Rest gekapselt


----------



## ybbs (20 Februar 2010)

trinitaucher schrieb:


> Und im FB steht zB:
> 
> ```
> CASE iParameter OF
> ...



Nachteil: 
Die (projektübergreifende) Verwendbarkeit des FBs ist schon sehr eingeschränkt. Für projektspezifische Sachen allerdings durchaus eine mögliche Option.

Ich bevorzuge i.d.R. die Übergabe eines Pointers auf eine Paramterstruktur außerhalb des FBs.


----------



## Werner29 (23 Februar 2010)

Chräshe schrieb:


> Gibt es hier eine Möglichkeit, im FB auszuwerten, welche Array-Nummer gerade aufgerufen ist?
> 
> Hintergrund ist, die Eingangs-Variable „AntriebNr“ wegzulassen und die Nummer von „Antrieb[i]“ direkt zu verwenden. Somit wird eine Falscheingabe verhindert und es müssen nicht so viele Parameter übergeben werden.
> 
> Bin für jeden Tipp dankbar. Im Moment dreh ich mich nur im Kreis…


Ich habe den Thread nur überflogen, und vielleicht wiederhole ich hier nur einen Tipp: wenn du den Index nicht zweimal benutzen willst, dann kannst du dir auch eine Hilfsfunktion schreiben, die dann den eigentlichen Aufruf macht. Entweder greifst du in der Funktion auf den Array als globale Variable zu, oder du übergibst den Array als VAR_IN_OUT (wichtig! nicht VAR_INPUT).
In so einer Funktion (kann auch ein FB sein) kannst du auch die Parametrierung kapseln, das finde ich viel besser als im Funktionsblock selber.


----------



## Chräshe (23 Februar 2010)

Hallo noch mal,

  danke für die unterschiedlichen Anregungen. 
  Ich hatte gehofft, dass es vielleicht gewisse Steuerflags oder Worte 
innerhalb von Funktionen und Funktionsbausteinen gibt, ähnlich den
AS-Flags wie „SFCCurrentStep“ …

  Folgende Variante scheint mir am übersichtlichsten und einfachsten:
  [FONT=&quot]
[/FONT](* Aufrufe der Antriebseinheiten *)[FONT=&quot]
[/FONT]*Idx* := 1
Antrieb*[**Idx**]*(
_ AntriebNr:= *Idx*, 
_ sName:= 'Steigband Süd ', [FONT=&quot]
[/FONT]_ …[FONT=&quot]

[/FONT]Gruß[FONT=&quot]
[/FONT]Chräshe


----------



## MarkusP (2 März 2010)

*Frage*

Hi,

passt zwar nicht zur ursprünglichen Fragestellung, aber etwas interessiert mich noch. Warum weist Du den FB-Eingängen und FB-Ausgängen eigentlich nocheinmal Variablen zu? Diese könnten doch auch direkt über den TSM verknüpft werden, wenn die Variablen entsprechend deklariert werden. Wir haben z.B. Bausteine mit zig E/A's ohne einer einziger Variablenzuweisung. Dann erspart man sich viel Schreibarbeit, zudem kann man die Variablenverknüpfung im TSM auch automatisieren. (z.B. über E/A-Zuordnungsliste) Das ist für mich eines der genialsten Dinge von Beckhoff.

Dann wird auch der Aufruf Deines FB's quasi zum Einzeiler.

Schönen Abend

Markus.


----------



## Chräshe (3 März 2010)

Hallo Markus,

interessanter Vorschlag. Bei Servo-Achsen hatte ich das sogar bereits gemacht, dass ich im Baustein unter VAR die <Variable> AT %I*... deklariert habe.  
Ansonsten ist es aber so, dass ich die Ein- und Ausgänge eines Projektes aus dem E-CAD in die globalen Variablen importiere. Dann habe ich identische und sehr eindeutige Variablenbezeichnungen, inklusive ausführlichem Kommentar. Ich finde es so übersichtlicher, weil ich mehr Informationen habe.

Gruß
Chräshe


----------



## Münchnerjunge (21 Dezember 2016)

MarkusP schrieb:


> Hi,
> 
> passt zwar nicht zur ursprünglichen Fragestellung, aber etwas interessiert mich noch. Warum weist Du den FB-Eingängen und FB-Ausgängen eigentlich nocheinmal Variablen zu? Diese könnten doch auch direkt über den TSM verknüpft werden, wenn die Variablen entsprechend deklariert werden. Wir haben z.B. Bausteine mit zig E/A's ohne einer einziger Variablenzuweisung. Dann erspart man sich viel Schreibarbeit, zudem kann man die Variablenverknüpfung im TSM auch automatisieren. (z.B. über E/A-Zuordnungsliste) Das ist für mich eines der genialsten Dinge von Beckhoff.
> 
> ...


 Hallo Markus, 
wenn auch offtopic, so möchte ich doch kein neues Thema erstellen. Kannst du das von dir beschriebene nochmal erläutern oder ein Beispiel geben, ich habe noch nicht ganz verstanden, was du meinst, wenngleich es sich nach einer vorteilhaften Funktion anhört.

Danke vorab!


----------



## holgermaik (21 Dezember 2016)

Twincat unterscheidet sich da ein wenig von normalen Codesys Steuerungen.
Bei Codesys weist du z.B. einen Eingang mit %IW direkt der Hardware zu.

Bei Twincat geht das anders. Hier hast du den *T*wincat *S*ystem *M*anager. Also eigentlich 2 voneinander unabhängige Programme.
1. PLC Editor ( Hier wird das Programm ganz normal wie in Codesys programmiert)
2. den TSM

z.B. Im PLC Editor programmierst du deinen  Ausgang. Jetzt nimmst du den TSM und liest alle Variablen aus dem PLC Editor ein. Auch deinen Ausgang. Da im TSM auch die Hardwarekonfig hinterlegt ist machst du jetzt eine Verknüpfung von deinem Ausgang zu einem Hardwareausgang.

Wenn du was konkretes wissen möchtest einfach nochmal melden.

Holger


----------

