# DWORD_TO_REAL( REAL_TO_DWORD( REAL ) ) unterschiedlich?!



## Jörn (10 September 2020)

Moin,

ich bekomme via Schnittstelle von einem Roboter REAL-Werte und muss auch REAL-Werte an den Roboter schicken. Die Schnittstelle ist als ARRAY OF DWORD (1) deklariert. Wenn ich nun ein REAL in ein DWORD konvertiere, dann schneidet er die Nachkommazahlen ab. Das wäre u.U. zu vertreten. Aber bei der Rückumwandlung wirds richtig ungemütlich:




Ich probiere schon den ganzen Tag herum, komme aber auf keinen grünen Zweig. Vielleicht hat jemand eine Idee? Oder muss ich mir eine Funktion schreiben, die mir das DWORD in ein IEEE REAL umrechnet (2)?

Gruß
Jörn



(1) https://www.sps-forum.de/codesys-un...ka-krc4-und-ipc-via-bridge-el6695-1001-a.html

(2) https://de.wikipedia.org/wiki/Gleitkommazahl #Berechnung_einer_IEEE_single_precision_Gleitkommazahl_(32-Bit-Gleitkommazahl)


----------



## StructuredTrash (10 September 2020)

DWORD ist vorzeichenlos. Du musst DINT nehmen.
Aber warum ist die Schnittstelle als Array of DWORD deklariert, wenn doch REALs übertragen werden sollen?


----------



## ms_gp (10 September 2020)

Was du brauchst ist ein umkopieren des dwords auf Real ohne Umwandlung. Das geht nur mit einem Pointer.

Oder vielleicht auch eine Union mit einem DWORD und einem Real drin, das ist vielleicht noch eleganter.


----------



## oliver.tonn (10 September 2020)

StructuredTrash schrieb:


> DWORD ist vorzeichenlos. Du musst DINT nehmen.
> Aber warum ist die Schnittstelle als Array of DWORD deklariert, wenn doch REALs übertragen werden sollen?


Achtung, Klugscheißerei!!! Die Vorzeichenfrage kann sich genaugenommen bei Variablen der Typen BYTE, WORD, DWORD, usw. nicht stellen, da dies genaugenommen keine Variablentypen für Zahlen sind und sie demzufolge auch keine Zahlen enthalten. Es sind vielmehr Containertypen zur Aufnahme einer bestimmten Anzahl von Bits, im Falle des DWORDS 32. Ich vermute mal der Roboter hat das Bitmuster des Real-Wertes einfach übertragen und bei Dir kommt es jetzt in einem DWORD an. Hier hilft Dir kein Konvertierungsbefehl weiter, außer Du steigst auf Siemens um, denn da gibt es Befehle mit denen die Bitmuster 1:1 in ein REAL übertragen werden können. Du müsstest entweder eine UNION mit einem DWORD und einem REAL anlegen und das DWORD dann mit der EL6695 verknüpfen, oder mit MEMCPY das Ganze von einer DWORD-Variable in eine REAL-Variable kopieren. Das klappt aber nur, wenn das wieder mit den Indianern passt, wie ich schon in dem von Dir verlinkten Thread schrieb. Ansonsten müsstest Du ein Array of BYTE erstellen und das dann mit dem Eingang verknüpfen, dann die einzelnen Bytes sortieren und das Ergebnis mit MEMCPY in eine REAL-Variable kopieren.
Umgekehrt halt genauso Daten mit MEMCPY von Real in DWORD kopieren.

Argh, zulange getippt, ms_gp war schneller.


----------



## oliver.tonn (10 September 2020)

StructuredTrash schrieb:


> Aber warum ist die Schnittstelle als Array of DWORD deklariert, wenn doch REALs übertragen werden sollen?


Weil Kuka die Prozessdaten die via EtherCAT übertragen werden so definiert hat, da kann der TE nichts für, natürlich hätte Kuka das auch anders machen können und ein Real in der Beschreibungsdatei definieren können, wenn das überhaupt geht, aber wenn sich die Indianer dann mal wieder nicht grün sind kommt da ein heilloses Chaos bei raus und da ist es so noch am Besten.


----------



## StructuredTrash (10 September 2020)

@oliver.tonn:
Ich weiss, konnte aber nicht der Versuchung widerstehen, mich provokativ etwas dumm zu stellen. Warum führt der TE seinen ursprünglichen Thread nicht fort, es geht doch wohl immer noch um das selbe Problem? Und was ist mit unseren beiden Lösungsvorschlägen, haben die nicht funktioniert? Dann wäre der nächste Schritt doch zu prüfen, warum nicht. Die möglichen Stolpersteine hast Du doch genannt.


----------



## oliver.tonn (10 September 2020)

@StructuredTrash: Stimmt wohl, hab mich auch über den neuen Thread gewundert.


----------



## Jörn (10 September 2020)

@ StructuredTrash:
Ich hab deswegen einen neuen Thread aufgemacht, weil dieses Problem nur bedingt mit dem ursprünglichen Problem zu tun hat. In dem genannten Thread ging es _grundsätzlich_ darum, 128 Byte gemischte Datentypen auf ein ARRAY OF DWORD zu mappen, hier ist das _speziellere_ Problem ein REAL korrekt aus einem DWORD zu bekommen. Wer nach DWORD_TO_REAL sucht, wird kaum in einem Thread namens _Variablenverknüpfung zwischen KUKA und IPC_ schauen. Wobei ich sagen muss, daß die Suchfunktion hier einiges besser als in so manch anderem Forum ist, in dem ich bisher unterwegs war. 

Ich danke auf jeden Fall für die Tipps. Ich schau es mir morgen mal an. 

Gruß
Jörn


----------



## redria (11 September 2020)

