# WinCC V7.2 C-Skript Performance beim ersten lesen von 150 String-Variablen



## RONIN (4 Dezember 2013)

Hallo Leute!
WinCCv7.2 CPU315PN/DP

Ich hab eine Frage zur Anbindung/Aktualisierung von Variablen die in Skripten verwendet werden bzw. wie man beim Zugriff auf viele Datenpunkte aus einem
Skript heraus die Performance verbessern kann. Und zwar nur nach dem ersten Öffnen.

Folgendes Setting:
 Ich habe eine kleine Rezeptsteuerung mit 150+ Artikel (string[32]) auf der SPS in einem DB. Die Strings sind in WinCC ganz normal angelegt.
Dann habe ich ein kleines C-Skript geschrieben das die Strings mit einem Filterkriterium vergleicht und dann eine Liste der entsprechenden ausgibt.

Dazu müssen natürlich alle 150 Einträge geprüft werden. Das Skript ist schon so weit optimiert das es nur solange sucht bis entweder die Liste voll ist oder
 alle Einträge in denen wirklich was drin steht durchsucht wurden.



ZUM PROBLEM:
 Nach dem Öffnen des Bilds dauert die Suche bzw. die Erstellung der Liste recht lange (bis zu 2min). Hat man dann erst mal alle Einträge durchgeschaut, dass heißt alle
Strings min. einmal mit GetTagChar gelesen, dauert der Ablauf des Skripts nur noch max. 0,5sek. Das gilt dann solange bis ich das Bild wechsle, beim nächsten Öffnen 
dauerts wieder genauso lange...

Warum ist das so bzw. was könnte ich dagegen machen...


----------



## Thomas_v2.1 (4 Dezember 2013)

Gibt bei Siemens ein FAQ dazu:
http://support.automation.siemens.com/WW/view/de/28017376

Find ich zwar auch nicht schön da 150 unsichtbare Objekte im Bild liegen zu haben, aber wenn es nicht anders geht.
Wobei ich gespannt bin ob das was bringt, wenn du deine Funktion ausführst unmittelbar nachdem das Bild aufgerufen wurde. Entweder es dauert die 2 Minuten die Variablen aus dem Skript beim Datenmanager anzumelden, oder es dauert so lange bis die Daten aus der SPS gelesen wurden. Wobei 150x 32-Zeichen Strings jetzt nicht die übermäßig große Datenmenge darstellen. Hast du nebenher noch viele Variablen im Tag- oder Alarmlogging verwendet? Denn das kann die Kommunikationsleistung zu anderen Variablen auch gehörig einbremsen, da diese zyklisch immer vorrangig abgefragt werden.


----------



## Thomas_v2.1 (5 Dezember 2013)

Ich habe mir mal mit Wireshark angesehen was WinCC da so treibt wenn man die Variablen nur über ein C-Skript abfragt. Kein Wunder dass das so lange dauert. Wenn du deinen ersten String mit GetTagChar() abfragst, wird nur diese eine Variable aus der SPS gelesen. Beim nächsten GetTagChar dann die erste UND die zweite, beim nächsten dann die erste, zweite und dritte usw. Das heißt, wenn du deinen 150ten String einliest, wurde der erste 150 mal aus der SPS gelesen, die zweite 149 mal usw. Ich habe es grade nur mit 16 Strings getestet, das benötigt schon 8 Sekunden.

Vorschlag:
Nicht GetTagChar verwenden, sondern GetTagMultiWait. Und diese zu einer oder mehreren Aufrufen gruppieren. Das geht dann Ratzfatz...

Eine 16er Gruppe z.B. so:

