# Productdaten speichern



## Andy082 (28 Oktober 2009)

Abend.

Bin dabei von der S7-200 & TP und meinen Nachbesserungen an einigen 300er-Programmen dazu überzugehen, mir mein eigenes 300er-Universalprogramm zu erstellen.

Eine ausgeklügelte Schrittkettenverwaltung und einen FB für schier alle Automationsabläufe habe ich mir bereits erstellt.

Was für mich nun interessant wäre, ist ein universelles Programm zum Speichern von Einstellungen, wie zB Farbe des Produkts, entsprechende Codierung, etc.

Natürlich könnte man das Ganze nun in einen/zwei/drei.... DB(s) hinterlegen und sagen, wenn ich zB Produkt 1 lade, dann kopiere ich DB1 in einen "Soll-DB", lade ich Produkt2, dann kopiere ich DB2...   usw.
Wäre wohl ein gangbarer Weg, wie ich meine, aber keine schöne Lösung, vor allem wenn es mehrere Produkte werden, benötige ich auch mehrere DBs.

Hab mir nun das Programm eines Bekannten unter die Lupe genommen und entdeckt, dass dieser einen GlobalenTypDB verwendet, in welchem er einen UDT und in diesem dann einzelne UDTs aufgerufen werden.

Nach dem Laden eines Produktes schreibt er die aktuellen Einstellungen wieder in einen neuen DB, in welchem die selben UDTs wie oben aufgerufen werden.

Zum Handling wird hier ein wohl selbst zusammengebastelter SCL-Baustein verwendet, welchen ich, da nur wenig Ahnung von SCL, gegen einen in AWL ersetzen will.

Was mir hier nun überhaupt nicht geläufig ist, ist das Handling der einzelnen UDT's. 
Ich will nicht, dass mir hier nun jemand mein Programm Stück-für-Stück vorkaut, sondern ledigtlich erklärt, was hier scheinbar mit den UDTs passiert. Denn egal, welchen Wert ich in einem der UDT's ergänze, dieser wird automatisch in alle Produktstände (bis zu 30!) übertragen und kann beliebig eingestellt/verändert werden, was für mich gerade zu ideal wäre.

Eventuell nimmt sich ja jemand etwas Zeit für mein Problem.

Danke!

Andy


----------



## Paule (29 Oktober 2009)

Andy082 schrieb:


> Was mir hier nun überhaupt nicht geläufig ist, ist das Handling der einzelnen UDT's.
> Ich will nicht, dass mir hier nun jemand mein Programm Stück-für-Stück vorkaut, sondern ledigtlich erklärt, was hier scheinbar mit den UDTs passiert. Denn egal, welchen Wert ich in einem der UDT's ergänze, dieser wird automatisch in alle Produktstände (bis zu 30!) übertragen und kann beliebig eingestellt/verändert werden, was für mich gerade zu ideal wäre.


Hallo Andy,

der UDT ist eigentlich nur ein Muster (Vorlage). 
Wenn Du den UDT in den DB einfügst, ändert der DB seine Struktur genau nach diesem Muster.
Und wenn Du die Vorlage änderst, ändert sich auch das Ergebnis, sprich der DB. Das ist der Vorteil, ich brauche die Vorlage nur einmal schreiben und überall wo sie eingefügt wird ändert sich das Ergebnis.
Ich hoffe das war einigermaßen verständlich.


----------



## Larry Laffer (29 Oktober 2009)

Hallo,
eigentlich hat Paule schon das Wesentliche gesagt ...
Vielleicht noch Eines :

Ein UDT ist eine Anwender-definierte Struktur. Dies eignet sich besonders dann, wenn diese Struktur öfter an unterschiedlichen Punkten im Programm Anwendung findet. Du kannst die in einem DB als Variablen-Typ einfügen oder aber auch dazu benutzen um von einem Baustein an den Anderen Daten zu übergeben, die den gleichen Aufbau haben und die du eben nicht Byte für Byte (oder Wort für Wort) einzeln übergeben möchtest.
Das macht natürlich besonders unter SCL Sinn ... deshalb gibt es da wohl auch diesen "Hantierungs-Baustein" ...

Zu beachten ist vielleicht noch, das UDT's, obwohl flexibel anzulegen, aufgrund des Daten-Handlings der SPS'en letztlich nicht wirklich flexibel sind. Veränderst du deine Struktur, so verändern sich in deinem Programm möglicherweise ein ganzer Haufen an Speicher-Adressen. Das kann die Step7-Software bei symbolischer Adressierung zwar kompensieren - das gilt dann aber nur für das Nachhalten der "echten" Speicher-Adressen. Daten-Inhalte, die in den Speicherstellen ggf. schon drin sind und erhalten werden sollen sind dann ggf. falsch zugeordnet.