In TwinCAT3 ist es prinzipiell auch möglich Variablen ungleichen Typs zu verknüpfen.
Hierzu muss bei der Verknüpfung der Variablen zwischen den Prozessabbildern im Auswahldialog das Häkchen bei "passender Typ" entfernt werden. Dann kannst Du Deine DWORD Variable aus EtherCAT mit einer REAL Variable aus der SPS verknüpfen und sparst die Verwendung von Memcpy.


----------



## Jörn (11 September 2020)

Moin,

nach ein bissl Rumprobieren hab ich mich jetzt für die UNION entschieden. Sieht nicht schön aus, geht aber:


```
TYPE U_REALDWORD:
  UNION
    uReal:   REAL;
    uDword:  DWORD;
  END_UNION
END_TYPE
```










redria schrieb:


> In TwinCAT3 ist es prinzipiell auch möglich Variablen ungleichen Typs zu verknüpfen. Hierzu muss bei der Verknüpfung der Variablen zwischen den Prozessabbildern im Auswahldialog das Häkchen bei "passender Typ" entfernt werden. Dann kannst Du Deine DWORD Variable aus EtherCAT mit einer REAL Variable aus der SPS verknüpfen und sparst die Verwendung von Memcpy.


Auch eine nette Sache, werde ich mir fürs nächste Mal merken. An dieser Stelle ist sie leider nicht anwendbar, weil die Jungs, die für die Schnittstelle zuständig sind, gepennt haben. Das ist aber eine lange Geschichte. 

Gruß
Jörn


----------



## StructuredTrash (11 September 2020)

Ok, dann entschuldige bitte meine Kritik.
Aber warum musst Du, nachdem Du die Daten irgendwie über die DWORD-Schnittstelle der EtherCat-Bridge bekommen hast, nochmal so eine Konvertierung machen?


----------



## Jörn (14 September 2020)

Weil die beiden Leute, die sich _eigentlich _um die Schnittstelle hätten kümmern sollen, dies nicht taten und ich jetzt die real überlappend in den dword stehen habe, d.h. das high-Word eines real steht im low-Word eines dword und das low-Word des gleichen real steht im high-Word des darauffolgenden dword. Etwa so:


```
REAL:      [fedcba9876543210  fedcba9876543210]
DWORD:  ... fedcba9876543210][fedcba9876543210 ...
```

