# Array für Histogramm



## McNugget (19 Oktober 2009)

Hallo allerseits.

Ich möchte ein Array mit Realwerten erstellen um diese in einem Histogramm darzustellen.

Leider bin ich bisher kläglich gescheitert, wenn es darum ging, Arrays und "Schiebefunktionen" im Strukturierten Text zu definieren.

Ich möchte ein Array mit 240 Elementen haben. Diese sollen im x-Minuten-Takt (definierbar) geschrieben und weitergeschoben werden.


Wie müsste ich so etwas im strukturierten Text definieren?

Muss ich für das Histogramm in das letzte Element des Arrays schreiben und "hoch-schieben"? Oder anders herum?

Vielen Dank schon mal an:  "wer immer sich erbarmt". 

;-)

Gruss

McNugget


----------



## Larry Laffer (19 Oktober 2009)

Hallo,
das ist zwar SCL-Code ... sollte aber im Wesentlichen für die zu gebrauchen sein :
	
	



```
FUNCTION_BLOCK FB100              
 
VAR_INPUT
   neue_Daten : REAL ; 
   Takt_vor    : BOOL ;        // Schiebe-Speicher vor-takten
END_VAR

VAR
   Daten_Speicher : ARRAY [1..240] OF REAL ;
   FM_Takt : BOOL ;   
END_VAR
VAR_TEMP
   i  : INT ; // Schleifen-Variable
END_VAR
 
BEGIN
 
IF Takt_vor AND NOT FM_Takt THEN 
   FOR i := 239 TO 1 BY -1 DO
       Daten_Speicher [i +1] := Daten_Speicher [i]  ;
   END_FOR ;
   Daten_Speicher [1] := neue_Daten  ;
END_IF ;
FM_Takt := Takt_vor ;
 
END_FUNCTION_BLOCK
```
Gruß
LL


----------



## RobiHerb (19 Oktober 2009)

*Nicht Schieben ...*

In der Regel macht man ein Array mit fester Länge und einen Schreib/Lese "Zeiger". Die Zeiger am Ende angelangt machen einen Wrap (Überlauf auf 0) . Das Rumkopieren kostet zuviel Zeit.


----------



## Larry Laffer (19 Oktober 2009)

kommt immer darauf an, was man mit den Daten so vor hat. Ich sehe da nicht Schlimmes daran und der Zykluszeitbedarf für diese Verschiebe-Aktion, die alle paar hundert (oder tausend) Zyklen mal stattfindet hält sich sogar bei Siemens noch in Grenzen ...

Gruß
LL


----------



## zotos (19 Oktober 2009)

Topic schrieb:
			
		

> *Array für Histogramm*



Das Histogramm ist eine art Kurve die das Array visualisiert. Ob man hier schiebt oder nicht ist keine Frage der Zykluszeit sondern des Geschmacks. Bei den Histogrammen bevorzuge ich das Schieben der Werte. Bei anderen Anwendungen (FIFO und Co) vermeide ich das.


----------



## witkatz (19 Oktober 2009)

Falls du schon Arrays für Histogramme schieben musst, dann dürfte Memcpy performanter sein als Schleife, was vor allem bei nicht sehr rechenstarken Plattformen wichtig ist:

IF Takt_vor AND NOT FM_Takt THEN
    MEMCPY(ADR(Daten_Speicher[2]), ADR(Daten_Speicher[1]), SIZEOF(Daten_Speicher) - SIZEOF(Daten_Speicher[1]));
    Daten_Speicher[1] := neue_Daten  ;
END_IF ;
FM_Takt := Takt_vor ;

Gruß,
witkatz


----------



## McNugget (19 Oktober 2009)

Danke Euch allen.

Super, wenn so etwas eine Diskussion auslöst, da kann man sehr gut mit lesen und die Gedankenansätze lernen.

Also entweder Performance, oder eben Idiotensicherheit für mich.. ;-)

@Larry Laffer: Wie muss ich es schreiben, wenn ich das Histogramm nicht von links nach rechts sondern von rechts nach links beschreiben möchte?

