fc als multiinstanz gut oder böse?

Markus

Administrator
Teammitglied
Beiträge
6.323
Reaktionspunkte
2.341
Zuviel Werbung?
-> Hier kostenlos registrieren
angeregt durch ein beispiel vonserem geschätzten mtglied "onkel dagobert" (welcher übrigens einen klasse programmierstil hat), habe ich das beim letzten projekt so gemacht.

ich bastel erst einen udt mit allen variablen.

den setz ich in einem aglobal db beliebig oft in ein array.

ich mache das was vorher ein fb gemacht hat mit einer fc, diese fc hat neben sonstigen in und out auch eine inout die als mein udt deklariert ist.

beim aufruf kommt eine instanz in form eines arrayelementes aus dem global db an den inout.

schöne sache, vor allam in sachen konfigurierbarkeit und flexibilität.

ich habe zb 20 anlagenteile, die sind aber alle etwas anders, nicht jeder teil hat jeden sensor...

der udt definiert den maximalausbau.

jetzt bastel ich mir den "grund-multi-fc" der für alle anlagenteile aufgerufen wird. dann gibst fc mit der selben inout schnittstelle für sonderfuntionen. wird an einem anlagenteil diese funktion nachgerüstet, reicht ein aufruf mit dem ensprechenden arrayelement und der sensorik.
oder wenn ich nur einen einzelnen messwert habe schreibe ich mit dem scalierungsbaustein direkt in die variable des elements.
ich kann bits aus dem element auch nach herzenslust auf e/a legen.

von der sache her gefällt mir das ganz gut, würde gerne mal eure meinung dazu hören.

nachteil, es scheint als würden die aufgeblasenen inout gewaltig auf die zykluszeit schlagen...
 
Hallo Markus.

Eine Frage vorweg, wo liegt der Vorteil gegenüber einem FB?

Klar hauen die IO´s mit großen UDT´s in den Zyklus, FB´s tun das auch.

Ich finde das unflexibler, bei einer Änderung des UDT´s kracht das an allen möglichen Stellen. Ich wüsste natürlich auch Vorteile durch diese Art, bei 100 Zylindern z.b. ist ein ARRAY gut lesbar.

Grundsätzlich ist es aber schwirig auf deine Beschreibung sich einen Reim zu machen.

Gruß, pt
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Diesen Programmierstil habe ich mir vor einigen Jahren auch angewöhnt,
ich möchte gar nicht anders programmieren :)

..es scheint als würden die aufgeblasenen inout gewaltig auf die zykluszeit schlagen...
In irgend einer Siemens FAQ (find ich grad nicht) habe ich mal gelesen, dass je nach Zugriffsart der Zugriff um Faktor 5..20 mal länger dauert, da der Zugriff nicht direkt sondern über Pointer erfolgt.

Klar hauen die IO´s mit großen UDT´s in den Zyklus
Es kommt nicht auf die Datenmenge in der UDT an, es ist egal ob ich der Funktion eine Variable(UDTx) mit 2 Byte oder 200 Byte gebe.
Es sind die Zugriffe auf diese Variable innerhalb der FC, die entscheidend sind. Wenn man einer FC eine Variable vom typt UDTx übergibt, sieht die FC an ihrem Eingang einen 6 Byte Pointer (DB-NR|Bereichszeiger) und nicht die tatsächliche Datenmenge.

Um die Zugriffszeit erträglicher zu machen gehe ich so vor :

1- UDT definieren
2- Variable im DB vom Typt UDTx anlegen
3- FC Eingangsvariable definieren , aber der Eingang ist vom Typ "Any-Pointer" und nicht UDTx
4- In der FC kopieren des Any-Pointers mit Hilfe der Adressregister auf einen temp. Any-Pointer
5- Definieren einer Temp. Variablen vom Typ UDTx
5- Mit SFC 20 (Quelle = temp. Any Pointer, Ziel = temp. Variable vom Typ UDTx) den Inhalt der DB-Variablen in Temp. Bereich kopieren
6- Nun erfolgt der Zugriff auf UDT Variablen "Lokal"
7- Nach der Verarbeitung mit SFC 20 die Temp. Variable in DB kopieren (Quelle = Temp. Variable(UDTx), Ziel = Temp. Any Pointer)