Deshalb sieht mein Programm zum Mapping entsprechend gruselig aus. Zum einen eine union mit einem real und einem dword als Hilfskonstruktion (weil real_to_dword bzw. dword_to_real nicht sauber bzw. gar nicht funktionieren) und zum anderen haufenweise shl( ... and 2#00000000000000001111111111111111, 16) bzw. shr(...) wegen der Überlappung.

Und um Deinem nächsten Einwand zuvorzukommen:
_Ich _hätte einfach real_to_dint(trunc(real*1000.0)) geschickt, dann hätte auf Roboterseite ein REAL = SIGNAL/1000.0 gereicht. Fertig. Dazu hätte man "nur" die Überlappung rausnehmen müssen, aber das war den Herren zu aufwändig. So hab ich jetzt halt 3 Tage damit verdaddelt. :roll:

Immerhin weiß ich jetzt über IEEE-konforme Fließkommazahlen bescheid und wie man sie berechnet. 

Vielen Dank an alle für die vielen Tips. 

Gruß
Jörn


----------



## ms_gp (14 September 2020)

Ja ja, die Schnittstellen. Ich habe manchmal das Gefühl das 50% meiner Arbeit dem Definieren, Absprechen und Programmieren von Schnittstellen zwischen Geräten besteht.

Aber das was du beschreibst hatte ich noch nicht. Wenn du auf deiner Seite solche Klimmzüge machen musst, dann muss die Gegenseite das doch auch? Wie kommt das zustande?


----------



## PN/DP (14 September 2020)

Jörn schrieb:


> Deshalb sieht mein Programm zum Mapping entsprechend gruselig aus. Zum einen eine union mit einem real und einem dword als Hilfskonstruktion (weil real_to_dword bzw. dword_to_real nicht sauber bzw. gar nicht funktionieren) und zum anderen haufenweise shl( ... and 2#00000000000000001111111111111111, 16) bzw. shr(...) wegen der Überlappung.


Das sieht so gruselig aus, weil Du (noch) keine Ahnung hast, wie man das ganz einfach macht. Aber Hallo, das Zusammenbasteln von Variablen verschiedenster Datentypen aus Word-Stücken ist doch unser täglich Brot  Schau Dir mal Modbus an, da geht das nur so und da kannst Du keinen anderen Jungs die Schuld geben...

Ich kenne Twincat 3 nicht und weiß nicht genau ob die Syntax ganz richtig ist, doch das Basteln sollte ungefähr so einfach aussehen:

```
TYPE U_REALWORDS:
  UNION
    uReal:   REAL;
    uaWord:  ARRAY[0..1] OF WORD;   //(könnte auch eine Struktur mit 2 Word sein)
  END_UNION
END_TYPE

tempU : U_REALWORDS;


tempU.uaWord[0] := dasEineWord;
tempU.uaWord[1] := dasAndereWord;
rMyReal := tempU.uReal;
```




Jörn schrieb:


> Und um Deinem nächsten Einwand zuvorzukommen:
> _Ich _hätte einfach real_to_dint(trunc(real*1000.0)) geschickt, dann hätte auf Roboterseite ein REAL = SIGNAL/1000.0 gereicht. Fertig. Dazu hätte man "nur" die Überlappung rausnehmen müssen, aber das war den Herren zu aufwändig. So hab ich jetzt halt 3 Tage damit verdaddelt. :roll:


So was würde Dein Problem nur verkomplizieren anstatt vereinfachen. Ob man einen REAL direkt verschickt oder vorher durch 1000.0 dividiert, macht keinen Unterschied, nur mehr Verwirrung und mehr Aufwand.




Jörn schrieb:


> Immerhin weiß ich jetzt über IEEE-konforme Fließkommazahlen bescheid und wie man sie berechnet.


Aha - Dein Problem hatte aber doch gar nichts mit irgendeiner Berechnung von Fließkommazahlen zu tun...  

Harald


----------



## PN/DP (14 September 2020)

PS: Wenn das REAL über 2 DWORDs überlappend liegt (und wenn Du Glück mit den Indianern hast) dann kannst Du den REAL so zusammensetzen:

```
TYPE U_REALDWORDS :
  UNION
    STRUCT
      uTrash1 : WORD;
      uReal   : REAL;
      uTrash2 : WORD;
    END_STRUCT
    uaDword : ARRAY[0..1] OF DWORD;
  END_UNION
END_TYPE

tempU : U_REALDWORDS;


tempU.uaDword[0] := dasEineDWord;
tempU.uaDword[1] := dasAndereDWord;
rMyReal := tempU.uReal;
//xresult := F_SwapRealEx(rMyReal); //falls die Words noch getauscht werden müssen
```

PPS: gibt es in TC3 nicht schon was fertiges, um die zwei WORDs in einem DWORD zu tauschen (swap-en), anstatt mit Shiften und Ver-ORen zu hantieren?

Harald


----------



## ms_gp (14 September 2020)

Im Systemmanager kann man einstellen man hibyte und lobyte und oder loword und hiword tauschen will. Geht aber nur für das ganze Modul.

Aber nochmal meine Frage:Wie sieht das auf der anderen Seite aus? Vielleicht ist da nur was um 2 Byte verschoben und die müsstest nur einen Teil der ganzen Daten um 2 Byte shiften und dann passt alles. Es muss ja eine Definition der einzelnen datenelemente geben und wie groß sie sind.


----------



## Jörn (15 September 2020)

PN/DP schrieb:


> So was würde Dein Problem nur verkomplizieren anstatt vereinfachen. Ob man einen REAL direkt verschickt oder vorher durch 1000.0 dividiert, macht keinen Unterschied, nur mehr Verwirrung und mehr Aufwand.


Das Problem ist des jeweiligen Herstellers interne Darstellung von REAL und die Tatsache, daß da einige nicht am gleichen Süppchen mitkochen. Viele Hersteller arbeiten inzwischen mit REAL, die der IEEE entsprechen, aber eben nicht alle. Wo ich bisher so gut wie keine Probleme hatte, war, mit real_to_dint(trunc(real*1000.0)) einfach ein INT zu schicken, das auf der Gegenseite mit  int_to_real() bzw. int/1000.0 wieder in ein REAL umgewandelt wird.




ms_gp schrieb:


> ... Wie sieht das auf der anderen Seite aus? Vielleicht ist da nur was um 2 Byte verschoben und die müsstest nur einen Teil der ganzen Daten um 2 Byte shiften und dann passt alles. Es muss ja eine Definition der einzelnen datenelemente geben und wie groß sie sind.


Die high- und low-Bytes zu tauschen hab ich schon probiert, das ist es nicht.  Die Gegenseite ist ein KUKA und obwohl dort die entsprechende Variable, in die der Werte dann geschrieben wird, als REAL deklariert ist, kommt dort immer ein verdammt großes INT an. KUKA hat nur die Datentypen INT, REAL, BOOL und CHAR. Ein int_to_real() gibt es bei KUKA nicht! Eine Umwandlung von int nach real macht man mit int/1000*.0*! Das *.0* ist hier wichtig, Stichwort implizite Typkonvertierung. Wenn man die .0 weglässt kommt wieder ein int raus. Da hab ich in meinem letzten KUKA-Programm auch schon mal einen Tag lang nach gesucht. 

Hier ein Auszug aus der KUKA-Doku. Mehr als das erfährt man nicht:


----------



## PN/DP (15 September 2020)

Jörn schrieb:


> Das Problem ist des jeweiligen Herstellers interne Darstellung von REAL und die Tatsache, daß da einige nicht am gleichen Süppchen mitkochen. Viele Hersteller arbeiten inzwischen mit REAL, die der IEEE entsprechen, aber eben nicht alle.


Klingt unglaublich. Hättest Du mal ein Beispiel wer sich nicht an die IEEE hält? 



Jörn schrieb:


> Wo ich bisher so gut wie keine Probleme hatte, war, mit real_to_dint(trunc(real*1000.0)) einfach ein INT zu schicken, das auf der Gegenseite mit  int_to_real() bzw. int/1000.0 wieder in ein REAL umgewandelt wird.


Solche Krücken sollte man nur brauchen, wenn man die Länge der verschickten Daten verringern muß. Normalerweise ist das absichtliche Umgehen/Vermeiden des REAL-Datentypes nicht nötig.



Jörn schrieb:


> obwohl dort die entsprechende Variable (...) als REAL deklariert ist, kommt dort immer ein verdammt großes INT an.


Da kommt ziemlich sicher nicht ein "verdammt großes INT" an, sondern 32 Bits in 4 Bytes, die sich irgendjemand fälschlicherweise als Ganzzahl anschaut. Dabei muß man sich das Bitmuster der 4 Bytes einfach nur als REAL anschauen, dann sieht man, daß das tatsächlich ein REAL ist.


Betreffen die "Überlappungenen" bzw. der 2 Byte Versatz viele/alle Daten in dem ganzen empfangenen Datenblock? Wie ms_gp schon schrieb, wäre es in dem Fall einfacher, den betreffenden Datenblock-Teil um 2 Byte zu verschieben und dann passt die vereinbarte Struktur wieder über die Daten? Vielleicht hast Du auch nur irgendwo in Deinem Programm beim Kopieren/Übergeben des Empfangstelegramms eine falsche um 2 Byte versetzte Anfangsadresse angegeben? Oder entspricht der von den Anderen gesendete Datenblock gar nicht Euren Vereinbarungen, vielleicht sind da nur unerwartet/unbemerkt zu viele oder zu wenig Füll-Bytes drin?

Ich will Dir nicht zu nahe treten, aber sorry: Was hast Du eigentlich für einen Job, daß Du für die Lösung simpler Standard-Probleme mehrere Tage verdaddeln kannst? Vielleicht sollte Dein AG Dich mal zu einem guten Programmier-Lehrgang schicken?  Wenn andere Leute etwas für Dich unverständliches tun, dann heißt das nicht automatisch, daß "die Anderen" etwas falsch machen...

Harald


----------



## Thomas_v2.1 (15 September 2020)

PN/DP schrieb:


> Klingt unglaublich. Hättest Du mal ein Beispiel wer sich nicht an die IEEE hält?



Siemens bei der S5 zum Beispiel.


----------



## PN/DP (15 September 2020)

Thomas_v2.1 schrieb:


> PN/DP schrieb:
> 
> 
> > Klingt unglaublich. Hättest Du mal ein Beispiel wer sich nicht an die IEEE hält?
> ...


Ja klar. Da kann ich so ziemlich jede Steuerung und Computer nehmen, die älter als der Standard IEEE 754 ist, und ihr vorwerfen, daß sie den damals noch gar nicht existierenden Standard nicht verwendet ... 

Siemens nennt das Gleitpunktformat der S5 auch nicht REAL oder FLOAT. Und Siemens liefert Konvertierbausteine zur Anpassung S5 Gleitpunkt <-> IEEE

Harald


----------



## Jörn (16 September 2020)

Ah, da ist jemand mit einer Goldwaage unterwegs?! Dem Mann kann geholfen werden. 



PN/DP schrieb:


> Klingt unglaublich. Hättest Du mal ein Beispiel wer sich nicht an die IEEE hält?


Wie in dem weiter oben von mir verlinkten Artikel bei Wikipedia zu lesen ist, halten sich z.B. einige Systeme von IBM nicht daran, welche, nebenbei bemerkt, ab 2000 am Markt sind. Ich sagte _nicht_, was wohl aus meiner Aussage herausgelesen werden konnte, daß Beckhoff und/oder KUKA sich nicht daran halten. Diese missverständliche Formulierung bitte ich zu entschuldigen.

Es wäre noch anzufügen, daß sich bei meiner weiteren Recherche herausgestellt hat, daß, obwohl es im zuvor geposteten Ausschnitt den KUKA-Handbuches nicht explizit erwähnt wird, KUKA sich scheinbar auch daran hält. Zumindest stimmen die im Handbuch angegebenen Grenzen mit denen in der IEEE angegebenen Werten (1) exakt überein: 1.1E-38 bis 3.4E38.




PN/DP schrieb:


> Da kommt ziemlich sicher nicht ein "verdammt großes INT" an, sondern 32 Bits in 4 Bytes, die sich irgendjemand fälschlicherweise als Ganzzahl anschaut. Dabei muß man sich das Bitmuster der 4 Bytes einfach nur als REAL anschauen, dann sieht man, daß das tatsächlich ein REAL ist.


Auch hier entschuldige ich meine unklare Ausdrucksweise. Genauer gesagt passiert folgendes:
Ich setze in der SPS eine REAL-Variable, die zusammen mit einem DWORD in der oben geposteten UNION steht, auf den Wert 18.4. In dem DWORD steht dann das korrekte Bituster dieses Wertes drin: 01000001100100110011001100110011.

Das DWORD aus der UNION wird dann via Schnittstelle an den KUKA geschickt. Beim Roboter kommt es auf einem Gruppensignal (SIGNAL GI_Signalname $in[...] TO $in[...] ) an. Im Gruppensignal ist das Bitmuster korrekt, d.h. die einzelnen binären Eingänge sind korrekt gesetzt: 01000001100100110011001100110011. Als INT interpretiert entspricht das dem Wert 1100165939. Wenn ich jetzt der REAL-Variable (REAL R_realname ) diesen Wert zuweise, dann steht nicht 18.4 in der REAL, wie man es erwarten würde, sondern "ein verdammt großer INT", genauer gesagt die INT-Repräsentation des Gruppensignales, der oben genannte Wert von 1100165939, in REAL-_Darstellung_, d.h. 1.1006589E+09.

An diesem Punkt wäre das Thema für mich - eigentlich - beendet, da ich aber auch KUKA programmiere und ich auch schon ab und an über dieses Problem gestolpert bin, war es mir ein Anliegen es auch zu lösen. Völlig ohne Eigennutz natürlich. 

Das Problem konnte inzwischen auch dank der tatkräftigen Hilfe eines KUKA-Technikers gelöst werden. Um wieder die 18.4 in die REAL-Variablen zu bekommen, wird der Gruppeneingang als stream betrachtet und in einer kleinen Funktion (ein 5-Zeiler) mittels cast_from auf eine temporäre Variable geschrieben, welche wiederum mit cast_to, beide Bestandteil von CREAD/CWRITE, in die real-Variable geschrieben wird. Im Programm sieht das dann so aus:


```
; So geht es nicht:
R_realname = GI_Signalname

; Aber so geht es. stream2real ist eine kleine Hilfsfunktion:
R_realname = stream2real(GI_Signalname)
```




PN/DP schrieb:


> Ich will Dir nicht zu nahe treten, aber sorry: Was hast Du eigentlich für einen Job, daß Du für die Lösung simpler Standard-Probleme mehrere Tage verdaddeln kannst? Vielleicht sollte Dein AG Dich mal zu einem guten Programmier-Lehrgang schicken?  Wenn andere Leute etwas für Dich unverständliches tun, dann heißt das nicht automatisch, daß "die Anderen" etwas falsch machen...


Du trittst mir nicht zu nahe. Besonders nicht, wenn Du die Aussage "Du bist imkompetent" durch einen _so _großen und hübschen Strauß Blumen sagst. 
Es wurde sich hier im Forum auch schon bei mir entschuldigt. 

Scheinbar wird die Netiquette hier gepflegt. Zusammen mit der i.d.R. sehr schnellen und kompetenten Hilfe, die man hier bekommt, auch, wenn man mal "noob"-Fragen stellt, macht das dieses Forum äußerst sympatisch. Einzig der facepalm-Smiley fehlt mir zur vollkommenen Glücksehligkeit. 

Ein "bißchen was" verstehe ich vom programmieren übrigens schon, bei TwinCAT 3 bin ich aber tatsächlich "noob".

Ich hoffe ich konnte alle Klarheiten beseitigen und gelobe zukünftig größere Sorgfalt bei meinen Fragestellungen und meinen Antworten walten zu lassen. 

Gruß
Jörn

(1) https://de.wikipedia.org/wiki/IEEE_754


----------



## PN/DP (16 September 2020)

Jörn schrieb:


> Wenn ich jetzt der REAL-Variable (REAL R_realname ) diesen Wert zuweise, dann steht nicht 18.4 in der REAL, wie man es erwarten würde, sondern "ein verdammt großer INT", genauer gesagt die INT-Repräsentation des Gruppensignales, der oben genannte Wert von 1100165939, in REAL-_Darstellung_, d.h. 1.1006589E+09.


Vermutlich das gleiche Problem wie bei allen Codesys-Dialekten: der Compiler macht da keine Zuweisung des Bitmusters sondern konvertiert den Wert von Ganzzahl zu REAL. Man bräuchte einen type cast, den es aber in Codesys nicht gibt. Da läßt sich der Datentyp eines Bitmusters nur ändern durch Umkopieren mittels Pointer oder MEMCOPY oder UNION.

Harald


----------



## ms_gp (16 September 2020)

Jetzt will ich vom Jörn aber immer noch wissen, wie die Schnittstelle zu seinem Roboter aufgebaut ist. Welches Format die Fließkommazahlen haben ist ja zweitrangig. Es muss doch eine Art Tabelle geben in der steht, was in welcher Reihenfolge übertragen wird.

Erst dann können wir die richtihe Lösung finden


----------



## Jörn (17 September 2020)

@ ms_gp:
Um Deine Frage aus Post #13 zu beantworten wie das zustande kommt:
Der Roboterprogrammierer hat _ohne _Absprache, wie es eigentlich ausgemacht war, die Schnittstelle einfach mal auf Roboterseite definiert. Ich musste die dann so übernehmen. Und jetzt möchte er es nicht mehr ändern, weil es so viel Aufwand ist.


Mit Augenmerkt auf die dword sieht die Schnittstelle so aus:


```
dword[0]  - 32 BOOL
dword[1]  - 32 BOOL
dword[2]  - 16 BOOL,   INT
dword[3]  - INT,       INT
dword[4]  - INT,       hi REAL1
dword[5]  - lo REAL1,  hi REAL2
dword[6]  - lo REAL2,  hi REAL3
dword[7]  - lo REAL3,  hi REAL4
// weitere REAL
dword[12] - lo REAL8,  hi REAL9
dword[13] - lo REAL9,  hi REAL10
dword[14] - lo REAL10, INT
dword[15] - INT,       INT
dword[16] - INT,       INT
dword[17] - INT,       INT
dword[18] - INT,       hi REAL11
dword[19] - lo REAL11, hi REAL12
dword[20] - lo REAL12, hi REAL13
dword[21] - lo REAL13, INT
dword[22] - INT,       hi REAL14
dword[23] - lo REAL14, hi REAL15
dword[21] - lo REAL13, INT
dword[22] - INT, hi REAL14
dword[23] - lo REAL14, hi REAL15
// weitere REAL
dword[30] - lo REAL21, hi REAL22
dword[31] - lo REAL22, INT
```


Mit Augenmerk auf die eigentlichen Signale sieht sie so aus:


```
// Ausschnitt

...
dword[4].0-15                    - INT
dword[4].16-31  + dword[5].0-15  - REAL1
dword[5].16-31  + dword[6].0-15  - REAL2
...
dword[13].16-31 + dword[14].0-15 - REAL10
dword[14].16-31                  - INT
...
```


Das Mapping hab ich dann so gelöst. Ich hab in die union noch zwei word mit reingenommen, wie Harald es vorschlug:


```
// Ausschnitt

...
IOstRob.uRobIn.arDwordRobIn[2].13 := stKukaInOEM.bx9_5;
IOstRob.uRobIn.arDwordRobIn[2].14 := stKukaInOEM.bx9_6;
IOstRob.uRobIn.arDwordRobIn[2].15 := stKukaInOEM.bx9_7;
IOstRob.uRobIn.arDwordRobIn[2]    := SHL(INT_TO_DWORD(stKukaInOEM.ix10), 16) OR IOstRob.uRobIn.arDwordRobIn[2];
IOstRob.uRobIn.arDwordRobIn[3]    := SHL(INT_TO_DWORD(stKukaInOEM.ix14), 16) OR INT_TO_DWORD(stKukaInOEM.ix12);


ioPuffer[1].uReal  := stKukaInOEM.rx18Entn1Versatz;
ioPuffer[2].uReal  := stKukaInOEM.rx24Entn1_X;
ioPuffer[3].uReal  := stKukaInOEM.rx28Entn1_Y;
...


IOstRob.uRobIn.arDwordRobIn[4]    := ioPuffer[1].uWord[loWord] OR INT_TO_DWORD(stKukaInOEM.ix16);
IOstRob.uRobIn.arDwordRobIn[5]    := ioPuffer[2].uWord[loWord] OR ioPuffer[1].uWord[hiWord];
IOstRob.uRobIn.arDwordRobIn[6]    := ioPuffer[3].uWord[loWord] OR ioPuffer[2].uWord[hiWord];
...
```


----------



## PN/DP (17 September 2020)

Du könntest vor dem Mapping (Umkopieren der einzelnen Werte) das empfangene DWORD-Array auf eine zur tatsächlichen Struktur passende Struktur kopieren (z.B. MEMCPY), das würde das Mapping erheblich vereinfachen.

Harald


----------



## PN/DP (17 September 2020)

Jörn schrieb:


> Der Roboterprogrammierer hat _ohne _Absprache, wie es eigentlich ausgemacht war, die Schnittstelle einfach mal auf Roboterseite definiert. Ich musste die dann so übernehmen.


Das ist doch nicht unnormal, das passiert jedem von uns. Wenn das Fremdsystem z.B. eine Wiegeeinrichtung wäre, dann fragt der Waagehersteller Dich auch nicht, wie Dir die Schnittstelle angenehm wäre, sondern er implementiert eine Standard-Schnittstelle nach seinen Vorstellungen.

Es ist doch eigentlich Wurst wie der Roboterprogrammierer die Schnittstelle definiert hat, Hauptsache man kann sie im eigenen System nachbauen (in STEP7 kann man z.B. keine INT/REAL/... auf einer ungeraden Adresse anfangen lassen). Dafür erstellt man sich eine Struktur die die logische Struktur der Schnittstelle abbildet und kopiert die Empfangsdaten aus dem Empfangspuffer (z.B. Byte/Word/DWord-Array) "am Stück" (MEMCPY) in diese Struktur und kann sich dann die einzelnen Variablen easy auf eine weitere Arbeits/Koppelstruktur umkopieren, deren Variablen dann im Programm verwendet werden.

z.B.

```
//pragma pack... ?

STRUCT
  Bit_1_1 : BOOL;
...
  Bit_1_32 : BOOL;
  Bit_2_1 : BOOL;
...
  Bit_2_32 : BOOL;
  Bit_3_1 : BOOL;
...
  Bit_3_16 : BOOL;
  Int_1 : INT;
  Int_2 : INT;
  Int_3 : INT;
  Int_4 : INT;
  Real_1 : REAL;
  Real_2 : REAL;
...
END_STRUCT
```
(bei Codesys/Twincat weiß ich nicht wie man ein BOOL auf ein Bit deklariert - evtl. mit einem Pragma?)

Ich arbeite nur mit solchen Datenstrukturen für Empfangs/Sende-Daten, weil bei Kommunikation mit Fremdsystemen eigentlich fast immer Byte/Word/DWord-Arrays übertragen werden, die nicht der wirklichen logischen Struktur entsprechen.

Harald


----------



## StructuredTrash (17 September 2020)

Ganz so einfach ist es wohl nicht. Wenn ich es richtig sehe, müssen low und high word der REALs auch noch getauscht werden. Trotzdem wäre eine Lösung mit einer UNION elegant. Vielleicht so:
Erstmal ein paar Typen:

```
TYPE arrHW :   // Für Verknüpfung mit der HW
   ARRAY[0..31] OF DWORD;
END_TYPE

TYPE str32Bits :   // Bit-Struktur mit 32 Bits (einzelne Bits gibt es bei Codesys nicht)
STRUCT
   Bit00,
   Bit01,
   // Bit02..Bit30
   Bit31:BIT;
END_STRUCT
END_TYPE

TYPE str16Bits :   // Bit-Struktur mit 16 Bits
STRUCT
   Bit00,
   Bit01,
   // Bit02..Bit14
   Bit15:BIT;
END_STRUCT
END_TYPE

{attribute 'pack_mode' := 1} 
TYPE strPLC :   // Struktur für das PLC-Programm
STRUCT
   Bits_0_31:str32Bits;   // 0
   Bits_32_63:str32Bits;   // 4
   Bits_64_79:str16Bits;   // 8
   Int1,   // 10
   Int2,   // 12
   Int3,   // 14
   Int4:INT;   // 16
   Real1,   // 18
   Real2,   // 22
   Real3,   // 26
   Real4,   // 30
   Real5,   // 34
   Real6,   // 38
   Real7,   // 42
   Real8,   // 46
   Real9,   // 50
   Real10:REAL;   // 54
   Int5,   // 58
   Int6,   // 60
   Int7,   // 62
   Int8,   // 64
   Int9,   // 66
   Int10,   // 68
   Int11,   // 70
   Int12:INT;   // 72
   Real11,   // 74
   Real12,   // 78
   Real13:REAL;   // 82
   Int13,   // 86
   Int14:INT;   // 88
   Real14,   // 90
   Real15:REAL;   // 94
   Int15,   // 98
   Int16:INT;   // 100
   Real16,   // 102
   Real17,   // 106
   Real18,   // 110
   Real19,   // 114
   Real20,   // 118
   Real21:REAL;   // 122   Real22 wäre eins zuviel, verzählt?
   Int17:INT;   // 126
END_STRUCT
END_TYPE

{attribute 'pack_mode' := 1} 
TYPE strSwap : Für Lo/hi word-Tausch in REALS
STRUCT
   W1:WORD;   // Word dummy 1
   DW:ARRAY[0..30] OF DWORD;   // DWORDs auf den späteren REAL-Adressen
   W2:WORD;   // Word dummy 2
END_STRUCT
END_TYPE

TYPE uniKukaInterface :
UNION
   HW:arrHW;
   PLC:strPLC;
   Swap:strSwap;
END_UNION
END_TYPE
```
Und dann noch ein bisschen Code:

```
FOR Loop:=4 TO 13 DO   // Real 1..10
   KukaInterface.Swap.DW[Loop]:=ROL(KukaInterFace.Swap.DW[Loop],16);
END_FOR
FOR Loop:=18 TO 20 DO   // Real 11..13
   KukaInterface.Swap.DW[Loop]:=ROL(KukaInterFace.Swap.DW[Loop],16);
END_FOR
FOR Loop:=22 TO 23 DO   // Real 14,15
   KukaInterface.Swap.DW[Loop]:=ROL(KukaInterFace.Swap.DW[Loop],16);
END_FOR
FOR Loop:=25 TO 30 DO   // Real 16..21
   KukaInterface.Swap.DW[Loop]:=ROL(KukaInterFace.Swap.DW[Loop],16);
END_FOR
```


----------



## PN/DP (17 September 2020)

@StructuredTrash
Sehe ich das richtig, daß Du Werte liest, swapst, und auf sich selbst zurückspeicherst? Das würde ich nicht machen, weil das darf man nur dann genau einmal machen wenn man neue Daten erhalten hat. Ich würde immer umkopieren auf einen zweiten Arbeits-Speicherbereich - mit oder ohne Swap, je nach dem wie es nötig ist. Dann ist auch egal in welcher Task ich mit den Daten im Arbeits-Speicherbereich arbeite.

Harald


----------



## StructuredTrash (17 September 2020)

@PN/DP:
Dein Einwand ist natürlich berechtigt. Der Swap-Code-Aufruf und die weitere Bearbeitung der Daten müssen in der Task erfolgen, an der die EtherCat-Bridge hängt, und der Datenaustausch zwischen den beiden EtherCat-Strängen muss synchron laufen. Mit der EL6695 soll das laut Beckhoff möglich sein. Ich habe aber selbst mit dieser Bridge noch nicht gearbeitet.
Aber auch wenn umkopiert werden muss, ist so eine Union hilfreich. Wenn man beide Variablen vom Uniontyp hat, kann man das mit einer einfachen Zuweisung machen.


----------



## ms_gp (17 September 2020)

Ich frage mich wirklich, was sich der Roboter-Programmier dabei gedacht hat. Der hätte doch nur das letzte INT nach oben schieben müssen und alles wäre gut gewesen. Aber man kann sich auch schwer machen. Aber ich hab's ja schon gesagt. Mit Schnittstellen kann man ganz Völkerscharen beschäftigen. Und am Ende braucht man eh immer nur 3 der Werte und 3 Bits und der Rest ist für den Eimer. 

Na ja. Ich weiß nicht, ob es in diesem Fall geht, aber wir tauschen auch Daten zwischen unseren Steuerungen mit der EL6695 aus. Dabei definieren wir aber gar kein Array of Irgendwas, sondern schreiben einfach genau ein PDO vom Type der Schnittstelle in die Klemme. Dann verknüpft man dieses PDO mit der Variable im Programm. Wenn auf beiden Steuerungen die Struktur gleich definiert wurde, dann funktioniert das. Ohne Zwischenschritt. Ist eigentlich genauso als würde man Variablen zwischen Tasks austauschen. Allerdings gibt's da keine Probleme mit den Indianern und das Byte Alignment ist bekannt und kann gleich eingestellt werden.


----------



## PN/DP (18 September 2020)

ms_gp schrieb:


> Ich frage mich wirklich, was sich der Roboter-Programmier dabei gedacht hat.


Vielleicht hat er einfach mit MEMCPY seine Struktur in das DWord-Array der Klemme kopiert, oder arbeitet mit einer UNION seiner Struktur und einer DWord-Struktur für die Klemme (Hauptsache die Gesamtlänge stimmt und Konsistenz ist über die gesamte Länge, und eine DWord-Struktur macht am wenigsten Schreibarbeit) und gedacht, der Partner könne das genau so einfach machen?
Oder ist da noch irgendwo ein Word-Array beteiligt (wie bei Modbus)? Da wäre es aus seiner Sicht egal in welche Nachbar-Words die Reals aufgeteilt werden.

Was mich ein bisschen wundert ist, daß bei den Reals nur die H/L-Words vertauscht sind und nicht alle 4 Bytes, wie bei einem big-endian/little-endian-Problem zu erwarten wäre (die Byte-Reihenfolge ist 3412 anstatt 4321) - Warum?

Harald


----------



## StructuredTrash (18 September 2020)

PN/DP schrieb:


> Was mich ein bisschen wundert ist, daß bei den Reals nur die H/L-Words vertauscht sind und nicht alle 4 Bytes, wie bei einem big-endian/little-endian-Problem zu erwarten wäre (die Byte-Reihenfolge ist 3412 anstatt 4321) - Warum?



1) "Hey, ich hab' ne Intel-CPU. Du musst noch die Bytes tauschen!"
2) "Warum soll ich die ganze Arbeit allein machen. Ich tausche die Bytes, um die Words soll sich der andere kümmern."
3) Es ist was ganz Spezielles, und die Behauptung, eine nachträgliche Änderung wäre zu aufwändig, ist mehr als nur eine Ausrede.


