# libnodave - Float auf SPS schreiben



## seeba (13 November 2005)

Hallo Zottel,
Hallo Forum Mitbenutzer,
folgende Frag: Gibt es auch eine Funktion um einen Floatwert wieder zurück zu wandel? Ähnlich daveGetFloat? Also sowas wie daveGetBytes(Floatwert)?

Gruß Sebastian


----------



## Zottel (13 November 2005)

seeba schrieb:
			
		

> Hallo Zottel,
> Hallo Forum Mitbenutzer,
> folgende Frag: Gibt es auch eine Funktion um einen Floatwert wieder zurück zu wandel? Ähnlich daveGetFloat? Also sowas wie daveGetBytes(Floatwert)?
> 
> Gruß Sebastian


daveToPLCFloat(float f) liefert ein formal ein Integer. Die 4 Bytes enthalten eine Gleikommazahl im Format der SPS. Daß der Rückgabetyp int ist, liegt daran, daß verhindert werden soll, daß die Laufzeitumgebung bei der Zuweisung an eine PC-Variable auf Gültigkeit prüft.
Das Ergebnis kann als "puffer" an daveWriteBytes übergeben werden. Siehe Beispiel in testMPI.cs.
In C gibt es darüber hinaus die Funktionen davePutFloat und davePutFloatAt, die eine Gleitkommazahl  in das Format der SPS wandeln und die Byte-Sequenz als nächstes. bz.w an der angegebenen Stelle in einen Puffer kopieren. Diese Funktionen können genutzt werden um, mehrere aufeinander folgende Werte in einen Schreibpuffer zu packen.
In Dot.Net sind sie nicht implementiert, da ich nicht finden konnte, ob Berechnungen mit Zeigern auf von Dot.NET erzeugte Puffer IMMER funktionieren müssen. Statt dessen können mit Dot.NET-spezifischen Funktionen 4 Bytes des Ergebnisses von daveToPLCFloat(float f) in einen Puffer vom Type Byte[] kopiert werden.


----------



## seeba (13 November 2005)

Ich will mal meinen Wunsch äußern, wenn ich darf: Ich würd mir Funktionen wünschen der man den Buffer vorgeben kann. 

z.B: daveGetU8(buffer, position)

und auch umgekehrt!

z.B.: daveGetBytesFromU16(buffer, position)

Das würde die Arbeit mit libnodave noch um einiges vereinfachen.

Wäre schön, wenn du sowas integrierst, aber kann auch sein das ich mal wieder blind bin.


----------



## Zottel (14 November 2005)

seeba schrieb:
			
		

> Ich will mal meinen Wunsch äußern, wenn ich darf: Ich würd mir Funktionen wünschen der man den Buffer vorgeben kann.
> 
> z.B: daveGetU8(buffer, position)
> 
> ...


In C gibt es das, aber ich habe nur einen Teil davon in die C#-Schnittstelle integriert. Der Grund ist, daß man Funktionen der Dot.NET CLR benutzen sollte, damit die CLR Gültigkeit von Referenzen und Grenzen von Arrays prüfen kann (was in C nicht getan wird).


----------



## seeba (14 November 2005)

Zottel schrieb:
			
		

> seeba schrieb:
> 
> 
> 
> ...



Also ich benutze jetzt den BitConverter von .NET... Funtioniert auch gut soweit. Allerdings bekomm ich mit dem keinen Float aus 4 Bytes gewandet... Hast du eine Funktion der ich die 4 Bytes übergebe und die mir dann einen Float rausschmeißt?


----------



## Zottel (14 November 2005)

seeba schrieb:
			
		

> Also ich benutze jetzt den BitConverter von .NET... Funtioniert auch gut soweit. Allerdings bekomm ich mit dem keinen Float aus 4 Bytes gewandet... Hast du eine Funktion der ich die 4 Bytes übergebe und die mir dann einen Float rausschmeißt?


Wir reden hier irgendwie aneinader vorbei? Wo willst du die 4 Bytes hernehmen? Wenn du in einem Programm in .NET eine float-Variable f hast, kannst du sie mit:
d=libnodave.toPLCFloat(f) 
oder
i=libnodave.daveToPLCFloat(f) 
in eine float oder int-Variable konvertieren.
Die Ergebnis-Variable enthält das Bit-Muster so wie es auch in der SPS stehen würde. Für .NET oder einen i386-Prozessor ist dieses Bitmuster sinnlos.
Es kann aber mittels BitConverter die 32 Bit als eine Folge von 4 bytes an daveWriteBytes als Puffer übergeben werden. Das ist in testMPI.cs beispielhaft interpretiert und bewirkt, daß der Wert, der vorher aus dem MD12 gelesesen, ins PC-Format konvertiert und dann um 1.1  erhöht wurde, wieder so in die SPS ggeschrieben wird, daß in MD12 der erhöhte Wert steht.
Hast du dir das angesehen? Es ausprobiert?

testMPI -w COM1