Und so sieht es als Code aus :
Code:
      i_apDaten = IN-Variabel vom Typ Any-Pointer
      t_apDaten = TEMP-Variable vom Typ Any-Pointer
      t_DATEN   = TEMP-Variable vom Typ UDTx


      TAR1  #t_AR1.Sicherung            // Adressregister 1 sichern
      TAR2  #t_AR2.Sicherung            // Adressregister 2 sichern

      L     P##i_apDaten       
      LAR1  
      LAR2  P##t_apDaten
      L     W [AR1,P#0.0]
      T     W [AR2,P#0.0]
      L     D [AR1,P#2.0]
      T     D [AR2,P#2.0]
      L     D [AR1,P#6.0]
      T     D [AR2,P#6.0]
      CALL  "BLKMOV"
       SRCBLK :=#t_apDaten
       RET_VAL:=#t_Fehlerwort
       DSTBLK :=#t_DATEN
      LAR2  #t_AR2.Sicherung


Irgend eine Verarbeitung
            .........
             .........
     U  t_Daten.Freigabe
     U  t_Daten.BlaBla
     =  t_Daten.Ergebnis
            .........
            .........

Nach der Verarbeitung zurück kopieren


     CALL  "BLKMOV"
       SRCBLK :=#t_DATEN
       RET_VAL:=#t_Fehlerwort
       DSTBLK :=#t_apDaten

      LAR1  #t_AR1.Sicherung            // Adressregister 1 wiederherstellen
      LAR2  #t_AR2.Sicherung            // Adressregister 2 wiederherstellen
Sieht sehr aufwendig aus, ist aber immer die gleiche Sequenz.
UDT kann sich Ändern, nach einer Konsistenzprüfung ist die Welt aber wieder in Ordnung, wenn man den "Operandenvorrang" richtig gesetzt hat (symbolisch / Absolut)
Vorteil gegebnüber FBs: Man spart Instanz-DBs. So kann ich meine Anlagenteile in einem DB halten und beliebig Gruppieren.
Beispielsweise kann ich die Variablen
Motor ARRAY[1..10] of UDT x
Ventil ARRAY[1..20] of UDT y
Platz ARRAY[1..15] of UDT z
.........
alle in einem DB halten.
Der Nachteil ist, dass je nach Größe der definierten UDTs der Lokaldatenstack aufgebläht wird.
Das nehme ich in Kauf, weil es sehr übersichtlich bleibt und die FCs nahezu "wartungsfrei" sind. (Achte natürlich drauf, dass meine UDTs nicht unnötig aufgebläht sind)

Bei FBs mache ich das auch so ähnlich (da muß man bei der obigen Sequenz noch auf den AR2 und Instanzanfang achten). Dann sind die Variablen vom Typt UDTx so zu sagen die öffentlichen Daten des Objekts.
 
Zuletzt bearbeitet:
hast du shconmal was von multiinstanz fbs gehört?
ich sehe bei der art zu programmieren keinen vorteil was den verbrauch von dbs angeht.

der einzige vorteil für mich ist das beliebig viele bausteine auf die instanz oder teile davon (instanz = udt) zugreifen können.

und ich je nach ausbau einfach module auf die instanz stecken muss.
 
Hmm... eigentlich ja nicht schlecht. Frage. Wofür machst du die INOUT variable wo du dein UDT angibst?

Wenn ich das richtig verstehe, brauchst du doch nur einen pointer angeben, damit der FC weiß, welche structur vom array er nutzen soll?

Das andere ist, wie greifst du bei einer Visu auf diesen DB zu, der ja eigentlich nur den Array enthält!?


Vielleicht bin ich auch falsch abgebogen, nicht so einfach deine Beschreibung, wenn mans nicht selber gemacht hat. ;)
 
Zuviel Werbung?
-> Hier kostenlos registrieren
INOUT weils am einfachsten ist, der Baustein kopiert beim öffnen die Daten des anparametrierten Arrayelementes das ja aus dem selben UDT besteht in den UDT der INOUT, und am schluss automatisch wieder zurück.
Man muss sich also im gegensatz zur Pointerlösung um nix kümmern.
sicher geht das mit Pointern auch, wobei das meiner meinung nur dann ein vorteil wäre, wenn es wirklich schondender für die Zykluszeit wäre.

Wenn ich im UDT aus einem Reserve BOOL zb. das Symbol "Ventil_AUF" mache, dann habe ich das in allen DBs und auch in den "lokalen" Daten der entsprechenden FC´s auf einmal geändert...


Was soll die Visu dabei für ein Problem haben?
Das Array besteht aus n UDTs und gut ist.

die Visu greift auf "Anlage.Teil[x].Tasten.Funktion_Start" oder auf Anlage.Teil[x].Parameter.Vordruck_Max" zu...
 
hast du shconmal was von multiinstanz fbs gehört?
ich sehe bei der art zu programmieren keinen vorteil was den verbrauch von dbs angeht.

der einzige vorteil für mich ist das beliebig viele bausteine auf die instanz oder teile davon (instanz = udt) zugreifen können.

und ich je nach ausbau einfach module auf die instanz stecken muss.
Natürlich benutze ich Multiinstanz FBs.
Den letzten Satz hast du wahrscheinlich überflogen. Ich benutze die Methode für FBs, um global Daten dem FB zu geben/holen. Instanzdaten bleiben in der Instanz, aber der FB erzeugt ja auch Zustände für die restlichen Programmteile. Diese Zustände kann man teilweise auf den Ausgang legen, oder bei "grösseren Sachen" mit UDTs abwickeln.
Ich gebe beispielsweise einem FB einen Datensatz (irgendWasTypUDT)
Der FB soll mit den Daten etwas machen (manipulieren) und mir den manipulierten Datensatz wieder global zur verfügung stellen, damit ich bei Bedarf an jeder beliebeigen Stelle dran kommen kann. Zum Arbeiten braucht der FB Variablen (aktuelle Zeit, Flanke,....) Dies sind die Instanzdaten. Nach getaner Arbeit will ich aber meinen Datensatz wieder haben, ich will nicht daß der Datensatz in der Instanz gekapselt ist. Was ich meine hat nichts mit Instanzdaten zu tun, die bleiben, es geht um Übergabe von zusammengesetzten Daten.

Es gibt Kollegen, die geben dem FB DB-Nr, Anfangsadresse, den Rest (was nach dem Offset wo liegt) haben sie im Kopf und toben sich dann im FB mit AUF [DN-NR], .... , L DW xx, .... aus. Ich will im FB überhaupt keine Absoulutzugriffe haben, deshalb mache ich die Datenübergabe mit Variable(UDTx) -> AnyPointer-> TempVariable.
Klar kostet das mehr Speicher und auch mehr Zykluszeit, als wenn ich U M3.4 sage. Wenn man den Zugriff über Any-Pointer-> LokalVariable macht hält sich das aber in Grenzen.

Es kommt darauf an, welche Visualisierung man benutz, wir benutzen zu 99% Siemens (Flexible und WinCC). Dort kann man Strukturen definieren (Kopie der UDTs) und die Visu-Variablen vom Typ dieser Struktur definieren. In Bildbausteinen wird dann dem Bildobjekt die Strukturvariable übergeben.
Ist man nur an einer Variable interessiert, so definiert man die Variable einzeln, die Daten stehen ja global zur Verfügung,da sehe ich kein Problem.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Markus,
ich mache bei unseren Anlagen etwas ähnliches ... Die Anlagen sind meisst Rundtisch-Anwendungen. Hier habe ich für die max. mögliche Anzahl von Stationen einen DB mit Array of UDT aus dem ich mir für jede der Sationen die Einzel-UDT's heraushole und wieder hineinschreibe.
Der Vorteil, der sich für mich daraus ergeben hat ist :
- ich habe einen SCL-Baustein, der die Liste scannt und aus Einzel-Info's Sammel-Info's baut.
- derselbe Baustein macht eine Laufzeit- und Timeout-Messung der Stationen.
- ich muss den Baustein nicht für jede Anlage neu schreiben, sondern er passt für alle Anlagen (Standard-Baustein).

Ich habe es so verstanden, dass du etwas ähnliches vorhast.
Für mich ist es so, dass ich durch den Standard-Baustein mit den x-mal Allgemein-UDT's eine Menge Grundarbeit gespart habe. Die Zykluszeit ist dabei aber um einige ms gestiegen. Komfort hat halt auch seinen Preis.

Das schöne an der Geschichte ist, dass auch meine Mitarbeiter das Werk einsetzen können, ohne es selber programmieren zu können oder dessen Teilfunktionen. Es gibt dann halt ein paar Standard-FC's und die erledigen bei richtiger Parametrierung die ganze Grundarbeit im Programm.
 
Es kommt nicht auf die Datenmenge in der UDT an, es ist egal ob ich der Funktion eine Variable(UDTx) mit 2 Byte oder 200 Byte gebe.
Es sind die Zugriffe auf diese Variable innerhalb der FC, die entscheidend sind. Wenn man einer FC eine Variable vom typt UDTx übergibt, sieht die FC an ihrem Eingang einen 6 Byte Pointer (DB-NR|Bereichszeiger) und nicht die tatsächliche Datenmenge.
Stimmt, das Problem ist, dass der Compiler vor jedem Zugriff auf die INOUT-Variablen das Adressregister neu berechnet und dann indiziert (speicherindirekt) darauf zugreift. Das schlägt sich (meiner Erfahrung nach) beim Speicher mit dem faktor 1,5 - 4 nieder, bei der Zykluszeit im Faktor 5-20 (je nachdem, wieviele Daten per INOUT übergeben werden).

Um die Zugriffszeit erträglicher zu machen gehe ich so vor :

1- UDT definieren
2- Variable im DB vom Typt UDTx anlegen
3- FC Eingangsvariable definieren , aber der Eingang ist vom Typ "Any-Pointer" und nicht UDTx
4- In der FC kopieren des Any-Pointers mit Hilfe der Adressregister auf einen temp. Any-Pointer
5- Definieren einer Temp. Variablen vom Typ UDTx
5- Mit SFC 20 (Quelle = temp. Any Pointer, Ziel = temp. Variable vom Typ UDTx) den Inhalt der DB-Variablen in Temp. Bereich kopieren
6- Nun erfolgt der Zugriff auf UDT Variablen "Lokal"
7- Nach der Verarbeitung mit SFC 20 die Temp. Variable in DB kopieren (Quelle = Temp. Variable(UDTx), Ziel = Temp. Any Pointer)
Löse ich genauso. Diese Variante hat zudem den Vorteil, dass - sollte man wider Erwarten den UDT so ändern müssen, dass sich der Zeitstempel ändern - man nur den UDT-Aufruf in den Lokaldaten des FC 1 x ändern muss + die DB-Deklaration aktualisieren (also ca. 3 Minuten Arbeit).

Hab das zuletzt bei einer Anlage mit 68 identischen Kühlkreisen so gelöst. Zykluszeit auf einer 317-2PN/DP mit INOUT 65ms, mit Lokaldaten 4ms.

Um den FC problemlos erweiterbar zu machen, liegen die gesamten daten (z.B. Eingänge/Ausgänge), welcher vom aufrufenden FC an die einzelnen FC-Instanzen übergeben werden bzw. welche dieser rausgibt, ebenfalls im UDT. Diese werden dann im aufrufenden FC direkt per DB-Zugriff zugewiesen bzw. gelesen.

mfg
Maxl
 
...
da du es jetzt noch einmal aufgerollt hast, Maxi ...

Ihr wisst schon, dass der ANY-Pointer sogar 12 Byte lang ist und auch nicht direkt auf eine Speicherstelle zeigt, sondern bei Gebrauch von der CPU erst berechnet wird ...:???:
 
Zuviel Werbung?
-> Hier kostenlos registrieren
angeregt durch ein beispiel vonserem geschätzten mtglied "onkel dagobert" (welcher übrigens einen klasse programmierstil hat), habe ich das beim letzten projekt so gemacht.

ich bastel erst einen udt mit allen variablen.

den setz ich in einem aglobal db beliebig oft in ein array.

ich mache das was vorher ein fb gemacht hat mit einer fc, diese fc hat neben sonstigen in und out auch eine inout die als mein udt deklariert ist.

Hallo Markus,

dass das mit dem UDT Komfort in die Programmierung bringt habe ich verstanden, weshalb du jedoch den FB durch den FC ersetzen tust ist mir schleierhaft ??

@Onkel Dagobert:
Alles Gute zum 60ten.
 
60?

..weshalb du jedoch den FB durch den FC ersetzen tust ist mir schleierhaft ??
Bin zwar nicht Markus aber der Vorteil Nr.1 ist wohl die problemlose Verwendung der Adressregister. Das bietet sich in komplexen, gut überlegten Funktionen oftmals an, besonders wenn man auf verschiedene Datenbereich zugreifen muss. FBs haben natürlich weiterhin ihre Berechtigung.

..@Onkel Dagobert:
Alles Gute zum 60ten.
Hä? Ich bin im zartem Alter von 41 Jahren :confused: ???


Gruß, Onkel
 
...
da du es jetzt noch einmal aufgerollt hast, Maxi ...

Ihr wisst schon, dass der ANY-Pointer sogar 12 Byte lang ist und auch nicht direkt auf eine Speicherstelle zeigt, sondern bei Gebrauch von der CPU erst berechnet wird ...:???:

In einer FC kann man die Daten über indirekte Adressierung mittels der Adressregister auch ganz einfach in einer Schleife kopieren. Der Aufwand ist nicht größer, als die Any-Pointer in den Lakaldatenberich zu übertragen. Ich handhabe das so.


Gruß, Onkel
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Damit handelst du dir aber ein, dass der FC nicht wie ein FB im "angegebenen Pfad" beobachtet werden kann. Da muss der entsprechende Global-DB wieder hinzugezogen werden und separat beochbatet werden, was wiederum eher unhandlich ist.
Der zweite Nachteil ist, dass du beim FC-Aufruf mit UDT's nicht mehr einfach an den Ein/Ausgänge beobachten kannst, wie der Baustein grade gefüttert wird.
Allerdings weiss ich jetzt auch nicht, wieviele diskrete Eingänge denn die Bausteine haben, wenn das sehr viele sind, ist anstatt die Variablen einzeln aufzuschalten eine DB-Nummer unter Umständen auch eine Lösung.
(So wie das z.B. manche früher mit Schrittketten-Verwaltungen gemacht haben). Die Technik ist jedoch eher die noch aus s5-Zeiten stammende.

Es hindert natürlich auch niemand uns daran, UDTs an FBs zu übergeben.

Die Methode von LL halte ich für derzeit am geschicktesten, was sich mit Step 7 machen läßt. (Und mache das auch so, wo ich die Gelegenheit dazu habe).
 
...da du es jetzt noch einmal aufgerollt hast, Maxi ...
Maxl mit L
Ihr wisst schon, dass der ANY-Pointer sogar 12 Byte lang ist und auch nicht direkt auf eine Speicherstelle zeigt, sondern bei Gebrauch von der CPU erst berechnet wird ...:???:
Naja, bei der Anwendung, welche ich zuletzt hatte, wurden 2 UDTs mit je 40 Bytes größe übergeben, wobei einer der beiden UDTs größtenteils per BOOL angesprochen wurde. Die Variante mit ANY-Pointern entstand einfach aus Speicher- und Zykluszeitgründen.

Multiinstanz-FBs wollte ich nicht verwenden, da ich mit denen schon schlechte Erfahrungen gemacht hab (abgesehen sind die bei uns nicht erlaubt). Und die ANY/FC-Kombination hat einfach gegenüber dem FB den Vorteil, dass man nicht so große Zeitstempel-Probleme kriegt, wenn man mal in der Deklaration was ändert.

Ich sag nur: jedem das Seine - das ist so ein typischer Fall, wo sich jeder Programmierer frei austoben kann. Nur soll keiner jammern, wenn nach 3 Jahren mal was dazu gemacht werden muss, er dann 68 FB-Instanz-DBs aktualisieren muss, und die Istwerte innerhalb dieser DBs weg sind.


mfg
Maxl
 
Zuviel Werbung?
-> Hier kostenlos registrieren

Sorry, hatte beim posten meine Brille wohl nicht auf. Dann rutsch einem schon einmal ein "i" als "l" durch ...:rolleyes:

... die ANY/FC-Kombination hat einfach gegenüber dem FB den Vorteil, dass man nicht so große Zeitstempel-Probleme kriegt, wenn man mal in der Deklaration was ändert.

In dem Punkt kann ich dir nicht widersprechen. Wenn man mal an der UDT herumgespielt hat (ohne dabei ihre Größe oder Aufbau geändert zu haben) dann kann das ganz schön nerven.

Trotz alledem, jedes Ding hat halt seine Vor- und Nachteile ...;)
 
Hallo,

ich habe einen FC programmiert, entsprechend mit Parametern InOut, Input, Output und Temp deklariert. Wie bekomme ich den FC Multiinstanzfähig?

Danke im Voraus.
 
Ein FC kann keine eigenen Instanzen bilden (hat also direkt keinen eigenen Speicherbereich).
Der hier geschilderte "Trick" ist, dass die "Instanzdaten" in einem Global-DB liegen (als UDT) und dem FC über seine Schnittstelle übergeben werden. Sofern man nun die Merkfähigkeit, die vielleicht ja benötigt wird, in diesem UDT legt hat man quasi eine Art Instanzierung geschaffen ...
Es ist halt eine Frage was wann sinnvoller ist - also ein FB oder ein FC mit dieser Quasi-Instanz ...
 
Zurück
Oben