Das Thema ist auf jeden Fall etwas, mit dem man "ein wenig" herum-experimentieren sollte ...

Gruß
LL


----------



## Andy082 (29 Oktober 2009)

Hallo....

....nach einem wohl leicht aufreibenden Tag. *in die Stammtisch-Rubrik rüber schau*  

Also was ich euren beiden Aussagen entnehmen kann ist, dass ein UDB quasi nur die Struktur eines oder mehrerer DBs bildet.
So weit so gut....   ist für mich alles verständlich.
Auch, dass sich einzelne Daten verschieben, sollte ich den Inhalt ändern ist klar. Worum es mir in Punkto "universell" geht ist, dass ich mir eben nur einmal die Arbeit antun will, sowas zu schreiben und dannach nur mehr meinen DB bzw. meine einzelnen UDBs meinen Wünschen anpasse.

Was nun mein "Projekt" betrifft, habe ich das Ganze mal wie folgt durchgekaut und versucht auf den Punkt zu bringen......

Grundlegend Definiere ich meinen DB mit einem array[0..64] und hänge dem Ganzen meinen GlobalTypUDT dran.
Somit erhalte ich 65x die Struktur meines UDT.

An meinem Panel gibt es zwei Tasten, welche einen Zähler pro Betätigung um 1 nach oben oder nach unten zählen.
Wird meine Indexzahl verändert, lade ich den zur Nummer meiner Indexzahl passenden UDT in den UDT[0].
Verändere ich etwas ohne zu speichern, bleibt der Inhalt unberührt, betätige ich jedoch am Panel mein Save, lege ich rückwirkend meine Einstellungen des UDT[0] in jenen der gewählten Indexzahl zurück.

Das Ganze funktioniert soweit so gut, aber wenn man sich den Baustein ansieht, ist zu erkennen, dass das Lesen und Schreiben der Daten mit "universell" noch nichts zu tun hat.

Kennt jemand nen' Möglichkeit das in AWL so umzusetzen, dass die Funktion eher universeller abläuft?


```
L     4
      L     #IN_OUT.TypIndex
      ==I   
      U     #temp.loadTyp
      SPBN  d004
      CALL  "BLKMOV"
         SRCBLK :="Typverwaltung".Typ[4]
         RET_VAL:=#tempInt
         DSTBLK :="Typverwaltung".Typ[0]
      R     #temp.loadTyp
d004: SET
```
 
....für jeden Produkttyp zu schreiben will ich mir sparen.
Sieht so, wenn auch funktionell ok, nicht sauber aus.


mfg,
Andy



P.S.:
Im Anhang meine Bausteine als Quelle exportiert.


----------



## Paule (29 Oktober 2009)

Andy082 schrieb:


> Kennt jemand nen' Möglichkeit das in AWL so umzusetzen, dass die Funktion eher universeller abläuft?


Ja Andy,

das geht am besten wenn Du Dir Quelle und Ziel für den SCF20 als Pointer zusammenstellst.

Brauchst Du dabei Hilfe?


----------



## Larry Laffer (29 Oktober 2009)

Hallo Andy,
ich hätte dir für AWL jetzt ziemlich genau die Blockmove-Geschichte vorgeschlagen wie in deinem Code-Schnipsel. Wenn du das universeller haben willst, so bleibt dir (bei AWL) nichts anderes übrig als aus der Datenquelle einen ANY-Pointer zu bauen und den an den SFC20 zu übergeben.
Ich will dir hier jetzt nicht SCL verkaufen, aber dort könntest du im Script wirklich genau das programmieren :
	
	



```
Typverwaltung.Typ(0) := Typverwaltung.Typ(meinIndex) ;
```
Gruß
LL


----------



## Andy082 (29 Oktober 2009)

Ja, Hilfe nehme ich immer gerne in Anspruch.....
Vorallem wenn es sich um Pointer handelt.

Hier bin ich mir nie 100%ig sicher, sollte diese Lücke mal auffüllen.

Was die Quellen des SFC20 betrifft, so verwende ich hier bereits Pointer.

- siehe Bausteine oder *jpg.


Wie sollte ich diesen zusammensetzen, damit er, je nach Indexzahl, auf ein anderes array verweißt?


mfg,
Andreas


----------



## Andy082 (29 Oktober 2009)

Hm, ich denke anhand dieses Posts kann ich mir meine Frage nun selbst beantworten......