@Witkatz: Was genau macht die Funktion Memcpy und in welcher Bibliothe finde ich die??


Gruss und guten Appetit

McNugget


----------



## witkatz (19 Oktober 2009)

McNugget schrieb:


> @Witkatz: Was genau macht die Funktion Memcpy und in welcher Bibliothe finde ich die??



Hallo McNugget, im http://infosys.beckhoff.com/ findest du die Antwort:
http://infosys.beckhoff.com/content/1031/tcplclibsystem/html/tcplclibsys_memcpy.htm

Gruß und Mahlzeit,
witkatz


----------



## Larry Laffer (19 Oktober 2009)

die Schleife umgekehrt wäre dann so :
	
	



```
IF Takt_vor AND NOT FM_Takt THEN 
   FOR i := 2 TO 240 BY 1 DO
       Daten_Speicher [i -1] := Daten_Speicher [i]  ;
   END_FOR ;
   Daten_Speicher [240] := neue_Daten  ;
END_IF ;
FM_Takt := Takt_vor ;
```
den Memcopy (bzw. dessen Syntax) kenne ich leider nicht - der sollte das aber auch können ... wahrscheinlich ungefähr so :
	
	



```
IF Takt_vor AND NOT FM_Takt THEN
   MEMCPY(ADR(Daten_Speicher[1]), ADR(Daten_Speicher[2]), SIZEOF(Daten_Speicher) - SIZEOF(Daten_Speicher[1]));
   Daten_Speicher[240] := neue_Daten ;
END_IF ;
FM_Takt := Takt_vor ;
```
Gruß
LL


----------



## McNugget (19 Oktober 2009)

@witkatz: Das hatte ich natürlich nicht gesagt: Ich verwende Wago 750-841 mit Codesys 2.3 und der OSCAT.Lib V3.10.

@Larry Laffer: Vielen Dank. Werde gleich mal ein wenig damit spielen.



Wie kann ich diese Schleifen sicherer lernen? Gibt es ein howto für Dummies????


Gruss

McNugget


----------



## zotos (19 Oktober 2009)

Auch die WAGO sollte sowas anbieten: SysMemCpy  *SysMemCpy aus der SysLibMem.lib
*


----------



## McNugget (19 Oktober 2009)

@zotos: Recht hast Du.. hab´s gefunden. Ich hatte gar nicht erst geschaut, da ich annahm, dass es sich um eine ausschliessliche Beckhoff-Funktion handeln würden. Danke.

Wenn ich dieses Array jetzt etwas aufpusten möchte, also 20 Real-Werte zum gleichen Zeitpunkt schreiben möchte. Wie erweitere ich es um diese Dimension? (Ist Dimension richtig?)

Die Ausgabe müsste dann ungefähr so lauten :
Daten_Speicher[1]= 1.11; 2.22; 3.33;...; 22.22; 
Daten_Speicher[1]= 1.11; 2.22; 3.33;...; 22.22; 

(Habe da mal Beispielhaft Wert=Kanal genommen.)

Und wie greife ich zum Beipiel aus dem Trend-Element direkt in diese Wertreihen?

Also zum Beispiel in den dritten Wert (3.33).



Gruss

McNugget


----------



## zotos (19 Oktober 2009)

Für die Datenreihe bietet sich ja geradezu ein zweidimensionales Array an.
Damit kann man dann z.B. 20 Messreihen mit je 100 Messwerten erfassen.
Das Problem ist nun aber das Histogramm das bei mir ums Verrecken keinen Zugriff auf einen Teil des Array macht. Ich bekomme es nicht geregelt dort eine Auswahl einzutragen die nur eine Datenreihe über einen Indes annimmt. Aber mit Pointern kommt das Teil klar und daher habe ich eine Lösung erarbeitet die zwar Q&D ist aber funktioniert.
Ich hoffe Du hast keine Angst vor Pointern ;o)