```
char varname[30][30];
char* value[30];
int i;
BOOL ok;

for (i = 0; i <= 15; i++) {
  sprintf(varname[i], "s001_%d", i +1);
}

ok=GetTagMultiWait("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 
  varname[0], &value[0],
  varname[1], &value[1],
  varname[2], &value[2],
  varname[3], &value[3],
  varname[4], &value[4],
  varname[5], &value[5],
  varname[6], &value[6],
  varname[7], &value[7],
  varname[8], &value[8],
  varname[9], &value[9],
  varname[10], &value[10],
  varname[11], &value[11],
  varname[12], &value[12],
  varname[13], &value[13],
  varname[14], &value[14],
  varname[15], &value[15]
);


for (i = 0; i <= 15; i++) {
   printf("Variable '%s' = '%s'\n", varname[i], value[i]);

}
```


----------



## RONIN (9 Dezember 2013)

Thomas_v2.1 schrieb:


> Gibt bei Siemens ein FAQ dazu:
> http://support.automation.siemens.com/WW/view/de/28017376
> Find ich zwar auch nicht schön da 150 unsichtbare Objekte im Bild liegen zu haben, aber wenn es nicht anders geht.



Sehr interessant! Ich hab dann auf dieser Basis ein paar Experimente durchgeführt und eine Methode stellte sich als ganz passabel raus.
  In meinem Basis-Framework (zu jederzeit sichtbar) habe ich ein paar Textfelder platziert und per Dynamik-Dialog eine Dynamik erstellt die auf alle String-Variablen zugreift.
Und zwar habe ich mit Excel ein kleines VB-Skript erstellt welches automatisch die Variablennamen generiert (Damit ich nicht alles tippen muss).
Ergebnis: 'SPZM_Art1.ArtBez' && 'SPZM_Art2.ArtBez' && .... 'SPZM_Art150.ArtBez'
Diese Riesenwurst habe dann einfach in irgendeine Dynamik am Textfeld kopiert (Eigenschaft Text) und habe sie dann in ein C-Skript umgewandelt

```
Ergebnis:
// WINCC:TAGNAME_SECTION_START
// syntax: #define TagNameInAction "DMTagName"
// next TagID : 151
#define TAG_1 "SPZM_Art1.ArtBez"
#define TAG_2 "SPZM_Art2.ArtBez"
...
#define TAG_150 "SPZM_Art150.ArtBez"
// WINCC:TAGNAME_SECTION_END

// WINCC:PICNAME_SECTION_START
// syntax: #define PicNameInAction "PictureName"
// next PicID : 1
// WINCC:PICNAME_SECTION_END

if ((double)GetTagDouble (TAG_1) && (double)GetTagDouble (TAG_2) && (double)GetTagDouble (TAG_3) && (double)GetTagDouble (TAG_4) && (double)GetTagDouble (TAG_5) && (double)GetTagDouble (TAG_6) .........)
  return "??????";
else
  return "??????";
```
Danach habe ich die ganzen GetTagDouble-Zugriffe im unteren Teil gelöscht und nur noch das return übrig gelassen.  Damit WinCC nicht versucht alle 150 Tags zyklisch zu lesen.
Und siehe da! Der Zugriff auf die 150 Strings im anderen Skript wurde von ca. 2min auf ne gute Sekunde reduziert.
Kann mir einer dieses merkwürdige Verhalten erklären???? :shock:



Thomas_v2.1 schrieb:


> Wenn du deinen ersten String mit GetTagChar() abfragst, wird nur diese eine Variable aus der SPS gelesen. Beim nächsten GetTagChar dann die erste UND die zweite, beim nächsten dann die erste, zweite und dritte usw. Das heißt, wenn du deinen 150ten String einliest, wurde der erste 150 mal aus der SPS gelesen, die zweite 149 mal usw. Ich habe es grade nur mit 16 Strings getestet, das benötigt schon 8 Sekunden.


:shock: Wie Bitte? Das heißt wenn man Constraints wie:

```
for(i = 0; i<=150; i++
{
     sprintf(CharVarName,"SPZM_Art%d.ArtBez,i);
     GetTagChar(CharVarName);
}
```
verwendet wird also folgendermaßen vorgegangen????
i=1: Lesen von: SPZM_Art1.ArtBez
i=2: Lesen von: SPZM_Art1.ArtBez; SPZM_Art2.ArtBez
i=3: Lesen von: SPZM_Art1.ArtBez; SPZM_Art2.ArtBez; SPZM_Art3.ArtBez
i=150: Lesen von: SPZM_Art1.ArtBez; SPZM_Art2.ArtBez; SPZM_Art3.ArtBez; ........................................................................... SPZM_Art150.ArtBez

*Ernsthaft?!?! Erklärt auch warum das lesen der 150 Strings zuerst schneller geht und je höher die Nummer (1-150) desto langsamer wird es.
 Was haben den die Leute die sich das ausgedacht haben geraucht? Gilt das auch für andere Typen außer GetTagChar????

Das mit dem GetTagMultiWait werde ich natürlich auch probieren!*

DANKE erstmal!


----------



## Thomas_v2.1 (10 Dezember 2013)

RONIN schrieb:


> Ernsthaft?!?! Erklärt auch warum das lesen der 150 Strings zuerst schneller geht und je höher die Nummer (1-150) desto langsamer wird es.
> Was haben den die Leute die sich das ausgedacht haben geraucht? Gilt das auch für andere Typen außer GetTagChar????



Folgende Erklärung aufgrund meiner Beobachtungen für das Verhalten:

Angenommen ich will in einem C-Skript mit GetTag Variablenwerte lesen, die bisher noch nie aus der SPS gelesen wurden, z.B. weil diese eben in keinen Bild angezeigt werden.
Darum müssen die GetTag Aufrufe blockierend sein, weil ich ja erwarte dass ich als Ergebnis der Funktion einen zumindest halbwegs aktuellen Wert der Variablen in der SPS zurückbekomme. Somit dauert der erste GetTag Aufruf so lange bis der Wert der einzelnen Variable die ich lesen wollte wirklich aus der SPS zurückgekommen ist.

Nun gruppiert der WinCC Datenmanager Variablen von gleichem Aktualisierungszyklus in Gruppen, um diese möglichst effizient aus der SPS lesen zu können. Auch wenn ich die Variablen mittels GetTag aus einem Skript auslese, werden diese zur zyklischen Aktualisierung (imho 2s Default) angemeldet.
Bedeutet, dass beim ersten GetTag-Aufruf eine neue Aktualisierungsgruppe nur mit Variable 1 angelegt wird. Bei GetTag der nächsten Variable wird diese der Gruppe hinzugefügt und es werden beide zusammen gelesen usw. usf.
Bis zu einer Anzahl an Variablen die in eine PDU passen ist dieses Verfahren nichteinmal verwerflich, da es keinen sehr großen Unterschied macht, ob ein Telegramm mit der Abfrage 1-Byte Variable oder der kompletten Ausnutzung der PDU-Größe gesendet wird.

Da eine Gruppe irgendwann mal voll ist und eine neue angelegt wird, wird sich das Verhalten nicht wie ich geschrieben habe bis zu 150 Variablen so fortsetzen. Aber das Prinzip bleibt, und man kann WinCC wegen diesem Verhalten eigentlich keinen Vorwurf machen.
Um das zu verhindern gibt es die GetTagMulti Funktionen.

Wenn du eine TCP/IP-Verbindung zur SPS hast, kannst du dir das Ganze ja mal mit Wireshark ansehen. Da sieht man sehr schön wie das funktioniert.


----------



## RONIN (11 Dezember 2013)

Danke für die ausführliche Beschreibung! 



Thomas_v2.1 schrieb:


> ... die bisher noch nie aus der SPS gelesen wurden...
> ... müssen die GetTag Aufrufe blockierend sein...





Thomas_v2.1 schrieb:


> ... werden diese zur zyklischen Aktualisierung (imho 2s Default) angemeldet.