http://www.sps-forum.de/showthread.php?t=31150&highlight=Pointer

Zumindest hoffe ich das nun mal.... 



Edit:
.....oder auch nicht.

http://sps-forum.net/showthread.php?t=12923
Auch die Erklärungen von Volker machen für mich die Thematik Pointer nicht einfach zu verstehen.


----------



## Larry Laffer (30 Oktober 2009)

... wo hängt es denn ?


----------



## Andy082 (30 Oktober 2009)

Das ich grundlegend keine Ahnung habe, wie ich hier meinen Pointer gestalten muss.....

.......kleine Hilfe, Andeutung, etc?


mfg


----------



## Larry Laffer (30 Oktober 2009)

Hallo Andy,
du mußt dir einen Pointer bilden auf die Adresse, auf der dein Datensatz mit dem entsprechenden Index liegt und diesen dann an den SFC20 übergeben. Das funktioniert allerdings nicht automatisiert - du mußt also alles "zu Fuß" machen. Hierbei sollte dir die Anleitung von Volker schon hilfreich sein - zumal dort eigentlich auch Alles ziemlich detailiert beschrieben ist. Deshalb meine Frage von vorhin noch einmal : woran hängt es denn konkret ?

Gruß
LL


----------



## Paule (30 Oktober 2009)

Hallo Andy,

da Deine Typverwaltung ja ein UDT ist und natürlich heute so und morgen ganz anders ausschauen kann und außerdem irgendein DB sein kann, sollte das ganze flexibel aufgebaut sein.

Dazu musst Du jetzt Deiner FCx den ersten Datensatz übergeben (Typverwaltung.typ[0]) damit Du weißt um welchen DB es sich handelt und wie lang der UDT ist.
Den list Du als Anyzeiger ein und zerlegst in um an die Daten zu kommen.
Außerdem übergibst Du die Int welchen Datensatz kopiert werden soll.

```
[COLOR=black][FONT=Verdana]FUNCTION FC1 : VOID[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]TITLE = BLOCK KOPIEREN[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]VERSION : 1.0[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana]VAR_INPUT[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]DATEN    : ANY ; // Typverwaltung Satz 0[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]PROG_NR : INT ; //  Welcher Datensatz[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]END_VAR[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana]VAR_TEMP[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]QUELLE : ANY ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]ZIEL     : ANY ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]FEHLER : INT  ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]DB_NR  : INT  ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]ANZAHL: INT  ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]OFFSET: INT  ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]QUELL_ANF: DWORD ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]ZIEL_ANF  : DWORD ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]END_VAR[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana]BEGIN[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]NETWORK[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]TITEL =Anyzeiger zerlegen[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana] L       P##DATEN;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] LAR1 ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] L     W [AR1, P#4.0]; // DB-Nummer[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] T     # DB_NR ;[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana] L     W [AR1, P#2.0];[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] T     #ANZAHL  ;       // Anzahl der Byte des UDT (flexibel ;))[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] L     #PROG_NR ;       // Welcher Datensatz soll kopiert werden[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] *I[/FONT][/COLOR]
[COLOR=black][FONT=Verdana]  T    #OFFSET   ;      // Offset für Anfangsadresse[/FONT][/COLOR]
 
[COLOR=black][FONT=Verdana]// Quell und Ziel Adresse ermitteln[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] L     D [AR1, P#6.0];[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] L     DW#16#7FFFF;  // Bereichskennung ausblenden[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] UD ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] SRD 3   [/FONT][/COLOR]
[COLOR=black][FONT=Verdana] T    #ZIEL_ANF;      // Hier landet der Kopierte Bereich[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] L    #OFFSET;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] +D ;[/FONT][/COLOR]
[COLOR=black][FONT=Verdana] T   #QUELL_ANF;    // Start Adresse zum kopieren[/FONT][/COLOR]
 
NETWORK
TITEL = Quellpointer zusammensetzen
 
   LAR1   P##QUELLE;    // Anfangsadresse ins AR1
   L        B#16#10;    // AWL Kennung für ANY
   T        LB [AR1, P#0.0];
 
   L        B#16#2 ;   // Bereichstype
   T        LB [AR1, P#1.0];
 
   L       #Anzahl ;         // Wurde im 1. NW ermittelt
   T       LW [AR1, P#2.0];  // Länge eintragen
 
   L       #DB_NR ;           // Wurde auch ermittelt
   T       LW [AR1, P#4.0];
 
   L       P#DBX 0.0;       // Pointer Format
   L       QUELL_ANF ;      // Wurde im 1.NW errechnet
   SLD 3;
   +D ;
   T      LD [AR1, P#6.0];  // Quelladresse in Any-Pointer
 
NETWORK
TITEL = Zielpointer zusammensetzen
 
// Genau das gleiche wie Netzwerk 2 nur statt [B]AR1 das AR2[/B] nehmen 
// und statt in 4. Letzter Zeile [B]QUELL_ANF das ZIEL_ANF[/B] nehmen
 
NETWORK
CALL "BLKMOV" (
   SRCBLK   := #QUELLE,
   RET_VAL := #FEHLER,
   DSTBLK   := #ZIEL);
```