----------



## Lambo89 (22 Oktober 2020)

Hallo Ihr Lieben,

ich habe mich auch an die Umwandlung FloatToReal versucht. Leider komme ich nicht so recht auf einen Nenner. Sobald meine Floatzahl kleiner 1 wird erhalte ich kein ergebnis mehr. Ich habe mich jetzt 2 Tage damit beschäftigt aber mein Verstand reicht scheinbar nicht dafür aus. :icon_cry: 
Grundlage war Wikipedia: https://de.wikipedia.org/wiki/IEEE_754

Zuerst ordne ich die Bytes in ein DWord. Danach nehme ich das DWord und zerlege es in Vorzeichen,Mantisse und Exponent und verrechne dies.
Dies funktioniert aber leider nich unter 1(als Realwert). 
Das Ergebniss wird als Infinity wiedergegeben obwohl wenn ich dies selbst nachrechne das gerechnete ergebnis richtig scheint.

Zahl: 0,627009646302255
DWORD: 1059095476

rErgebnis := rVZ*rManti*(EXPT(2, usiExponent-127));  = 1(VZ)* 1,25(Manti) * 0,5(Exponent) = 0,625(Infinity)





Vielleicht kann mir jemand helfen mein Problem zu finden.


----------



## PN/DP (22 Oktober 2020)

