Grundsatzfragen zur Restrukturierung einer bestehenden Programmstruktur

Geextah

Level-2
Beiträge
12
Reaktionspunkte
0
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo, Forengemeinde,

derzeit bin ich an der Entwicklung einer Software zur automatisierten Quellcodeerstellung für Siemens TIA (das soll hier aber bitte nicht das Thema sein).

Da die SPS-Entwickler selbst über Perfomanceprobleme hinsichtlich Zykluszeit klagen, habe ich es mir einmal angeschaut und versucht, es sehr vereinfacht mit Xmind aufzuzeigen. Parallel dazu noch ein Entwurf (ebenfalls sehr vereinfacht) von mir, den ich zukünftig dafür hernehmen würde.

Die Anlage besteht aus mehreren Stationen, die über Transportbändern miteinander verbunden sind. Man könnte sich grob an folgendem Bild orientieren:

KTS2.jpg

Bestand:
Bestand.png

Das Programm besteht im Wesentlichen aus den beiden Funktionsbausteinen (es gibt noch einige mehr FBs, allerdings wird 95 % des Programms in diesen beiden FBs aufgerufen) FB_Process und FB_Actors, die beide nacheinander im OB1 aufgerufen werden.

Im statischen Bereich des FB_Process sind alle Stationen definiert, die in der Anlage verbaut sind, z.B. Bänder oder Umlenkstationen (nennen wir diesen der Einfachheit FB_Station). Je nach Anlage sprechen wir hier von rund 100 Stationen.

Die Sensoren und Freigaben sind direkt an FB_Station angebunden. Schrittkette der Station befindet sich innerhalb des FBs. Kommunikation nach "außen" wird über den DB_Communication Datenbaustein realisiert. Zylinder oder Motoren tauschen die Informationen über CylinderControl bzw. EngineControl aus.

Nach Abarbeitung der Stationen werden die Aktoren im FB_Actors aufgerufen. Ebenfalls sind dort alle Aktoren der Gesamtanlage (Zylinder, Servoantriebe, Motoren, Pneumatikventile) im statischen Bereich definiert.

Ob nun ein Aktor angesteuert wird oder nicht, erhält er über den DB_Communication Baustein.

Problematisch finde ich:
- Jede Station/Aktor hat rund 3-4 Strings als Input
- Jede Station/Aktor hat 4-5 Strings im statischen Bereich
- Beim Aufruf eines FB wird der komplette statische Bereich in den Speicher (Akku?) geladen. Unabhängig davon ob ich alle Daten für diesen Zyklus brauche oder nicht.
- Beim Aufruf der FBs werden sämtliche Inputs (vor allem die der Strings) kopiert (siehe SPS-Leitfaden 3.3.3)

Nachdem der FB abgearbeitet wurde, wird der Inhalt des Speichers freigegeben oder neu überschrieben.
Bitte korrigiert mich, wenn ich hier falschliege!
Bei dieser Art des Programmaufbaus wird quasi (vereinfacht ausgedrückt) beim Aufruf des FB_Process das halbe Programm in den Speicher geladen und dann beim Aufruf des FB_Actors die zweite Hälfte.

Neuer Ansatz:
NeuerAnsatz.png

Gesehen als Pendant zum vorherigen Schema, wird der komplette Ablauf im FC_Process stattfinden. Jede der 100 Stationen erhält einen separaten FC (z.B. FC_Station1) in dem die zugehörige Schrittkette (FC_Specific_Station), sowie die zu dieser Station zugehörigen Aktoren aufgerufen werden.

Die Daten für den Datenaustausch zwischen Station, Aktor und HMI würden in DB_Stations, DB_Cylinders und DB_Engines liegen.

Die einzelnen FCs (FC_Specific_Station, FC_Cylinder, FC_Engine) greifen per Referenz auf das entsprechende "Ablagefach" im Datenbaustein zu. Die ganzen Strings würden in den Datenbausteinen liegen und müssten so nicht mehrmalig kopiert werden. Ein mehrmaliges Kopieren ist so ausgeschlossen bzw. würde diese erst gar nicht in den Speicher gelangen. Es würden auch generell nur die Daten verwendet werden, die im Zyklus jetzt gerade gebraucht werden, und nicht immer alle, da es keinen statischen Bereich gibt.

