libnodave - Float auf SPS schreiben

seeba

Level-1
Beiträge
1.867
Reaktionspunkte
62
Zuviel Werbung?
-> Hier kostenlos registrieren
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
 
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.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
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. :(
 
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)

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. :(
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).
 
Zottel schrieb:
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)

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. :(
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).

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?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
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.
 
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?
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.

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. :(
 
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?
 
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. :(
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?

Genau! Diese Funktionen fehlen mir :(
 
Zuviel Werbung?
-> Hier kostenlos registrieren
seeba schrieb:
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. :(
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?

Genau! Diese Funktionen fehlen mir :(
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.
 
Hier die Funktionen unter Verwendung von BitConverter.IsLittleEndian:
Code:
    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);
    }
 
Zottel schrieb:
Hier die Funktionen unter Verwendung von BitConverter.IsLittleEndian:
Code:
    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);
    }

Klasse, Zottel! Dickes Lob! :) Und anderstrum? :lol: Und: getBitFrom() fehlt mir noch...
 
seeba schrieb:
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:
Code:
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:
Code:
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]
 
Zurück
Oben