Lambo89 schrieb:


> ich habe mich auch an die Umwandlung FloatToReal versucht.


Wie umrechnen Float zu Real? Float = Real, da braucht man nichts rechnen.

Nimm das Bitmuster des DWORD und speichere es in eine REAL-Variable bzw. speichere es auf einen 32-Bit-Speicherplatz und hole es als REAL-Wert wieder raus.
DWord-Bitmuster in REAL-Variable speichern:

```
VAR
  dwVar : DWORD;            //Eingang DWORD
  pReal : POINTER TO REAL;
  rReal : REAL;             //Ausgabe REAL
END_VAR

dwVar := MyDWord;     //Bitmuster in das DWord speichern
pReal := ADR(dwVar);  //Pointer auf das DWord setzen
rReal := pReal^;      //REAL-Wert aus dem DWord in Real-Variable speichern
```
Oder den REAL-Wert via UNION aus Words oder Bytes zusammenbasteln:

```
TYPE U_REALWORDS :
  UNION
    uReal  : REAL;
    uaWord : ARRAY[0..1] OF WORD;
  END_UNION
END_TYPE

tempU : U_REALWORDS;


tempU.uaWord[0] := dasEineWord;
tempU.uaWord[1] := dasAndereWord;
rMyReal := tempU.uReal;
```