Ja so um dem Daumen herum war mir dass auch so bekannt. Ich hatte auch zwischenzeitlich versucht bei Runtimestart ein Skript aufzurufen das die infrage kommenden Variablen einmal abfragt um sie für die zyklische Aktualisierung anzumelden.
Leider brachte das keine Verbesserung.



Thomas_v2.1 schrieb:


> Wenn du eine TCP/IP-Verbindung zur SPS hast, kannst du dir das Ganze ja mal mit Wireshark ansehen. Da sieht man sehr schön wie das funktioniert.


Werd ich definitiv noch machen, meine Neugier ist geweckt. 

Leider geht das Projetkt nächste Woche in Betrieb also wenig Zeit. Deswegen werd
ich vorerst auch noch mit der Lösung der Variablenanmeldung über den Dynamikdialog eines Objektes (So wie oben beschrieben) leben. Will es jetzt noch nicht unbedingt auf GetTagMulti umbauen.

Es wäre allerdings schön wenn man WinCC schon ohne versteckten Aufwand verklickern könnte: "Hey ich brauch die Tags in Skripten und zwar PRONTO! Also meld diese gefälligst von Haus aus an du Dummes Ding."
Sowas wie ein Häckchen beim Erstellen das Tags, vielleicht. :-x


----------



## sailor (11 Dezember 2013)

Hallo,
Variablen mit Excel-VBA generieren?
Wie geht denn das? 
Gruß
Sailor


----------



## LowLevelMahn (11 Dezember 2013)

@Thomas_v2.1


geht GetTagMultiWait auch mit TIA/HMI Bordmitteln oder brauche ich da ein richtiges WinCC V12?

könnte ich die Funktionalität mit einem der WinCC Trials testen - reicht das die Basic?

http://support.automation.siemens.c...objaction=csview&extranet=standard&viewreg=WW


----------



## RONIN (11 Dezember 2013)

sailor schrieb:


> Variablen mit Excel-VBA generieren?


Nur die Variblen*NAMEN* bzw. die Lange Wurst. Damit ich nicht alles tippen musste. Siehe Anhang

Wirklich Tags generieren natürlich über das WinCC Configuration Tool.
Anhang anzeigen SPZM_VarNames.zip


----------



## Thomas_v2.1 (11 Dezember 2013)

LowLevelMahn schrieb:


> geht GetTagMultiWait auch mit TIA/HMI Bordmitteln oder brauche ich da ein richtiges WinCC V12?
> 
> könnte ich die Funktionalität mit einem der WinCC Trials testen - reicht das die Basic?



Ich kenne diese TIA-WinCC Varianten nicht, darum kann ich nicht sagen obs da sowas gibt.
Aber ich kann mir nicht vorstellen dass das viel mit dem "richtigen" WinCC (V5, V6, V7) gemein hat.


----------



## RONIN (26 Januar 2014)