Weitergehende Operationen mit Puffern sind sinnvoll, wenn mehrere aufeinaderfolgende Werte "in einem Rutsch" in den Merkerbereich oder einen DB geschrieben werden sollen.
Beispiel:
MD 0 INT
MD 4 REAL
MD 8 REAL
MD 12 INT
Sollen auf die Werte 42, 5.5, 6.123, 17 gesetzt werden:
byte[] buffer = new buffer[16];
int i,j=42;
i=libnodave.daveSwapIed_32(j);
Array.Copy(buffer,0,BitConverter(i),GetBytes,0,4);
i=libnodave.daveToPLCFloat(5.5);
Array.Copy(buffer,4,BitConverter(i),GetBytes,0,4);
i=libnodave.daveToPLCFloat(6.123);
Array.Copy(buffer,8,BitConverter(i),GetBytes,0,4);
i=libnodave.daveSwapIed_32(17);
Array.Copy(buffer,12,BitConverter(i),GetBytes,0,4);
dc.WriteBytes(...0,16,buffer);

Das ist kein getesteter Code, aber so oder so ähnlich geht es.


----------



## Bewareofthis (14 November 2005)

hmmm, 
er meint vielleicht sowas :


```
(c#)  (float)BitConverter.ToUInt32(buffer,index))
```

HTH
Bewareofthis


----------



## seeba (14 November 2005)

Zottel schrieb:
			
		

> seeba schrieb:
> 
> 
> 
> ...



Ich will die 4 Bytes aus meinem Buffer zum Float umwandeln. Und du bietest ja keine Funktion zum lesen aus einem bestimmten Buffer an einer bestimmten Stelle.


----------



## Zottel (14 November 2005)

Zottel schrieb:
			
		

> Array.Copy(buffer,12,BitConverter(i),GetBytes,0,4);


usw. muß natürlich richtiger heißen:
Array.Copy(BitConverter(i).GetBytes(),0,buffer,12,4);
entsprechend der Definition:
Array.Copy(src, srcStart, dest, destStart, length);
Bin mit .NET nicht so vertraut.


----------



## Zottel (15 November 2005)

seeba schrieb:
			
		

> Ich will die 4 Bytes aus meinem Buffer zum Float umwandeln. Und du bietest ja keine Funktion zum lesen aus einem bestimmten Buffer an einer bestimmten Stelle.


Das heißt, der Puffer ist mittels einer der Lesefunktionen, z.B. readBytes, mit Bytes aus der SPS gefüllt?
Dann bräuchtest du ja dieselbe Funktion wie daveGetFloat, aber von einem eigenen Puffer?


			
				seeba schrieb:
			
		

> folgende Frag: Gibt es auch eine Funktion um einen Floatwert wieder zurück zu wandel? Ähnlich daveGetFloat? Also sowas wie daveGetBytes(Floatwert)?


Dann habe ich in dieser Frage das Wort "zurück" mißverstanden? Oder sprechen wir inzwischen über eine neue Frage?


----------



## seeba (15 November 2005)

Zottel schrieb:
			
		

> seeba schrieb:
> 
> 
> 
> ...



Genau! Diese Funktionen fehlen mir


----------



## Zottel (15 November 2005)

seeba schrieb:
			
		

> Zottel schrieb:
> 
> 
> 
> ...


Die Funktionen sind in der C-Version (und damit in libnodave.dll) enthalten, z.B.
daveGetFloatfrom(uc * buffer, int position);
Du kannst durchaus versuchen, eine entsprechende Definition in libnodave.net.cs einzufügen.
Aus den vorher genannten Gründen (Prüfung der Array-Grenzen durch die CLR) ist das nicht meine favorierte Lösung.
Die Funktion in nodave.c tauscht die Bytes, falls sie auf einem little endian System (i386) läuft. In .NET gibt es glaube ich, keine bedingte Kompilierung. Eine Möglichkeit wäre, den Byte-Tausch in Abhängigkeit von BitConverter.IsLittleEndian auszuführen. Gefällt mir aber auch nicht, weil überall Fallunterscheidungen und Sprünge nötig sind. Daher suche ich noch nach der optimalen Lösung.


----------



## Zottel (15 November 2005)

Hier die Funktionen unter Verwendung von BitConverter.IsLittleEndian:

```
public static int getS16from(byte[] b, int pos) {
	if (BitConverter.IsLittleEndian) {
	    byte[] b1=new byte[2];
	    b1[1]=b[pos+0];
	    b1[0]=b[pos+1];
	    return BitConverter.ToInt16(b1, 0);
	}    
	else 
	    return BitConverter.ToInt16(b, pos);
    }
    
    public static int getU16from(byte[] b, int pos) {
	if (BitConverter.IsLittleEndian) {
	    byte[] b1=new byte[2];
	    b1[1]=b[pos+0];
	    b1[0]=b[pos+1];
	    return BitConverter.ToUInt16(b1, 0);
	}    
	else 
	    return BitConverter.ToUInt16(b, pos);
    }
        
    public static int getS32from(byte[] b, int pos) {
	if (BitConverter.IsLittleEndian) {
	    byte[] b1=new byte[4];
	    b1[3]=b[pos];
	    b1[2]=b[pos+1];
	    b1[1]=b[pos+2];
	    b1[0]=b[pos+3];
	    return BitConverter.ToInt32(b1, 0);
	}    
	else 
	    return BitConverter.ToInt32(b, pos);
    }
    
    public static uint getU32from(byte[] b, int pos) {
	if (BitConverter.IsLittleEndian) {
	    byte[] b1=new byte[4];
	    b1[3]=b[pos];
	    b1[2]=b[pos+1];
	    b1[1]=b[pos+2];
	    b1[0]=b[pos+3];
	    return BitConverter.ToUInt32(b1, 0);
	}    
	else 
	    return BitConverter.ToUInt32(b, pos);
    }
    
    public static float getFloatfrom(byte[] b, int pos) {
	if (BitConverter.IsLittleEndian) {
	    byte[] b1=new byte[4];
	    b1[3]=b[pos];
	    b1[2]=b[pos+1];
	    b1[1]=b[pos+2];
	    b1[0]=b[pos+3];
	    return BitConverter.ToSingle(b1, 0);
	}    
	else 
	    return BitConverter.ToSingle(b, pos);
    }
```


----------



## seeba (15 November 2005)

Zottel schrieb:
			
		

> Hier die Funktionen unter Verwendung von BitConverter.IsLittleEndian:
> 
> ```
> public static int getS16from(byte[] b, int pos) {
> ...



Klasse, Zottel! Dickes Lob!  Und anderstrum?  :lol: Und: getBitFrom() fehlt mir noch...


----------



## Anonymous (15 November 2005)

Hallo,
Mann seeba,


			
				seeba schrieb:
			
		

> Und: getBitFrom() fehlt mir noch...


Also kaum wirft Zottel euch ein Würstchen hin, willst Du gleich danach noch ein Steak.     
Gruß
Question_mark


----------



## seeba (15 November 2005)

Anonymous schrieb:
			
		

> Hallo,
> Mann seeba,
> 
> 
> ...



Nagut ich bekomm's sicherlich auch so hin, allerdings denke ich, dass es für viele Anwender die mit .NET arbeiten wollen so besser ist!


----------



## Zottel (15 November 2005)

seeba schrieb:
			
		

> Und anderstrum?


Habe ich das nicht voraer ausführlich erläutert?


			
				seeba schrieb:
			
		

> Und: getBitFrom() fehlt mir noch...


Je nachdem, wie du das Bit bekommst ( 1. daveReadBits() oder 2. du hast per daveReadBytes() z.B. ein MB gelesen) vergleichst du auf ungleich 0 oder du vergleichst mit der Bit-Maske: 
if ((MeineVariableDieEineKopieVomMB_XIst & 0x80) !=0) then MB_X_Punkt_7 =true, else MB_X_Punkt_7=false.
Selbstverständlich könnte man getBitFrom() definieren. Auch getU8from. Aber getU8from(buffer, position) ist wirklich trivial: = buffer[position]!!!!
Aber ich möchte auch nicht die Leute verführen, wirklich beschissen ineffizienten Code zu produzieren:
Etwas wie:
if ((MeineVariableDieEineKopieVomMB_XIst & 0x80) !=0) then MB_X_Punkt_7 =true, else MB_X_Punkt_7=false.
setzt ein halbwegs effizienter Compiler für i386 so um:

```
MOV MeineVariableDieEineKopieVomMB_XIst,EAX
AND EAX,00000080h
JZ ok
MOV EAX,TRUE
ok: Nachfolgender Code
```
Ein getBitfrom(buffer, position,BitNumber) sähe, mitsamt Bereichsprüfungen, eher so aus:

```
push buffer
push position
push bitNumber
call getBitfrom
//getBitfrom
enter ParameterGröße
MOV EAX,Buffer.Length
CMP EA, Position
JGT   DoArrayBoundsException
MOV EDX,Buffer
XOR EDX,EDX
CMP EA, Position
JLE  DoArrayBoundsException
ADD EDX,BUFFER
MOV EAX,[EDX]
PUSH EAX
MOV ECX,1
MOV EDX,BitNumber
SHL ECX,EDX
AND EAX,ECX
JZ ok
MOV EAX,TRUE
ok: leave ParameterGröße
ret
```
Ich habe das jetzt nicht ausprobiert und dissassembliert, aber selbst als handoptimierter Assemblercode wäre es so ganz in Ordnung.
In .NET ist es natürlich kein i386-Assembler, sondern jede Anweisung hat entweder eine Entsprechung im Maschinencode der VM oder der JIT-Compiler macht es noch länger...

Wenn du Bits aus einem Byte-Puffer lesen willst, ist es insbesondere günstig, zunächst das byte in eine einfache Variable zu kopieren, weil dadurch die Array-Indizierung und Prüfung der Grenzen einmal gemacht werden und du danach alle 8 Bits testen kannst.
[/code]


----------