---

Im Leitfaden und auch im Siemens Forum wird mehr oder weniger davon geschwärmt und empfohlen, man solle oder könne alles recht einfach und dynamisch mittels Array lösen. Gleichzeitig wird davor "gewarnt" nicht optimierte Bausteine zu verwenden, da ja das Programm sehr langsam werden würde.
Jedoch habe ich eine interessante Auflistung der Zugriffsgeschwindigkeiten gefunden. Wenn dem so wäre, ist der Zugriff auf nicht optimierte Bausteine schneller als das Benutzen von Arrays?

3.3.3.jpg

zugriff.jpg


Nun noch ein paar Infos zu mir.
Ich selbst habe rund 5 Jahre SPS (300er; ausschließlich in AWL) programmiert und bin inzwischen seit mehr als 7 Jahren im Umfeld der Hochsprachen unterwegs.

Vielen Dank und auf einen konstruktiven Austausch 😉
 
Welche SPS (MLFB) ist im Einsatz und welche Zykluszeit hat sie? Welche Zykluszeit wird benötigt?
Grundsätzlich kann man natürlich zykluszeitoptimiert programmieren, wenn dann aber die Verständlichkeit der Software leidet, würd ich lieber ne grössere CPU einsetzen falls möglich. Evtl. reichen ja schon die "neueren" Modelle.
Viel Zykluszeit verbrät bei den alten auch Kommunikation etc...
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Nabend,

dein altes SPS-Konzept kenne ich als funktionsorientierte Programmierung.
Das hat man früher oft gemacht, also dieses getrennte strukturieren nach Logik und I/Os.
Sehr flexibel, skaliert aber miserabel & hat viel Potenzial sich mal irgendwo zu verhaspeln.

Dein neues Konzept sieht für mich nach einer, für neuere SPS-Programme, üblichen Struktur aus.
Damit solltest du im Bezug auf Bibliotheken & objektorientierte Ansätze ganz gut fahren.

Nachdem der FB abgearbeitet wurde, wird der Inhalt des Speichers freigegeben oder neu überschrieben.
Auch die neueren SPSen arbeiten im Grunde wie die älteren Generationen.
Sowas wie Speicher allocieren/freigeben gibt es da, zumindest auch Sicht des Anwenderprogramms, nicht.
Ist bei 1500ern kein Thema mit dem man sich auseinandersetzen müsste (oder war es bisher zumindest für mich nicht).
Die temp-Variablen aber erst mal zu initialisieren (bzw. erst schreiben, dann lesen sicher zu stellen) mache ich aber grundsätzlich aus Paranoia.

Die einzelnen FCs (FC_Specific_Station, FC_Cylinder, FC_Engine) greifen per Referenz auf das entsprechende "Ablagefach" im Datenbaustein zu.
Hier solltes du drauf achten, dass die Bausteine auch wirklich CallByReference nutzen.
Tia macht hier auch Unterschiede zwischen elementaren und strukturierten Datentypen.
Die Tabelle im Programmierleitfaden dazu hast du unten in dem Screenshot ja schon gefunden.

Es würden auch generell nur die Daten verwendet werden, die im Zyklus jetzt gerade gebraucht werden, und nicht immer alle, da es keinen statischen Bereich gibt.
Nur weil es einen statischen Bereich gibt, wird dieser nicht prinzipiell erstmal geladen.
Im Endeffekt heißt "FB" nur, dass dieser einen eigen Speicherbereich im Datenspeicher besitzt & auch Infos über mehrere Zyklen speichern kann.
Und man kann die Variablem in einer Instanz auch von anderen Funktionen aus referenzieren, was ich teilweise zum Ankoppeln von optionalen Softwaremodulen an eine Instanz nutze. Beispielsweise für sowas.

Und ein FC behält nur das, was er im aktuellen Zyklus als Aktualparameter bekommen hat.
Daten rein => Codemagic => Rückgabewert raus, fertig.
Und man in SCL eine Funktion mit Rückgabewert als Aktualparameter verwenden (spart Geschiebe über temp-Variablen).

Performance-technisch haben FBs/FCs (meines wissens) keinen praktischen Geschwindigkeitsunterschied.