```
TYPE U_REALBYTES :
  UNION
    uReal  : REAL;
    uaByte : ARRAY[0..3] OF BYTE;
  END_UNION
END_TYPE

tempU : U_REALBYTES;


tempU.uaByte[0] := Byte_1;
tempU.uaByte[1] := Byte_2;
tempU.uaByte[2] := Byte_3;
tempU.uaByte[3] := Byte_4;
rMyReal := tempU.uReal;
```



Lambo89 schrieb:


> Zahl: 0,627009646302255
> DWORD: 1059095476


Übrigens: das Bitmuster 1059095476 dez = 16#3F2083B4 = 0.627009630203
siehe z.B. diesen IEEE-754 Konverter

Harald


----------



## Lambo89 (22 Oktober 2020)

Vielen Dank für diese Ausleselösung, habe es so probiert und es funktioniert natürlich super. :-D

Was mich jetzt aber noch interessiert warum die Umwandlung nicht funktioniert hat. Es sollte ja möglich sein über das "extrahieren" der Bits und Bytes auch auf das Ergebnis zu kommen. Was habe ich da falsch gemacht das dies im 0,xxx Bereich nicht funktioniert hat.
Hat hierzu jemand eine Antwort? Mich würde mein Fehler sehr interessieren.


----------



## Lambo89 (28 Oktober 2020)