```
PROGRAM PLC_PRG
VAR
    MyArray : ARRAY[0..19,0..99] OF REAL;
    pMyArray : POINTER TO  ARRAY[0..19,0..99] OF REAL;
    pMyDummy : POINTER TO  ARRAY[0..99] OF REAL;
    i : INT;     (* aktuelle Messreihe *)
    x :INT;  (* aktueller Messpunkt *)
END_VAR

(* Die FOR-Schleife ist nur zum testen damit da Daten reinkommen *)
FOR x := 0 TO 99 DO
 MyArray[i,x] :=  MyArray[i,x] + 0.1;
END_FOR;

pMyArray := ADR(MyArray[i,0]); (* Setze Pointer auf die Messreihe i *)
pMyDummy := pMyArray; (* Kopiere die Pointer adresse *)
```

So und nun zum Histogramm da habe ich dann als Variable einfach:
PLC_PRG.pMyDummy^
verwendet. Das ^ am Ende ist wichtig! 


Vielleicht gibt es ja schönere Lösungen aber die hier verbraucht ausser den paar DWORDs für die Pointer keinen zusätzlichen Speicher.


----------



## McNugget (19 Oktober 2009)

Ähhh... Alle schlottern vor Pointern, da sie sehr abstrakt sind, das tue ich dann auch mal...
(Mal sehen, wie lange ich brauche, es durch learning by doing zu kapieren.)

Was ich nun nicht ganz verstehe: wo gebe ich die 20 Real-Werte rein? Ich muss doch irgendwie Daten reinreichen.
So wie das jetzt ist, ist der Baustein doch abgeschlossen, oder??

LERNEN WILL.. ;-)



Zwischenzeitlich habe ich mal testweise eine Struktur aus meinen Realwertem und einem timestamp gemacht. 
Hätte den Vorteil, dass man das dann so mit timestamp wegloggen könnte um es später noch mal mit excel etc. auszuwerten.
Diese Struktur konnte ich wieder gut in mein Array schreiben. Aber ich hab mir auch die Zähne dran ausgebissen, die Werte aus dem Innern des Arrays in das Histogramm zu bekommen.

Sehr sympathisch, dass Du das gleiche Problem hattest, das bedeutet, dass ich zum Teil schon auf dem richtigen Weg war.. 



 Bei der Lösung mit der Struktur, hatte mein Progrämmchen nur 12 ms Laufzeit, finde ich super.



Gruss

McNugget


----------



## zotos (19 Oktober 2009)

Eine Struktur mit Zusatzinformationen hätte halt das Problem das man mit dem Pointer Trick nicht arbeiten kann da das Histogramm das Array mit den Werten ja in einem Block erwartet.

Dann bliebe Dir wohl nur die Alternative eine Schleife zu basteln die Dir aus Deinem Array der Struktur die relevanten Werte in ein extra Anzeige Array umkopiert.


----------



## McNugget (19 Oktober 2009)

Naja... Diese Schleife hätte ich ja schon aus den ersten Beiträgen in diesen Thread. Ich müsste es nur pro Kanal, den ich anzeigen möchte, instanziieren.

Die Histogramm-Anzeige wäre mir aber gar nicht so wichtig wie eine Möglichkeit, die Daten aus dem Controller rauszubekommen und später auf dem PC zu visualisieren.


Noch mal der Vollständigkeit halber: wie bekomme ich das zweidimensionale Array gefüllt?



Gruss

McNugget


----------



## McNugget (19 Oktober 2009)

... und wüsste jemand ein ausführliches How-To, wie man Schleifen, Schieberegister und Arrays sauber in ST schreibt?

Gruss und schönen Abend noch

McNugget


----------



## Larry Laffer (20 Oktober 2009)

... ein how-to-do für ST-Programmierung im Allgemeinen und Schleifen-Erstellung im Besonderen kenne ich nicht - man kann sich aber auch Schritt-für-Schritt da herein-arbeiten - ggf. in dem man seine bisherige Arbeit ins Forum stellt und bewerten läßt.

Gruß
LL


----------



## McNugget (20 Oktober 2009)

Guten Morgen.

Hmm, Na dann werde ich mich mal weiter reinfuchsen.
Pascal Büchlein noch mal anlesen..