----------



## Andy082 (30 Oktober 2009)

Danke!

Ich hoffe du hattest das Ganze bereits in der Schublade und musstest nicht extra für meinen Post hier zum Tippen anfangen.

Hab die verwendete SPS+TP in der Arbeit liegen, werd aber einfach meine Gebäudesteuerung plattmachen und eines meiner Panelchen zum Spielen dran hängen. (ich hasse PLCSIM)

Geb bescheid wie's aussieht...

Andy


----------



## Andy082 (31 Oktober 2009)

@ Larry
Ich hab bisher nicht mit Pointern gearbeitet, außer jenem, mit welchem ich mein Datum und meine Uhrzeit in einzelne Bytes zerlege.

@Paule
So, hab's denn nun doch gleich per Simulator ausprobiert....
(Frau schläft bereits, und "Batman die Rückkehr" auf ATV fesselt mich nicht gerade)

Hab nun alles entsprechend deinem Codeauszug in einen eigenen FC geschrieben und rufe diesen nun bei jedem Wechsel meiner Indexzahl auf.
Nur werden meine hinterlegten Einstellungen nicht so geladen wie Sie es denn tun sollten.

Da in deinem Bsp von einem Ziel- und einem Quell-Pointer gesprochen wird, bin ich mir nicht sicher, ob sich unsere beiden Vorstellungen kreuzen.

Ich will bei jedem Wechsel meiner Indexzahl den entsprechenden DB in meinen GrundDB laden.

Indexzahl: 1
"Typverwaltung".Typ[1] in "Typverwaltung".Typ[0]

Indexzahl: 2
"Typverwaltung".Typ[2] in "Typverwaltung".Typ[0]

usw.

Somit sollte meine Quelle am SFC der erstellte "Quellpointer" sein,
mein Ziel immer "Typverwaltung".Typ[0] - P#DB110.DBX100.0

Hab dies bereits verändert, was leider keine Veränderung brachte.

Mal weitersuchen....


----------



## Paule (31 Oktober 2009)

Andy082 schrieb:


> Da in deinem Bsp von einem Ziel- und einem Quell-Pointer gesprochen wird, bin ich mir nicht sicher, ob sich unsere beiden Vorstellungen kreuzen.
> 
> Ich will bei jedem Wechsel meiner Indexzahl den entsprechenden DB in meinen GrundDB laden.
> 
> ...


Hallo Andy,
eigentlich sollten sich unsere Vorstellungen nicht kreuzen sondern in einer Linie laufen. 



Andy082 schrieb:


> Wird meine Indexzahl verändert, lade ich den zur Nummer meiner Indexzahl passenden UDT in den UDT[0].


Ich ging bei Deinem Beitrag 3 davon aus, dass es sich um einen DB handelt. Sprich:

```
P# DB110.DBX  0.0 Byte 20 ==> Typverwaltung Soll
P# DB110.DBX 20.0 Byte 20 ==> Typverwaltung.Rezept.[1]
```
Darauf basiert mein Beispiel: 
Es wird die Typverwaltung Soll in die Bestandteile zerlegt daraus lässt sich der DB, die Zieladresse und die UDT Länge und somit der Offset für die Rezeptnummern ermitteln.

Wenn Dein Ziel in einem anderen DB liegt, musst Du auch diesen noch übergeben.
Brauchst Du dabei Unterstützung?


----------



## Andy082 (31 Oktober 2009)

Ich sage vorerst nein und nochmals ein großes Danke.

Dass du aus der "Typverwaltung" Soll deine für den Pointer benötigten Daten herausziehst ist mir nun klar, denn woher solltest du denn sonst alle Attripute für den Pointer nehmen.

Anhand deiner Angaben, sollte es mir nun möglich sein, meinen FC selbst so hinzubasteln, dass dieser entsprechend funktioniert.

Andernfalls muss ich eben nochmals posten, bzw. gibts natürlich von meinerseite auch eine Rückmeldung mit Codeauszug, damit auch andere was von der Sache haben.   


Andy


----------