Hallo! Mittlerweile hab ich ein wenig Zeit gefunden das Thema wieder aufzugreifen.

 Grundsätzlich funktioniert die von Thomas beschriebene Funktion (Beitrag #3) GetTagMultiWait sehr schön.
Jetzt hatte ich mir gedacht ich schreib mir im GlobalScript eine Funktion die mir mittels GetTagMulti ein globales String-Array bereitstellt auf welches
ich dann von meinen Funktionen in den Bildern zugreifen kann. Also sowas:


```
char SPZM_ArtBez[10][32];

void neueFunktion()
{
...
```
zugreifen kann man dann in einem Prozessbild mit z.B.:
	
	



```
extern char SPZM_ArtBez[10][32];
```
Alles bekannt soweit.

 Im Beispiel von Thomas wird für die Funktion GetTagMulti bzw. für die Variablenwerte (sofern ich's richtig verstehe) ein Array von Pointern (char* value[30]; )angelegt.
GetTagMulti bekommt dann mit &VarName die Adresse der Pointer, legt die Ergebnisse mit SysMalloc an und schreibt in den Pointer die jeweile Adresse rein.
Glaub ich halt. An diesem Punkt kommt mein Mangel an Verständnis echt ans Tageslicht.

Mein Problem ist dass ich den globalen Zugriff auf diese Pointer nicht zustande bringe. Kann mir da jemand bitte auf den richtigen Weg helfen?
Wie muss man z.B.: im Mausklick-Event eines Buttons in einem Bild auf diese Daten zugreifen damit man die GetTagMulti-Werte aus dem Global-Script bekommt?


----------



## Thomas_v2.1 (26 Januar 2014)

Da ich nicht vollständig weiß wie die SysMalloc Verwaltung WinCC intern funktioniert, würde ich die Strings die bei GetTagMulti zurückkommen in deine globalen Variablen umkopieren. Dann sollte man bezüglich Speicherverwaltung auf der sicheren Seite sein.
Dazu hinter den GetTagMulti pro Array Eintrag:
strcpy(SPZM_ArtBez[0], &value[0]);
oder mit strncpy wenn du eine Längenüberprüfung mit drin haben möchtest.

Das würde dann auch zu deiner Deklaration der globalen Variable passen.

Andere Variante wäre, die globale Variable als Array aus char Zeigern anzulegen (char *SPZM_ArtBez[10]) und diese direkt der GetTagMulti Funktion zu übergeben. Da weiß ich aber nicht wie sich die Funktion verhält, wenn die Parameter schon mit SysMalloc reservierten Speicher enthalten. Schlimmstenfalls wird jedes Mal mit SysMalloc neuer Speicher reserviert, und der alte nicht freigegeben.


----------



## RONIN (26 Januar 2014)

Hatte ich den ganzen Pointer-Zusammenhang tatsächlich richtig verstanden?


Thomas_v2.1 schrieb:


> Andere Variante wäre, die globale Variable als Array aus char Zeigern anzulegen (char *SPZM_ArtBez[10]) und diese direkt der GetTagMulti Funktion zu übergeben. Da weiß ich aber nicht wie sich die Funktion verhält, wenn die Parameter schon mit SysMalloc reservierten Speicher enthalten. Schlimmstenfalls wird jedes Mal mit SysMalloc neuer Speicher reserviert, und der alte nicht freigegeben.


 Ja leider habe ich mit dieser Version nichts zustande gebracht, wenn ich von einem Bild drauf zugreifen wollte bekam ich mal "Access Violation" mal Kauderwelsch als Daten.


Thomas_v2.1 schrieb:


> Da ich nicht vollständig weiß wie die SysMalloc Verwaltung WinCC intern funktioniert, würde ich die Strings die bei GetTagMulti zurückkommen in deine globalen Variablen umkopieren.


Haha, Lustig! So sieht meine "Behelfslösung" im Moment aus. Kann mich deinem Betrag aber nur anschließen dass dies vermutlich die
sicherer Variante ist und ich auch dabei bleiben werde.

Ich werde jetzt erstmal die ersten Skripte daraufhin umbauen und sehen wie sich die Performance verhält.


----------



## Pipboy (27 Januar 2014)

> Hatte ich den ganzen Pointer-Zusammenhang tatsächlich richtig verstanden?



Ich glaube nicht ganz. Arrays und Pointer sind in C nicht immer austauschbar, auch wenn das oft behauptet wird. Einfachstes Beispiel:


```
Datei 1:
int mango[100];

Datei 2:
extern int *mango;
xyz = mango[i]; // autsch
```

Es gibt einen grundsätzlichen Unterschied zwischen char *SPZM_ArtBez[10] und char SPZM_ArtBez[10][32]

*SPZM_ArtBez[10] 
ist ein Array von 10 Pointern und hat selbst keinerlei Speicher für echte Daten.
Worauf diese Pointer zeigen (Adresse) kann beliebig verändert werden. Vorrausgesetzt der Speicher wurde an anderer Stelle allokiert - sei es malloc oder eine anderes Array.

SPZM_ArtBez[10][32] 
ist wirklich ein *zusammenhängendes* Stück Speicher von 320 Bytes. Man kann es mit [][] oder [] adressieren und den Inhalt verändern, aber niemals die Adresse ändern auf die z.B. SPZM_ArtBez[0] zeigt.

Das hier kann also unmöglich gut gehen:

```
char SPZM_ArtBez[10][32];
GetTagMultiWait("%s","Tag1", &SPZM_ArtBez[0]);
```

Das hier schon (ist prinzipiell richtig):

```
char *SPZM_ArtBez[10];
GetTagMultiWait("%s","Tag1", &SPZM_ArtBez[0]);
```
GetTagMultiWait reserviert den Speicher selbst und kann auch den Pointer auf die neue Adresse ändern (was beim der Array Deklaration nicht geht).
So wie ich die Hilfe verstehe, bleibt der Speicher von Sysmalloc aber nur bis zum Ende der Aktion bestehen. Deshalb greift das Bild später ins Leere.

Man könnte malloc im Global Script versuchen. Die Frage ist dann, ob und wann ein Bild dazu auch free aufruft - und ob das überhaupt zu koordinieren ist.
Das Umkopieren in globale Variablen ist vermutlich einfacher.


----------



## RONIN (27 Januar 2014)

@Pipboy. Vielen Dank das du die die Mühe gemacht hast die Sache noch mal so übersichtlich darzustellen.
So wie du es schreibst hatte ich das schon richtig verstanden. 

Ich hab nur merkwürdigerweise keinen globalen Zugriff auf die Strings hinter den Pointern zustande bekommen.
 (Ich hatte schon char ***SPZM_ArtBez[10]; global deklariert, also als Pointer-Array)
Also hab ich die Daten, wie Thomas schon vorgeschlagen, in ein fest deklariertes globales struct-Array kopiert.

Aber trotzdem danke für die Bestätigung.


----------



## LowLevelMahn (27 Januar 2014)

@Pipboy irgendwie lesen sich deinen Aussagen komisch




> *SPZM_ArtBez[10]
> ist ein Array von 10 Pointern und hat selbst keinerlei Speicher für echte Daten.




Doch es hat Speicher für Daten die der 10 Pointer




> Das hier kann also unmöglich gut gehen:
> char SPZM_ArtBez[10][32];
> GetTagMultiWait("%s","Tag1", &SPZM_ArtBez[0]);




warum soll das nicht gehen? &SPZM_ArtBez[0] ist ein 100% valider Zeiger auf einen Speicherbereich der 32 Zeichen aufnehmen "könnte"
ob der Speicher direkt linear im Array angeordnet ist oder das Array per Zeiger drauf verweist ist doch völlig egal
- aber GetTagMultiWait mag es so eben für Strings so nicht - sonst aber schon




> GetTagMultiWait reserviert den Speicher selbst und kann auch den Pointer auf die neue Adresse ändern



http://support.automation.siemens.c...objaction=csview&extranet=standard&viewreg=WW


----------



## Pipboy (27 Januar 2014)

> Doch es hat Speicher für Daten die der 10 Pointer



Natürlich. Der Punkt ist, dass Adressen allein nutzlos sind, wenn es nicht etwas gibt auf das man zeigen kann.



> warum soll das nicht gehen? &SPZM_ArtBez[0] ist ein 100% valider Zeiger auf einen Speicherbereich der 32 Zeichen aufnehmen "könnte"


Mit der Definition char SPZM_ArtBez[10][32] ist &SPZM_ArtBez[x] ein fester Offset zur Startadresse des Arrays. Malloc wird versuchen die Adresse zu ändern - nicht den Inhalt - und das geht nicht.



> ob der Speicher direkt linear im Array angeordnet ist oder das Array per Zeiger drauf verweist ist doch völlig egal


Hier kannst du sehen, was im Vergleich passiert:
http://codepad.org/YpUBtqLy


----------