Bezugnehmend auf den Beitrag von zotos von gestern 14:27 Uhr:
...und wie bekomme ich nun dieses zweidimensionale Array gefüllt?

Gruss

McNugget


----------



## zotos (20 Oktober 2009)

In Dem Du die Werte in die einzelnen Zellen hineinschreibst z.B. mit einer Schleife.

Ein zweidimensionales Array ist ja quasi eine Tabelle mit Zeilen und Spalten. Daher hat es eine x und eine y Koordinate.

Datenspeicher[x,y] := MyRealValue;

Im Prinzip musst DU doch nur den Code von Larry um die Information einer Spalte erweitern.


----------



## Werner29 (20 Oktober 2009)

zotos schrieb:


> Für die Datenreihe bietet sich ja geradezu ein zweidimensionales Array an.
> Damit kann man dann z.B. 20 Messreihen mit je 100 Messwerten erfassen.
> Das Problem ist nun aber das Histogramm das bei mir ums Verrecken keinen Zugriff auf einen Teil des Array macht.



An der Vorgehensweise ist überhaupt nichts auszusetzen und gerade für diese Sachen sind Pointer gemacht. Aber ich will trotzdem noch auf eine zweite Möglichkeit der Array-Deklaration hinweisen, mit der man hier vielleicht das erreicht was Zotos gerne möchte.


```
PROGRAM PLC_PRG
VAR
    MyArray : ARRAY[0..19] OF ARRAY [0..99] OF REAL;
    MyArray1 : ARRAY[0..19] OF REAL;
    i : INT;     (* aktuelle Messreihe *)
    x :INT;  (* aktueller Messpunkt *)
END_VAR

(* Die FOR-Schleife ist nur zum testen damit da Daten reinkommen *)
FOR x := 0 TO 99 DO
 MyArray[i][x] :=  MyArray[i][x] + 0.1; (*zweimaliger Indexzugriff*)
END_FOR;

MyArray1 := MyArray[i]; (* Kopiere Messreihe i: Deutlich langsamer als Pointerzuweisung !!*)
```
Ich habe es nicht ausprobiert, aber das sollte auch funktionieren.


----------



## Larry Laffer (20 Oktober 2009)

```
MyArray : ARRAY[0..19] OF ARRAY [0..99] OF REAL;
```
@Werner:
Danke für den Hinweis ... den Trick kannte ich noch nicht und der funktioniert auch in SCL ... und das ermöglicht mir einen Programm-Code in einer Aufgabenstellung  eleganter zu gestalten bei dem ich mich aktuell anders beholfen habe ...

Gruß
LL


----------



## McNugget (20 Oktober 2009)

Ok.. Jetzt wird´s mir deutlich zu hoch:

Wenn ich es nur einkopiere, erhalte ich in Codesys die Fehlermeldung:
Fehler 4010:History_03 (24): Unverträgliche Typen: Kann 'Array [0..] OF REAL' nicht in 'ARRAY [9..19] OF REAL' konvertieren.

Sehe ich das richtig, dass es sich hierbei um ein "verschachteltes" Array handelt?


@Zotos: Ich hatte mich mehr auf Deinen Code mit dem zweidimensionalen Array bezogen. 

Ich hoffe, es kommt nicht so rüber, dass ich erwarte, dass man ALLES für mich macht, aber aktuell kann ich nur kleine Trippelschritte machen und noch muss man mir leider alles vorkauen. 
Ich weiss selber, wie anstrengend es ist, wenn man immer wieder das gleiche runterbetet, und der dem man es erklärt.,immer wieder mit dem selben Problem ankommt, ohne zu lernen. 
Ich möchte ja lernen, und es stört mich selber, Euch mit so´nem Kleinsch... zu behelligen. Ich wüsste nur nicht, wo und wie ich die Fragen besser anbringen kann.
Da bitte ich ehrlich um Verständnis.

Es ist noch kein Autodidakt vom Himmel gefallen.. ;-)
Daher fragte ich ja nach Infos für Dummies, in denen ich erst mal weiter lernen/lesen kann, ohne Eure Ressourcen zu verschwenden.