Hallo,

habe mich nochmal mit meinem Problem beschäftigt. Ich denke ich kam nicht zu einem Ergebnis, da EXPT(x,-1) mit negativen Exponenten probleme hatte . Zumindest funktionierte es nach dem ich es in
1 / EXPT(x,1) umgeschrieben hatte. Danach habe ich noch einwenig getestet und mein Ergebnis wäre unten mitzuverfolgen. Wenn jemand noch Fehler/ Verbesserungen sieht freue ich mich darüber. CLOSE 


```
//////////////////////////////////////////////////////////
//    Berechnung von Float nach Real                        
//                                                        
//    Ergebnis = Vorzeichen * Exponent * Mantisse           
//                                                        
//    Vorzeichen     = (-1)^ VZ                                
//    Exponent    = 2^(Exp-127) od. 1/(2^126)               
//    Mantisse    = 1+(Mant/(2^23)) od. (Mant/(2^23))       
//                                                        
//                                                        
//////////////////////////////////////////////////////////
ObError := FALSE;

// Vorzeichen
rVZ:= EXPT(-1, usiVorzeichen);

    //Null
IF (usiExponent = 0) AND (diMantisse = 0) THEN
    rExpo := 0;
    rManti:= 0;
    
    //denormalisierte Zahl
ELSIF (usiExponent = 0) AND (diMantisse > 0) THEN

    rManti         := (diMantisse/EXPT(2, 23));
    rExpo        := (EXPT(2, 1-127));

    //Unendlich
ELSIF (usiExponent = 255) AND (diMantisse = 0) THEN
    rExpo := 0;
    rManti:= 0;
    ObError := TRUE; (*Infinity*)
    
    //keine Zahl
ELSIF (usiExponent = 255) AND (diMantisse > 0) THEN
    rExpo := 0;
    rManti:= 0;
    ObError := TRUE; (*NaN*)

    // normalisierte Zahl
ELSE
    rManti         := 1+(diMantisse/EXPT(2, 23));
    IF (usiExponent < 127) THEN 
    rExpo        := (1/EXPT(2, 127-usiExponent));

    ELSE
    rExpo        := (EXPT(2, (usiExponent-127)));

    END_IF    
END_IF

rErgebnis    := rVZ*rManti*rExpo;

// Ergebnisausgabe
OrErgebnis:= rErgebnis;
```


----------