Im Leitfaden und auch im Siemens Forum wird mehr oder weniger davon geschwärmt und empfohlen, man solle oder könne alles recht einfach und dynamisch mittels Array lösen
Du schreibst ja, dass du aus dem Hochsprachenbereich kommst.
Ich gehe daher davon aus, dass du halbwegs weißt was man mit Arrays anstellen kann.
In SPSen hast du lediglich ein paar SPS-spezifische Einschränkungen, beispielsweise dass du die Größe von Arrays zur Laufzeit nicht ändern kannst (Definieren über Konstanten geht aber....und Arrays, die nicht bei 0 beginnen (ノ`□´)ノ⌒┻━┻ ).

Persönlich kenne ich genug SPSler für die Arrays=Hexerei/komisches, neumodisches Zeugs sind.
Betreffend "dynamisch" kann man z.B. mit Array[*] oder Variant tatsächlich sehr flexibel programmieren.
Deswegen alles in Arrays zu stopfen ist aber auch nicht die ultimative Lösung.
In der Siemens-LGF Bibliothek gibt es ein paar hübsche Beispiele zu Bausteinen, die beispielsweise unabhängig von der Arraygröße oder Datentyp eine Aufgabe erledigen.

Gleichzeitig wird davor "gewarnt" nicht optimierte Bausteine zu verwenden, da ja das Programm sehr langsam werden würde.
In der Praxis ist es performance-seitig ziemlich irrelevant ob du optimierte oder nicht oprimierte Daten/Bausteine verwendest, du solltest nur darauf achten beides nicht zu mischen.
Beide Datensysteme haben unterschiedliche endianes, also muss die SPS bei Operationen zwischen optimiert/nicht optimiert immer die Daten zusätzlich umformatieren (was Rechenzeit kostet).
Rest der Unterschiede siehe hier.

Wenn dem so wäre, ist der Zugriff auf nicht optimierte Bausteine schneller als das Benutzen von Arrays?
Der Liste nach ja.
Hab aber noch nie handfeste Zahlen gefunden wie groß der Geschwindigkeitsunterschied genau ist.
Ich würde deshalb aber nie anfangen einen nicht optimierten Array-DB per Pointer durchzunudeln.
Wobei ich nicht sicher bin ob Pointer nicht unter 5. in der Aufzählung fallen.
Für mich gilt, wenn nicht extrem Performance-kritisch: Code-Lesbarkeit/Wartbarkeit > Performance.
 
Ist denn der Zugriff auf die CallByReference Daten in den Bausteinen selbst bei der S7 1200/1500 vergleichbar mit der S7 300/400?
Denn dann spart man zwar beim Aufruf, aber der Zugriff auf die einzelnen Elemente wäre Systemintern aufwendig und verschlingt auch wieder ordentlich an Zykluszeit.
 
Da wäre jetzt die Frage: wie viel ist "ordendlich"?
In den Datenblättern habe ich leider bisher nur Angaben zu Arimetrik-Operationen gefunden.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Da die SPS-Entwickler selbst über Perfomanceprobleme hinsichtlich Zykluszeit klagen, habe ich es mir einmal angeschaut und versucht, es sehr vereinfacht mit Xmind aufzuzeigen. Parallel dazu noch ein Entwurf (ebenfalls sehr vereinfacht) von mir, den ich zukünftig dafür hernehmen würde.

Hiermit könnte man die Perfomanceprobleme recht gut analysieren.
 
Danke für euer reges Interesse.

Welche SPS (MLFB) ist im Einsatz und welche Zykluszeit hat sie? Welche Zykluszeit wird benötigt?

Es werden aktuell ausschließlich 1500er CPUs eingesetzt. Als Siemens den OPC Server auf ihren CPUs implementiert hatte, hatten diese leider unterschiedliche Entwicklungsstände auf den einzelnen CPUs bzw. die 1200er Reihe hat auch noch funktionstechnisch massiv nachgehinkt.

Grundsätzlich kann man natürlich zykluszeitoptimiert programmieren, wenn dann aber die Verständlichkeit der Software leidet, würd ich lieber ne grössere CPU einsetzen falls möglich. Evtl. reichen ja schon die "neueren" Modelle.

Ich hätte gerne einen Kompromiss aus beiden. Je "aufgedröselter" die Struktur, desto flexibler bin ich, nicht standardisierte Abläufe oder Gegebenheiten zu implementieren. Der Mehraufwand an Softwarecode, der dadurch entsteht, ist durch die Automatisierung der Codegenerierung irrelevant.
Je optimierter der Programmcode an sich, desto kleiner kann meine CPU sein und das schlägt sich in den Kosten natürlich auch wieder nieder. Dieser Schritt kommt allerdings erst im Nachgang. Bis dahin sollten die 1200er OPC technisch auf demselben Stand sein wie ihre großen Brüder…

dein altes SPS-Konzept kenne ich als funktionsorientierte Programmierung.
Das hat man früher oft gemacht, also dieses getrennte strukturieren nach Logik und I/Os.
Sehr flexibel, skaliert aber miserabel & hat viel Potenzial sich mal irgendwo zu verhaspeln.

Dein neues Konzept sieht für mich nach einer, für neuere SPS-Programme, üblichen Struktur aus.
Damit solltest du im Bezug auf Bibliotheken & objektorientierte Ansätze ganz gut fahren.

Mit alt meinst du den Bestandscode? Hast du Infos dazu, was heute und früher "Stand der Technik Struktur" ist?

Nach welcher Struktur hast du dein Programm aufgebaut?

Nur weil es einen statischen Bereich gibt, wird dieser nicht prinzipiell erstmal geladen.
Im Endeffekt heißt "FB" nur, dass dieser einen eigen Speicherbereich im Datenspeicher besitzt & auch Infos über mehrere Zyklen speichern kann.

Dem kann ich jetzt zustimmen. Ich ging immer davon aus, dass dieser komplett geladen wird, das ist aber nicht so.

Performance-technisch haben FBs/FCs (meines wissens) keinen praktischen Geschwindigkeitsunterschied.

Aktuell versuche ich sämtliche "Arten", wie man seine Daten lagern kann bzw. deren Zugriff darauf zu analysieren und in Excel niederzuschreiben.
Darin versuche ich, unter anderem, genau dieses und oben stehendes Thema ("FB lädt seinen statischen Bereich immer komplett") zu analysieren.
Sobald ich damit fertig bin, lasse ich es euch wissen.👍

Für solch detaillierte Fragen findet man irgendwie nichts in den Handbüchern von Siemens.

Hiermit könnte man die Perfomanceprobleme recht gut analysieren.

Danke! Genau das hatte ich in TIA gesucht, bevor ich es händisch in XMind gezeichnet hatte. 🤦‍♂️😅
 
Es werden aktuell ausschließlich 1500er CPUs eingesetzt.
ja, welche halt? Und um wieviel muss die Zykluszeit verkürzt werden??? Auf die Hälfte, auf ein Hundertstel? Werd doch mal konkret, sonst ist das doch nur Palaberei um den heissen Brei... ähm, ergebnisorientiertes Arbeiten würde mal wohl dem Chef vorschwärmen...
Je optimierter der Programmcode an sich, desto kleiner kann meine CPU sein und das schlägt sich in den Kosten natürlich auch wieder nieder. Dieser Schritt kommt allerdings erst im Nachgang.
Ja, macht aber nur bei Serienmaschinen Sinn. Evtl. wird die benötigte Verkürzung aber alleine schon durch die "neuen" 1500er Typen erreicht...
Ansonsten schaut man eher selten auf optimierten Code sondern eher auf Wartungsfreundlichkeit.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Ist denn der Zugriff auf die CallByReference Daten in den Bausteinen selbst bei der S7 1200/1500 vergleichbar mit der S7 300/400?
Denn dann spart man zwar beim Aufruf, aber der Zugriff auf die einzelnen Elemente wäre Systemintern aufwendig und verschlingt auch wieder ordentlich an Zykluszeit.
Orgendwo gabs hier schonmal Diskussionen darüber. Ist schon ewig her...

Hier z.B.

 
ja, welche halt? Und um wieviel muss die Zykluszeit verkürzt werden??? Auf die Hälfte, auf ein Hundertstel? Werd doch mal konkret, sonst ist das doch nur Palaberei um den heissen Brei... ähm, ergebnisorientiertes Arbeiten würde mal wohl dem Chef vorschwärmen...
Ich denke es ging bei seiner Frage generell um "wie vermeide ich unnötigen Leistungsbedarf?", nicht um ein "ich muß x% schneller werden".

Wenn man schon Mal am neu Konzeptionieren ist, kann man sich darüber ja Mal Gedanken machen.
Und wenn's doch Mal eng wird, kann man immer noch mit ner größeren SPS das Performance-Problem erschlagen ¯⁠\⁠_⁠(⁠ツ⁠)⁠_⁠/⁠¯

Ansonsten schaut man eher selten auf optimierten Code sondern eher auf Wartungsfreundlichkeit.
Bei den Kosten für ne größere SPS vs. mein Stundensatz stimme ich dir absolut zu.

Z.B. Parameter-Daten in einem entsprechenden DB zentralisiert zur Verfügung zu stellen, wie @Geextah oben erwähnt hat, wäre Beispielsweise ein Schritt in diese Richtung.
Seine Programmstruktur in Richtung objektorientierte Programmierung, wie in seinem neuen Strukturbeispiel gezeigt, zählt für mich ebenfalls als "wartungsfreundlich".

Mit alt meinst du den Bestandscode? Hast du Infos dazu, was heute und früher "Stand der Technik Struktur" ist?
Das ist jetzt ne gute Frage...
So nen richtigen "Standard" gibt es da (meines Wissens) nicht.

Ich kenn es eben aus Bestandsprogrammen, die ihre Wurzeln noch in S5-Programmierung hatten.
Da ist es oft üblich die Funktionen & Daten danach zu strukturieren was sie sind.
Beispielsweise die Variablen mit präfixen nach DI, DQ, PEW etc. zu sortieren.
Da hieß "Kuelmodul2UmwaelzDiffdruck" eher "pewUmwaelzDiffdruckKuelmodul2".
Gibt nicht aus Langeweile im Siemens Styleguide Richtlinien zum Thema "Benenn den 💩 wartungsfreundlich".
Das war dann eher strukturiert & benannt wie es im ePlan eben nacheinander drin stand.
Und die Schaltplan-Götter hatten selten ein gespür dafür sich ordentlich mit den Bitschibsern abzustimmen.

Oder das zuerst alle Eingänge aufbereitet wurden, dann alles durch funktionsbezogene FCs gewürgt wurde, um dann Merker etc in einem FC auf die Ausgänge zu schreiben.
Ohne irgendwelche Kapselung zwischen den logischen Funktionen.
Die funktionsbezogenen Bausteine waren dabei meist eher getrennt in was sie gemacht haben, beispielsweise störmeldungen, hmi Schnittstelle, Antriebe, Ventilansteuerung, ....
Nicht nach Hierarchie sortiert.

Also nicht z.B. nach Zelle => Station => Baugruppe => Bauteil, wie es bei Konzepten in Richtung Objektorientierung üblich ist.
Und im Bauteil dann Störmeldungen, I/O Zugriffe usw abzuhandeln.
 
Ohne irgendwelche Kapselung zwischen den logischen Funktionen.
Die funktionsbezogenen Bausteine waren dabei meist eher getrennt in was sie gemacht haben, beispielsweise störmeldungen, hmi Schnittstelle, Antriebe, Ventilansteuerung, ....
Nicht nach Hierarchie sortiert.

Also nicht z.B. nach Zelle => Station => Baugruppe => Bauteil, wie es bei Konzepten in Richtung Objektorientierung üblich ist.
Und im Bauteil dann Störmeldungen, I/O Zugriffe usw abzuhandeln.
Beides hat seine Vorteile... ich würd keines der beiden Konzepte per se als "besser" bezeichnen.

Beim ersten Konzept kann man ja auch unterstrukturieren "Meldungen_Anlagenteil1" "Meldungen_Anlagenteil2" usw...
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Beispielsweise die Variablen mit präfixen nach DI, DQ, PEW etc. zu sortieren.
Da hieß "Kuelmodul2UmwaelzDiffdruck" eher "pewUmwaelzDiffdruckKuelmodul2".

Die Verwendung eines präfixes wie DI / DQ / AI etc. bei Hardwareeingängen finde ich persönlich hilfreich.

Offtopic:
Davon abgesehen, tue ich mich schwer damit, Siemens und OOP, solange es keine wirkliche Kapselung der Daten gibt. Vererbung und Polymorphie las ich erstmal aussen vor.
 
Zuletzt bearbeitet:
Zurück
Oben