Verstehe weder, was Du in Deinem Array-Code machst, geschweige denn, das Werner da schreibt.
Und daher wüsste ich eben gerne, wie ich diese Code-Teile bei mir mit Daten belegen kann, um zu sehen, wie es läuft.

Gruss

McNugget


----------



## Werner29 (20 Oktober 2009)

Larry Laffer schrieb:


> ```
> MyArray : ARRAY[0..19] OF ARRAY [0..99] OF REAL;
> ```
> @Werner:
> ...


Ach du scheisse, habe ich jetzt unbeabsichtigt Hilfestellung für Siemens Programmierer gegeben?

@McNugget: Es funktioniert, wenn du die Dimensionen vertauschst
   MyArray : ARRAY [0..99] OF ARRAY [0..19] OF REAL;
   MyArray1 : ARRAY[0..19] OF REAL;
Es ist zugegebenermassen verwirrend.


----------



## Larry Laffer (20 Oktober 2009)

Werner29 schrieb:


> Ach du scheisse, habe ich jetzt unbeabsichtigt Hilfestellung für Siemens Programmierer gegeben?


@Werner: ... ich habe dich auch lieb ...


----------



## Larry Laffer (20 Oktober 2009)

@McNugget:
Das was du in Werner's Code nicht verstehst ist, dass ST (und auch SCL) gleich ganze Strukturen oder Datenblöcke mit gleichem Aufbau quasi in einem Rutsch von einem Element an das andere übergeben kann. Letztlich wird zwar Element für Element übergeben - aber das läuft intern.

```
PROGRAM PLC_PRG
VAR
    MyArray : ARRAY[0..19] OF ARRAY [0..99] OF REAL;
    MyArray1 : ARRAY[0..99] OF REAL;
    i : INT;     (* aktuelle Messreihe *)
    x :INT;  (* aktueller Messpunkt *)
END_VAR

MyArray[i] := MyArray1;
```
Im korrigierten Beispiel von Werner :
Das MyArray1 ist genau gleich aufgebaut wie der 2.Teil der Deklaration von MyArray. Deshalb kannst du das Unter-Element so übergeben.

Gruß
LL


----------



## McNugget (21 Oktober 2009)

Guten Morgen Larry,

das hatte ich in Groben Zügen verstanden. 

Was nach wie vor meine Frage ist:

Wie belege ich das Array mit REAL-Werten?

Wie gebe ich die ein, dass sie abgelegt werden?

Gruss

McNugget


----------



## Larry Laffer (21 Oktober 2009)

Hallo Mac,
kann sein, das ich irgendwo etwas überlesen haben ... deswegen (vielleicht noch einmal) die Frage : Woher kommen die Werte, mit denen du das Array füllen möchtest. Das mit dem Trigger und dem "Schiebe-Register" hatte ich ja in meinem ersten Posting schon mal gebracht ...

Gruß
LL


----------



## McNugget (21 Oktober 2009)

Hallo LSL. ;-)

Im Prinzip ist in der Diskussion hier die Variable "neue_Daten" aus Deinem ersten Post verschwunden...

Die Werte sind (aktuell noch) einzelne REAL-Werte, die ich von aussen an den Baustein lege.

Sobald der Trigger kommt, sollen der (später die) Wert(e) in das Array geschrieben werden.

Sobald ich mehrere Werte reingebe, möchte ich ein Struct reingeben:
Timestamp; REAL_01;Real_02;..;Real_20;



Gruss

McNugget


----------



## Larry Laffer (21 Oktober 2009)

... ich würde dir schon gerne weiterhelfen nur verstehe ich gerade nicht so genau wo es hängt ...

Aber prinzipiell :
Wenn du mehrere Einzelwerte nacheinander in ein ARRAY schreiben willst, so geht das wie in meinem ersten Beispiel. Hier gibt der Trigger den Befehl "Weret übernehmen". Gleichzeitig mußt du den Speicher weiterschieben ... oder aber du merkst dir (für direktes Befüllen) die Position (Index) an die du den letzten Werte geschrieben hast und erhöhst denselben dann für den nächsten Wert usw. ...

Gruß
LL


----------

