# 2 Byte Arrays addieren



## Snooky (22. Jul 2009)

Hallo Javafreunde,

ich möchte gerne 2 Byte Arrays, die einen Datentyp repräsentieren, addieren, ohne dazu den Umweg über den repräsentierten Datentyp gehen zu müssen.

Bei Integer funktioniert es, einfach beide 4 Felder große Arrays zu summieren.
Bei Short und Long aber leider nicht, da kommen vollkommen unterschiedliche Werte raus, wenn man einfach beide Arrays addiert.

Gibt es da vielleicht vorhandene Methoden?
Über die Größe des Arrays kann ich natürlich rausfinden, welche Zahl darin lagert (bei 1 ists Byte, bei 2 Short, bei 4 Integer und bei 8 Long). Ich denke das ist auch ein Punkt, wo man ansetzen kann, indem man eine Methode schreibt, die bei einer Länge von 4 einfach aufsummiert und bei allen anderen den Umweg über die Umwandlung beider Arrays, das Addieren in einer Variablen und das zurückwandeln geht.
Ich hätte aber lieber eine vorgefertigte Methode.
Wenn es nichts gibt, schreib ichs aber selber 

Danke schonmal


----------



## Marco13 (22. Jul 2009)

Ich bezweifle, dass das für Integer funktioniert, wenn in den Bytes "große" Zahlen stehen (d.h. 128 und größer - das Vorzeichen mal ignorierend). Wie auch bei short und long muss der Überlauf beachtet werden. DANN kann die Methode aber für alle Typen gleich aussehen. Was vorgefertigtes gibt's da wohl nicht.


----------



## Snooky (22. Jul 2009)

Das versteh ich nun nicht. Ich dachte gerade dann müsste die Methode für die Dinger unterschiedlich aussehen!
Ich hab mir eine Klasse geschrieben, die mir ein mitgegebenes Array in einen bestimmten Datentyp umwandelt. Dann habe ich ferner Methoden, um aus einem beliebigen byte Array der Längen 2, 4 und 8 (short, int, long) die zahl rauszuholen.

Die Methode, die mir nun 2 bytearrays (gleiche länge, d.h. gleicher datentyp, wird vorausgesetzt und kann aufgrund der herkunft der daten auch nicht anders sein), sieht bisher so aus:


```
public byte[] add (byte [] data1, byte [] data2)
	{
		byte data[];
	
		if(data1.length == 4)
		{
			data = new byte[4];
			for(int i=0; i<4; i++)
			{
				data[i] =(byte)(data1[i] + data2[i]);
			}
		}
		
		if(data1.length == 1)
		{
			data = new byte[1];
			data[1] = (byte)(data1[1]+data2[1]);
		}
		
		if(data1.length == 2)
		{
			data = new byte[2];
			short v1 = this.getShort(data1);
			short v2 = this.getShort(data1);
			try
			{
			data = this.getByte(((short)(v1+v2)));
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
		}
		
		if(data1.length == 8)
		{
			data = new byte[8];
			long v1 = this.getLong(data1);
			long v2 = this.getLong(data1);
			try
			{
			data = this.getByte(((long)(v1+v2)));
			}
			catch(IOException e)
			{
				e.printStackTrace();
			}
			
		}
		else
		{
			data = new byte[1];
		}
		
		return data;
		
	
	}
```

Die Reihenfolge der Abfragen basiert auf der Erfahrung der Häufigkeit. Die Methode wird wohl meistens mit einem 4 Byte großen Array aufgerufen werden.


----------



## Marco13 (22. Jul 2009)

Ein KSKB wäre hilfreich. Mal schauen ob mir demächst so langweilig ist, dass ich das bastle.


----------



## Ark (22. Jul 2009)

Kann es sein, dass du den Übertrag vergisst?

Ark


----------



## Snooky (22. Jul 2009)

Das Byte Array <-> anderer Datentyp ist in der Klasse auch geregelt:

```
public class byteFacility {
	
	private ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
	private DataOutputStream datastream = new DataOutputStream(bytestream);
	private ByteBuffer buf = ByteBuffer.allocate(0);
	
	public byte[] getByte(short value) throws IOException
	{		
			bytestream.reset();
			datastream.writeShort(value);
			datastream.flush();
			byte[] bytes = bytestream.toByteArray();
			return bytes;			
	}
	
	
    public byte[] getByte(int value) throws IOException
	{		
    		bytestream.reset();
    		datastream.writeInt(value);
    		datastream.flush();
    		buf.order(ByteOrder.LITTLE_ENDIAN);
			byte[] bytes = bytestream.toByteArray();		
			return bytes;
	}

	
	public byte[] getByte(long value) throws IOException
	{		
			bytestream.reset();
			datastream.writeLong(value);
			datastream.flush();
			byte[] bytes = bytestream.toByteArray();		
			return bytes;
	}

	
	public byte[] getByte(float value) throws IOException
	{		
			bytestream.reset();
			datastream.writeFloat(value);
			datastream.flush();
			byte[] bytes = bytestream.toByteArray();		
			return bytes;
	}
	
	public byte[] getByte(double value) throws IOException
	{		
			bytestream.reset();
			datastream.writeDouble(value);
			datastream.flush();
			byte[] bytes = bytestream.toByteArray();		
			return bytes;
	}
	
	public short getShort(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(8);
		buf.put(data);
		return buf.getShort(0);
			
	}
	
	public int getInt(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(4);
		buf.put(data);
		return buf.getInt(0);
	}
	
	public long getLong(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(8);
		buf.put(data);
		return buf.getLong(0);
	}

	public float getFloat(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(4);
		buf.put(data);
		return buf.getFloat(0);
	}

	public double getDouble(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(8);
		buf.put(data);
		return buf.getDouble(0);
	}
```
Von Übertrag hab ich ehrlich gesagt noch nichts gehört, weil ich das alles über die beiden Streams und den ByteBuffer mache. Ist das von Hand eleganter/schneller? Wenn ja, wo kann ichs lernen?


----------



## Ark (22. Jul 2009)

Snooky hat gesagt.:


> Von Übertrag hab ich ehrlich gesagt noch nichts gehört, weil ich das alles über die beiden Streams und den ByteBuffer mache. Ist das von Hand eleganter/schneller? Wenn ja, wo kann ichs lernen?


Dass bei Addition ein Übertrag auftreten kann, lernt man irgendwann in der Zeit der ersten bis dritten Klasse, glaube ich.

Wenn ich den Code mit der Intention richtig zusammenbringe, willst du zwei Zahlen beliebiger Größe addieren."Beliebig" heißt hier so viel wie: Die Zahlen liegen in mehreren Häppchen als bytes vor und eben nicht als Ganzes. Beispielsweise ist ja ein int vier Bytes groß, also haben wir die Zahlen in zwei Arrays byte[4]. aber man kann nicht ja nicht byte[]+byte[] rechnen, wie man int+int rechnen könnte, deswegen soll das jetzt nachgebaut werden.

So etwa?

Wenn ja: Warum um alles in der Welt diese Unterscheidung? Konvertiere einfach einmal alles in long, und gut ist.

Ark


----------



## Marco13 (22. Jul 2009)

Zum Übertrag:

data_ =(byte)(data1 + data2);

Wenn data1 hier FF ist, und data2 ist 1, dann kommt da 0 raus, und die nächsthöhere stelle müßte um 1 erhöht werden.



Zur Konvertier-Klasse... :autsch: Wir wissen ja: Java ist langsam 

Wie auch immer. Das eigentliche addieren sollte immer gleich gehen können, wie hier ganz unten in der add-Methode


		Java:In die Zwischenablage kopieren


class ByteAdder
{
    public static void main(String args[])
    {
        testInt(0x8080ABCD, 0x8080ABCD);
        testInt(0x8080FFFF, 0x80000001);
        testInt(0xFFFFFFFF, 0x00000001);

        testLong(0x8080ABCD8080ABCDL, 0x8080ABCD8080ABCDL);
        testLong(0x8080FFFFFFFFFFFFL, 0x8000000000000001L);
        testLong(0xFFFFFFFFFFFFFFFFL, 0x0000000000000001L);

    }

    private static boolean testInt(int i0, int i1)
    {
        byte b0[] = new byte[4];
        byte b1[] = new byte[4];
        byte result[] = new byte[4];
        putInt(i0, b0, 0);
        putInt(i1, b1, 0);
        add(b0, b1, result);
        int r = getInt(result, 0);

        boolean passed = (r==(i0+i1));
        System.out.println("   "+hex(i0));
        System.out.println(" + "+hex(i1));
        System.out.println(" = "+hex(r)+" "+passed);
        System.out.println("");
        return passed;
    }

    private static String hex(int i)
    {
        String s = Integer.toHexString(i);
        while (s.length() < 8)
        {
            s = "0"+s;
        }
        return "0x"+s;
    }

    private static boolean testLong(long i0, long i1)
    {
        byte b0[] = new byte[8];
        byte b1[] = new byte[8];
        byte result[] = new byte[8];
        putLong(i0, b0, 0);
        putLong(i1, b1, 0);
        add(b0, b1, result);
        long r = getLong(result, 0);

        boolean passed = (r==(i0+i1));
        System.out.println("   "+hex(i0));
        System.out.println(" + "+hex(i1));
        System.out.println(" = "+hex(r)+" "+passed);
        System.out.println("");
        return passed;
    }

    private static String hex(long i)
    {
        String s = Long.toHexString(i);
        while (s.length() < 16)
        {
            s = "0"+s;
        }
        return "0x"+s;
    }





    private static void putInt(int value, byte array[], int offset)
    {
        array[offset+0] = (byte)((value >> 24) & 0xFF);
        array[offset+1] = (byte)((value >> 16) & 0xFF);
        array[offset+2] = (byte)((value >>  8) & 0xFF);
        array[offset+3] = (byte)((value >>  0) & 0xFF);
    }

    private static int getInt(byte array[], int offset)
    {
        int result = 0;
        result |= (unsignedInt(array[offset+0]) << 24);
        result |= (unsignedInt(array[offset+1]) << 16);
        result |= (unsignedInt(array[offset+2]) <<  8);
        result |= (unsignedInt(array[offset+3]) <<  0);
        return result;
    }

    private static void putLong(long value, byte array[], int offset)
    {
        array[offset+0] = (byte)((value >> 56L) & 0xFF);
        array[offset+1] = (byte)((value >> 48L) & 0xFF);
        array[offset+2] = (byte)((value >> 40L) & 0xFF);
        array[offset+3] = (byte)((value >> 32L) & 0xFF);
        array[offset+4] = (byte)((value >> 24L) & 0xFF);
        array[offset+5] = (byte)((value >> 16L) & 0xFF);
        array[offset+6] = (byte)((value >>  8L) & 0xFF);
        array[offset+7] = (byte)((value >>  0L) & 0xFF);
    }

    private static long getLong(byte array[], int offset)
    {
        long result = 0;
        result |= (unsignedLong(array[offset+0]) << 56L);
        result |= (unsignedLong(array[offset+1]) << 48L);
        result |= (unsignedLong(array[offset+2]) << 40L);
        result |= (unsignedLong(array[offset+3]) << 32L);
        result |= (unsignedLong(array[offset+4]) << 24L);
        result |= (unsignedLong(array[offset+5]) << 16L);
        result |= (unsignedLong(array[offset+6]) <<  8L);
        result |= (unsignedLong(array[offset+7]) <<  0L);
        return result;
    }



    private static int unsignedInt(byte b)
    {
        if (b<0)
        {
            return b+256;
        }
        return b;
    }
    private static long unsignedLong(byte b)
    {
        if (b<0)
        {
            return b+256;
        }
        return b;
    }

    private static void add(byte b0[], byte b1[], byte result[])
    {
        int carry = 0;
        for (int i=b0.length-1; i>=0; i--)
        {
            int sum = unsignedInt(b0[i])+unsignedInt(b1[i])+carry;
            carry = sum >> 8;
            result[i] = (byte)sum;
        }
    }
}


So viel zum Thema "langweilig"._


----------



## Snooky (22. Jul 2009)

Wow, das muss ich mir morgen in Ruhe angucken  Aber Danke schonmal!

Zur Unterscheidung:
Ich kann nicht einfach alles in long rechnen. Wenn ich einen Integerwert habe, darf der auch nur in einem 4 Byte großen Array sein, nicht in einem Long Array.
Das liegt daran, dass ich Offsets von FSUIPC, einem Plug-In zum Flugsimulator von MS beschreiben will. Die Java Version davon braucht zum schreiben den Offset, die Byteanzahl (wird vom Offset "vorgegeben" bzw. liegt in einer Hashtable) und eben das byte-Array.
Wenn ich also in ein 2 byte großes offset schreibe, darf ich dem als Array auch nur ein 2 Felder großes byte-Array mitgeben.
Das heißt, einfach alle Zahlen als long zu behandeln würde hier leider nicht gehen.

Da wir aber gerade beim Thema bytes sind. Hat Java etwas vorgefertigtes, um einzelne Bits in einem Byte zu manipulieren? Einige der Offsets sind bitorientiert, z.B. ist 2^0 eine Funktion, 2^1 eine Funktion usw. Das Ganze dann eben bis 2^7.

Edit:
Hat vielleicht noch jemand einen Link, wo diese << >> usw. Operatoren erklärt werden? Das hat ja scheinbar was mit Bytes oder Bits in Bytes zu tun. Ich hab auch schon was von Mask gelesen, wo man einem Byte (irgendwie) z.B. über eine 15 die Bits 2^0, 2^1, 2^2 und 2^3 ändert.


----------



## ice-breaker (22. Jul 2009)

:arrow: Bitweise Operatoren

irgendwie glaube ich, wenn du das alles verstanden hast, wirst du auch Teile deines Postings revidieren


----------



## 0x7F800000 (22. Jul 2009)

Marco13 hat gesagt.:


> So viel zum Thema "langweilig".


:bahnhof:
http://java.sun.com/javase/6/docs/api/java/math/BigInteger.html#BigInteger%28byte[]%29


----------



## Marco13 (23. Jul 2009)

Ich bin... naja ... "davon ausgegangen", dass das ganze möglichst performant sein sollte. (Dazu bestand eigentlich kein Anlass, aber ... aus Prinzip, eben  )

Und wenn man z.B. zwei 100000*4 byte große "int"-Arrays hat, und die addieren will, ist sowas wie

```
for (int i=0; i<400000; i+=4)
{
    // Ein paar zusätzliche Parameter für 
    // die offsets und die Größe...
    add(input0, i, input1, i, result, i, 4);
}
```
sicher schneller als das alles erst in BigIntegers umzuwandeln.


----------



## 0x7F800000 (23. Jul 2009)

Marco13 hat gesagt.:


> ... aus Prinzip, eben


bzw. sportliches Interesse ist natürlich schon grund genug  , aber


> sicher schneller als das alles erst in BigIntegers umzuwandeln.


...schleifen sind nicht besonders wiederverwendbar
...fehleranfällig, irgendwo baut man sicherlich früher oder später Fehler ein
...zeitaufwendig, bei der division wird's stressig


----------



## Marco13 (23. Jul 2009)

Ja, sportliches Interesse trifft es vielleicht sogar noch eher. Es kann nämlich gut sein (und ist sogar recht wahrscheindlich) dass bei größeren Datenmengen (wie 100000 ints) die _am Stück_ verarbeitet werden sollen eine Lösung wie 
Sende byte[]s in ObjectOutputStreams
Lies int[]s aus ObjectOutputStreams
Addiere int[]s
Schreibe int[] in ObjectOutputStream
deutlich effizienter wäre.


----------



## Snooky (23. Jul 2009)

Also prinzipiell ist von der späteren Funktionsweise des Programms davon auszugehen, dass in den meisten Fällen genau eine Addierfunktion öfter wiederholt wird, nämlich genau so lange, wie der Benutzer die zugehörige Taste gedrückt hält. Vielleicht werden es ab und an zwei oder drei parallel sein, aber nicht mehr.
Die Daten werden auch immer nur aus 2 Integern bestehen. Einer, wo der aktuelle Wert drin ist, und einer, der addiert wird. Bei Wiederholungen wird der zu addierende Wert einfach nochmal auf den aktuellen gepackt.

Die Bitweisen Operatoren werd ich mir mal zu Gemüte führen, danke für den Link


----------



## Ark (23. Jul 2009)

@Snooky: Es gibt so Dinge beim Programmieren, die nennen sich Schnittstellen (engl. Interfaces) und Kapselung. Ziel dieser Überlegungen ist es, die verschiedenen internen Repräsentationen, Datenstrukturen usw. der zusammenarbeitenden Systeme voneinander abzuschotten.

Schönes Beispiel: Die USB-Schnittstelle. Man hat sich darauf geeinigt, wie Stecker und Buchse auszusehen haben und wie sich das Gerät dem Computer mitteilt (das so genannte _Protokoll_). Beide Systeme wissen nichts von der internen Verarbeitung dieser Signale. Der PC weiß nicht, wie das Gamepad die Daten handhabt, und das Gamepad weiß nicht, was der PC aus den gesendeten Daten macht. Beide Systeme haben ihre eigene Art der Verarbeitung, gerade so, wie es für sie am einfachsten ist. (So sollte es zumindest sein.) Nur für die Übertragung per USB dolmetschen sie gegenseitig und beugen sich der Sprache des jeweiligen Gegenübers.

Wenn du also etwas programmierst, wo mehrere Systeme kooperieren müssen (und das ist schon bei reinen Java-Programmen meistens der Fall), dann einigt euch auf eine gemeinsame Sprache, die ihr lediglich zum Austausch verwendet. In deinem Fall ist wohl die Sprache des Simulators vorgegeben, also beugst du dich der Einfachheit halber an nur einer Stelle, nämlich der Schnitt-Stelle (und eben _nur_ an dieser Stelle!) der Sprache des Simulators. Das kann z.B. eine Klasse sein, die nur dafür bestimmt ist, die eine Repräsentation in die jeweils andere zu überführen. Im Rest des Java-Programms arbeitest du dann nur noch mit deiner Repräsentation, deinen Datenstrukturen usw.

Wie geeignete Strukturen aussehen könnten, hängt im Wesentlichen von den Aufgaben ab, die das Java-Programm erfüllen soll. Wenn sie nicht geeignet sind (wie sie momentan zu sein scheinen), werden selbst einfache Aufgaben unnötig kompliziert, wie du siehst. Ein weiterer wichtiger Punkt ist _natürlich auch_ das Protokoll an der Schnittstelle, damit die Konvertierung nicht zu lange dauert. Aber zu sehr sollte man sich nicht in diese Richtung reinsteigern, denn dort fangen schneller als gedacht die Optimierungen an, die möglicherweise nie nötig werden. 

Ark


----------



## Snooky (23. Jul 2009)

Was du damit sagen willst ist also, ich sollte intern (z.B. für meine Actions), so rechnen, wie es für mich am einfachsten ist. Das wären dann natürlich Integer und nicht dieses Bytegefummel. Und erst wenn die Daten tatsächlich zum Flugsimulator gehen, konvertiere ich sie in das passende Format, während alle internen Sachen in "meiner" Sprache passieren. Soweit richtig?

Vom Flugsimulator (bzw. dem Plug-In FSUIPC) habe ich eine Java DLL bekommen und ein Programm, das so aussieht:

```
public class fsuipc_wrapper
	{ 
	static
		{ 
		System.loadLibrary("fsuipc_java" ); 
		} 
	public static synchronized native int Open();
	public static synchronized native void Close();
	
    public static synchronized native void ReadData(int aOffset,int aCount,byte[] aData);	
	public static synchronized native void WriteData(int aOffset,int aCount,byte[] aData);	
	public static synchronized native void Process();
	
    }
```

Ein interface, das auch als solches deklariert ist, finde ich nirgends.
Ich gehe also davon aus, dass ich mich da nochmal schlau lesen muss und selbst eins schreibe.
Für den Programmablauf in meinem Programm, also der Repträsantation von 64 Buttons pro IO-Warrior (für den ich schon eine super Schnittstelle habe), wäre es natürlich am einfachsten, ich könnte einfach mit Integern rechnen wie es mir beliebt und diese erst in byte-Arrays wandeln, wenn wirklich Daten an FSUIPC geschickt werden.

Hast du vielleicht nochmal einen Link, vielleicht auch etwas, womit ich in die DLL reingucken kann? Ich muss ehrlicherweise sagen, dass ich noch ziemlich neu auf dem Gebiet der Programmierung bin. Bis vor ein paar Wochen wusste ich nichtmal, was AND, OR usw. ist.
Bis dahin schonmal vielen Dank für deine Hilfe! 
Ich hoffe, ich hab mich mit dem Projekt nicht übernommen.


----------



## Ark (23. Jul 2009)

Na, bitte, damit hast du doch schon den "Stecker" (nehmen wir mal an, dein Simulator wäre so etwas wie ein Gamepad  ), jetzt brauchst du nur noch die entsprechende "Buchse" zu schreiben. Ich habe das mal beispielhaft gemacht:

```
public class FsuipcInterface{

	private static byte[] buf = new byte[8];// angenommen, wir brauchen nie mehr als long

	public static void writeInt(int value){
		buf[0] = (byte)(value);
		buf[1] = (byte)(value >> 8);
		buf[2] = (byte)(value >> 16);
		buf[3] = (byte)(value >> 24);
		fsuipc_wrapper.WriteData(0, 4, buf);
	}
	
	public static int readInt(){
		fsuipc_wrapper.ReadData(0, 4, buf);
		return
			(buf[0] & 0xFF) |
			(buf[1] & 0xFF) << 8 |
			(buf[2] & 0xFF) << 16 |
			(buf[3] & 0xFF) << 24;
	}
}
```
Das ist aber nur geraten. Ich kann nicht garantieren, dass das in deinem Fall funktioniert. Was du noch brauchst bzw. haben solltest, ist die _Spezifikation der Schnittstelle_ (also Dinge wie "Wie sind die Parameter zu verstehen? Was machen die Methoden?") und die _Spezifikation des Protokolls_ (also Dinge wie "Wie werden Daten ausgetauscht? Wie müssen sie aufgebaut sein? In welcher Reihenfolge wird was erwartet?").

Dieser Codeschnipsel basiert auf Vermutungen, die ich aus ein paar Dingen, die du schriebst, rauszulesen versucht habe. Wenn so weit aber alles passen sollte, brauchst du nur noch [c]FsuipcInterface.writeInt(42);[/c] zu machen, und schon hast du 42 an den "Stecker" weitergeleitet. 

Ark

*EDIT:* Fehler in [c]writeInt()[/c] korrigiert, siehe Snookys folgenden Beitrag.


----------



## Snooky (23. Jul 2009)

Hallo Ark,

sowas werde ich mir schreiben, das ist wirklich viel besser als das Rumgehampel in allen Teilen meines Programms 

Jetzt habe ich aber noch 2 Fragen:

Frage 1: Ich habe dein Programm mal übernommen, um zu verstehen, was es mit diesem Bitgeschubse auf sich hat. Eclipse hat bei der writeInt Methode gemeckert, dass er nicht von Int zu Byte konvertieren kann und mir einen Bytecast vorgeschlagen. Damit führt er das Programm nun aus und mit der readInt Methode bekomme ich den Integer auch zurück. Ist das mit dem Bytecast richtig so?

Frage 2: Wenn ich das mit der writeInt Methode gefüllte byte-Array mit folgendem Code zurückwandle, bekomme ich eine falsche Zahl ausgespuckt. Mein Int lautete 89654 und bekommen habe ich 912130304.

```
public int getInt(byte[] data)
	{
		buf.clear();
		buf = ByteBuffer.allocate(4);
		buf.put(data);
		return buf.getInt(0);
	}
```
Woran liegt das? Sonst hat das mit obigem Code immer geklappt. Sollte 89654 in einem byteArray nicht immer gleich aussehen?
Der Code, mit dem ich die Zahl sonst in ein byte-Array umgewandelt habe, sieht übrigens so aus:

```
private ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
	private DataOutputStream datastream = new DataOutputStream(bytestream);
	private ByteBuffer buf = ByteBuffer.allocate(0);

    public byte[] getByte(int value) throws IOException
	{		
    		bytestream.reset();
    		datastream.writeInt(value);
    		datastream.flush();
    		buf.order(ByteOrder.LITTLE_ENDIAN);
			byte[] bytes = bytestream.toByteArray();		
			return bytes;
	}
```
Danke für die Antwort


----------



## Ark (23. Jul 2009)

Snooky hat gesagt.:


> Frage 1: Ich habe dein Programm mal übernommen, um zu verstehen, was es mit diesem Bitgeschubse auf sich hat. Eclipse hat bei der writeInt Methode gemeckert, dass er nicht von Int zu Byte konvertieren kann und mir einen Bytecast vorgeschlagen. Damit führt er das Programm nun aus und mit der readInt Methode bekomme ich den Integer auch zurück. Ist das mit dem Bytecast richtig so?


Ja, das war mein Fehler, ich habe ihn inzwischen oben korrigiert. Ich schreibe häufig nur so ohne Prüfungen, deshalb passiert so was schon mal. 



Snooky hat gesagt.:


> Frage 2: Wenn ich das mit der writeInt Methode gefüllte byte-Array mit folgendem Code zurückwandle, bekomme ich eine falsche Zahl ausgespuckt. Mein Int lautete 89654 und bekommen habe ich 912130304.
> [...]
> Woran liegt das? Sonst hat das mit obigem Code immer geklappt. Sollte 89654 in einem byteArray nicht immer gleich aussehen?


Nein, das muss es nicht. Die Problematik dahinter hast du im Wortlaut schon in deinem Code verwendet: Little Endian vs. Big Endian, Stichwort Bytereihenfolge. Dass ein int aus 4 Bytes besteht, sagt ja nichts darüber aus, in welcher Reihenfolge diese Bytes notiert werden. 

Dazu müsstest du jetzt in die Protokollspezifikation schauen. Da müsste genau drinstehen, in welcher Reihenfolge die Bytes sortiert sind. In deinem bisherigen Code, den du gezeigt hast, verwendest du Little Endian, weshalb ich das mal nach Gutdünken und besten Wissen und Gewissen so umgesetzt habe. Dass es anders kommt, ist wohl offensichtlich. Allerdings sagt das immer noch nicht, dass deine oder meine Bytereihenfolge die richtige wäre. Das musst du alles, wie gesagt, der Spezifikation entnehmen. Da musst du nachschlagen, das kann hier sonst niemand beantworten.

Wenn du komplexere Strukturen zu übertragen hast als nur ints, longs etc., solltest du Bescheid geben. Dann kann ich eventuell ein Beispiel liefern, wie man so was machen kann, und dann werden wahrscheinlich auch interfaces, wie sie die Sprache Java kennt, zum Einsatz kommen. 

Ark


----------



## Snooky (23. Jul 2009)

Und nochmal Danke 

Leider steht nichts dabei, darum werde ich mal in dem Forum von FSUIPC fragen. Die dll und die Java-Beispiele kamen alle von einer SDK von FSUIPC. Ich denke, da wird man mir sagen können, in welcher Reihenfolge der fsuipc_wrapper die bytes haben möchte.

Ich hab das ganze gerade mal bei Wikipedia nachgeschlagen:
Little Endian bedeutet demnach, dass dasjenige Byte mit dem höchsten Wert (also an Position 0 des Arrays) zuerst gespeichert wird und Big Endian bedeutet, dass das Byte dem niedrigsten Wert zuerst gespeichert wird?

Was meinst du mit komplexere Strukturen? Also außer, dass vielleicht mal ein String gelesen werden soll (das kommt aber erst sehr viel Später), dürfte es nicht wirklich komplizierter werden 

Edit:

Ich hab mir gerade mal beide Arrays stellenweise ausgeben lassen:

54    0
94    1
1    94
0    54

Links das von deiner Methode erzeugte, rechts das von meiner Methode erzeugte.
Was ist das denn nun für ne Byte Reihenfolge? Müsste nicht die 94 oben bzw. unten stehen?!


----------



## Marco13 (23. Jul 2009)

Bei  896*54* passt das mit der 54 doch?!


----------



## Snooky (23. Jul 2009)

Ich glaub ich hab das mit dem Endian falsch verstanden. Das heißt, jetzt habe ich es gar nicht mehr verstanden.
Hier Byte-Reihenfolge ? Wikipedia wird eine viel größere Zahl umgewandelt, da steht das letzte Byte ja auch nicht nur für die letzten 2 Ziffern. Ich blick da glaub ich nicht ganz durch 
Alles, was ich bisher verstanden habe, ist dass Little Endian die Umkehrung von Big Endian ist.


----------



## Ark (23. Jul 2009)

Snooky hat gesagt.:


> Leider steht nichts dabei, darum werde ich mal in dem Forum von FSUIPC fragen. Die dll und die Java-Beispiele kamen alle von einer SDK von FSUIPC. Ich denke, da wird man mir sagen können, in welcher Reihenfolge der fsuipc_wrapper die bytes haben möchte.


Das ist eine sehr gute Idee. Eine andere gute Idee wäre es, das eine oder andere Beispiel zu zeigen, wo die gleichen Schnittstellen (also gleich aufgebaute Methoden, Parameter usw.) benutzt werden, wie wir sie hier haben. Vielleicht kann man daraus schon das eine oder andere lesen.



Snooky hat gesagt.:


> Ich hab das ganze gerade mal bei Wikipedia nachgeschlagen:
> Little Endian bedeutet demnach, dass dasjenige Byte mit dem höchsten Wert (also an Position 0 des Arrays) zuerst gespeichert wird und Big Endian bedeutet, dass das Byte dem niedrigsten Wert zuerst gespeichert wird?


Ich glaube, du hast es komplett gar nicht verstanden. ^^

Nehmen wir mal die Zahl 123. Interessant sind für uns nur die Reihenfolge der Zeichen '1', '2' und '3', nicht die Kodierung oder die Tatsache, dass es eine Dezimalzahl ist, usw. Uns interessiert allein die Reihenfolge der Ziffern.

Wenn ich diese Zahl in eine Datei schreibe, damit sie für einem Menschen gut lesbar ist (so wie "123" in einer Textdatei), dann schreibe ich die Ziffern eben in dieser Reihenfolge in die Datei: "123" Diese Reihenfolge ist Big Endian, weil die Ziffer an der Stelle mit dem höchsten Wert (hier die 1) _zuerst_ kommt, und dann die Wertigkeit immer weiter abnimmt.

Diese Darstellung ist schön einfach für Menschen, für Maschinen und Berechnungen ist sie der blanke Wahnsinn. Stell dir vor, du möchtest die Zahl 941 direkt darauf addieren. Dann müsstest du dazu bis zum Ende der Datei gehen, die "3" einlesen, die 1 dazuaddieren, macht 4, die ehemalige "3" durch die neue "4" ersetzen, dann wieder einen Schritt zurück(!) in der Datei gehen, die "2" einlesen, die 4 aus der 941 draufaddieren, die 6 dann da hinschreiben usw. Du müsstest dich also von hinten nach vorn hangeln, was in höchstem Maße unnatürlich aussieht. Und das ganz dicke Ding ist dann der Übertrag von der dritten in die vierte Stelle: durch die Addition von 123 und 941 kommst du auf 1064, und das braucht eine Ziffer mehr! Jetzt müsstest du wieder ganz ans Ende der Datei und Ziffer für Ziffer nach hinten schieben, um vorn Platz für die "1" in "1064" zu machen. Das ist alles einfach nur Mist, das Durchwandern der Datei an sich ist schon voll kompliziert, und dann macht so ein Übertrag alles extrem langsam.

Für Berechnungen ist diese Darstellung einfach nur total ungeeignet.

Deswegen nun der Trick: Ich drehe die Reihenfolge der Ziffern einfach um. Aus "123" mache ich "321" und aus "941" mache ich jetzt "149". Wie du siehst, kommt die Ziffer an der Stelle mit der kleinsten Wertigkeit als erstes, deswegen eben Little Endian.

Wenn ich jetzt die beiden Zahlen addieren möchte, kann ich direkt am Anfang der Datei auch mit der Addition beginnen, und wenn ich merke, ich brauche nach "460" (ein Stück "1064" rückwärts) noch eine weiter Stelle mit einer "1", dann hänge ich sie einfach ans Ende der Datei an, und schon bin ich fertig: "4601". Es gibt auch noch viele andere Zusammenhänge mehr (z.B. das Stellenwertsystem, damit der Logarithmus, Division, Modulo etc.), weshalb man sagen kann, dass Little Endian für Berechnungen in 99% aller Fälle besser geeignet ist als Big Endian.



Snooky hat gesagt.:


> Was meinst du mit komplexere Strukturen? Also außer, dass vielleicht mal ein String gelesen werden soll (das kommt aber erst sehr viel Später), dürfte es nicht wirklich komplizierter werden


"Kompliziert" hat nichts mit "komplex" zu tun. Kompliziert heißt umständlich, komplex heißt zusammengesetzt. (Beispiel: Komplexe Zahlen sind zusammengesetzte Zahlen, eben zusammengesetzt aus einem Realteil und einem Imaginärteil.)

Komplexe Strukturen sind alle, die aus mehr als nur einer einzelnen "primitiven" Struktur bestehen. "Komplex" und "primitiv" sind hier gemünzt auf die Datentypen von Java. int, byte, short, char, float etc. sind hier Primitive. Alle anderen, also alle komplexen Typen heißen Objekte. Ein Objekt kann aus mehreren Primitiven und Objekten zusammengesetzt sein, deswegen sind sie die komplexen Typen.

Ein Beispiel gebe ich dir vielleicht im nächsten Beitrag. 

Ark


----------



## Snooky (23. Jul 2009)

Ich hab mal ein paar Codezeilen aus dem Beispiel Javateil gefunden. Das geht alles in eine andere Richtung als die, die ich möchte. Ich möchte nicht die einzelnen Komponenten des Flugzeugs abbilden, sondern das Programm so flexibel wie möglich im Lesen und Schreiben der Offsets halten.
Aber vielleicht kann man daraus lesen, welche Order hier benutzt wird:

```
public short StandByFreq()
	{
	return getShort(iStandbyFreq);
	}

	public String StandByFreqAsString()
	{
		int freq = StandByFreq();
		int dig1 = (freq>>12 & 0x0f);
		int	dig2 = (freq>>8 & 0x0f);
		int dig3 = (freq>>4 & 0x0f);
		int dig4 = (freq& 0x0f);
		String ret = new String("1"+new Integer(dig1).toString()+new Integer(dig2).toString()+"."+new Integer(dig3).toString()+new Integer(dig4).toString());
		return ret;
	}
```

Der Leseteil vom Short findet sich hier:


```
public short getShort(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(2);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[2];
		fsuipc_wrapper.ReadData(aOffset,2,data);
		buf.put(data,0,2);
		return buf.getShort(0);
		}
```

Ich bin auch von der Programmiermethode nicht so ganz überzeugt und habe das anders gelernt. Zum Beispiel würde ich einen Integer nicht unbedingt in einen String umwandeln.

```
String ret = "1"+dig1+dig2+"."+dig3+dig4;
```
Das bewirkt ja genau das gleiche.
Und das Aufrufen der Methode StandByFreq() überzeugt irgendwie auch nicht, das könnte man doch komplett in StandByFreqAsString() regeln.

Ich glaube das Interface, was in dem Programm benutzt wird, ist die Klasse FSUIPC.
Die sieht so aus:

```
public class FSUIPC {
public byte getByte(int aOffset)
		{
		byte[] data = new byte[1];
		fsuipc_wrapper.ReadData(aOffset,1,data);
		return data[0];
		}
    
	public short getShort(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(2);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[2];
		fsuipc_wrapper.ReadData(aOffset,2,data);
		buf.put(data,0,2);
		return buf.getShort(0);
		}
	
	public int getInt(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(4);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[4];
		fsuipc_wrapper.ReadData(aOffset,4,data);
		buf.put(data,0,4);
		return buf.getInt(0);
		}
	
	public long getLong(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(8);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[8];
		fsuipc_wrapper.ReadData(aOffset,8,data);
		buf.put(data,0,8);
		return buf.getLong(0);
		}
    
	public float getFloat(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(4);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[4];
		fsuipc_wrapper.ReadData(aOffset,4,data);
		buf.put(data,0,4);
		return buf.getFloat(0);
		}

	public double getDouble(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(8);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[8];
	
		fsuipc_wrapper.ReadData(aOffset,8,data);
		buf.put(data,0,8);
		/*
		System.out.println("0 " + Integer.toHexString(iData[0]));
		System.out.println("1 " + Integer.toHexString(iData[1]));
		System.out.println("2 " + Integer.toHexString(iData[2]));
		System.out.println("3 " + Integer.toHexString(iData[3]));
		System.out.println("4 " + Integer.toHexString(iData[4]));
		System.out.println("5 " + Integer.toHexString(iData[5]));
		System.out.println("6 " + Integer.toHexString(iData[6]));
		System.out.println("7 " + Integer.toHexString(iData[7]));
		*/
		return buf.getDouble(0);
		
		}
    
    public String getString(int aOffset,int aLength)
		{
		byte[] data = new byte[aLength];
		fsuipc_wrapper.ReadData(aOffset,aLength,data);
		return new String(data);
		}

    }
```


----------



## Ark (23. Jul 2009)

What. The. Fuck? :autsch:

Welches Rindvieh hat denn diesen geistigen Dünnschiss von sich gegeben? 

Sorry, aber wenn man so was sieht, rollen sich einem die Fußnägel rauf und runter!

...

Such bitte ein Beispiel für eine Schreibmethode (vorzugsweise int) heraus. Wenn das nicht geht, bzw. was eigentlich (angesichts der Umstände) viel besser ist: Lies möglichst verschiedene ints einmal mit der "Beispielmethode" *würg* und einmal mit meiner Methode aus. Gib dann die eingelesenen ints bitte jeweils so aus: 
	
	
	
	





```
System.out.println(Integer.toHexString(derIntWert));
```
Trotz allem fehlt uns noch immer die Spezifikation des Protokolls. Wir könnten dieses natürlich auch einem RE unterziehen, wenn wir den Code haben, der auf das Protokoll aufbaut.

Eine ganz andere Möglichkeit: Du gibst dich mit den Beispielcodes vorerst zufrieden. (ich wäre es aber ganz und gar nicht.)

Gute Nacht.
Ark


----------



## Painii (23. Jul 2009)

Snooky hat gesagt.:


> ```
> String ret = new String("1"+new Integer(dig1).toString()+new Integer(dig2).toString()+"."+new Integer(dig3).toString()+new Integer(dig4).toString());
> ```
> Ich bin auch von der Programmiermethode nicht so ganz überzeugt und habe das anders gelernt. Zum Beispiel würde ich einen Integer nicht unbedingt in einen String umwandeln.
> ...



Bewirkt so das gleiche, aber das erste ist eindeutig.

Bei "1"+dig1+dig2 gibt es ja 2 Möglichkeiten:
a) Du willst "1" + dig1 + dig2 aneinanderhängen.
b) Du willst "1" + (dig1 + dig2) bauen, also erst dig1 und dig2 addieren und dann die 1 vorne anhängen
So stellst du eher sicher dass du nicht irgendwie was falsches zusammenbaust.


----------



## mvitz (23. Jul 2009)

Aber selbst dann wäre:

```
String ret = "1" + String.valueOf(dig1) + String.valueOf(dig2) + "." + String.valueOf(dig3) + String.valueOf(dig4);
```
oder

```
String ret = "1" + Integer.toString(dig1) + Integer.toString(dig2) + "." + Integer.toString(dig3) + Integer.toString(dig4);
```
schöner.


----------



## Painii (23. Jul 2009)

Ja das stimmt natürlich.


----------



## Snooky (23. Jul 2009)

Schwierig, weil ich weder FSUIPC noch den Flugsimulator hier habe 
Aber ich pack mir mal Eclipse aufs Handy und werds morgen an der Uni auf den Rechner hauen und gucken ob ich ein paar Ints auslesen kann 
Übers Wochenende bin ich aber nich da, werd mich also wohl erst Sonntag wieder melden können.

Zu der Sicherheit:
Wenn ich 

String ret = "1"+dig1+dig2+"."+dig3+dig4;

schreibe, stelle ich doch auch sicher, dass genau das passiert, was ich möchte, oder nicht? Würde ich dig1 und dig2 addieren wollen, würde ich es in Klammern setzen. "Von alleine" kann da doch nichts passieren, oder?

Schreibmethoden hab ich aber 3 gefunden:


```
public void SetElevatorTrim(int aValue)
	{
	byte[] data = new byte[2];
	data[0] = (byte) (aValue & 0xff);
	data[1] = (byte) ((aValue >>8) & 0xff);
	fsuipc_wrapper.WriteData(0x0bc0,2,data);
	}

	public void SetElevator(int aValue)
	{
	byte[] data = new byte[2];
	data[0] = (byte) (aValue & 0xff);
	data[1] = (byte) ((aValue >>8) & 0xff);
	fsuipc_wrapper.WriteData(0x0bb2,2,data);
	}

	public void SetAileron(int aValue)
	{
	byte[] data = new byte[2];
	
	data[0] = (byte) (aValue & 0xff);
	data[1] = (byte) ((aValue >>8) & 0xff);
	fsuipc_wrapper.WriteData(0x0bb6,2,data);
	}
```
Aber warum kann er einen Int in ein 2 Byte großes Array packen? Geht das mit jedem Int, der kleiner ist als die Maximalgröße von Short?


----------



## Spacerat (24. Jul 2009)

Ich blick bei dem ganzen nun zwar auch nicht mehr durch, aber 2 ByteArrays addiert man halt, wie 0x7F800000 schon andeutete, über BigInteger. BigInteger verlangt die Arrays im Big-Endian-Format. Um vom Little-Endian zum Big-Endian zu wandeln, ist diese Methode hilfreich.
	
	
	
	





```
void wandeln(byte[] pBuffer)
{
  byte b ;
  int iLength = pBuffer.length;     
  for (int i = 0 ; i < iLength/ 2 ; i++) {
    b = pBuffer [i] ;
    pBuffer [i] = pBuffer [iLength - i - 1] ;
    pBuffer [iLength - i - 1] = b ;
  }
}
```
Mit BigInteger benötigt man zum Wandeln in die Primitivtypen dann auch keinen Beispielcode mehr. Man kann "longValue()", "intValue()", "shortValue()" usw. verwenden.


----------



## Painii (24. Jul 2009)

Snooky hat gesagt.:


> Zu der Sicherheit:
> Wenn ich
> 
> String ret = "1"+dig1+dig2+"."+dig3+dig4;
> ...



Und falls du es in Eile, weil du abgelenkt warst oder sonstwas mal vergisst die 2 Klammern zu setzen (nicht in deinem Beispiel), dann gehts halt schief... und es kann fies sein solche Fehler zu suchen.
Trifft grade nicht auf deinen Fall zu, aber falls du mal was in nem String addieren musst musst du halt immer dran denken  . (Oder die Addition generell vor dem String ausführen... sind halt dann mehr Zeilen code)


----------



## Snooky (24. Jul 2009)

Painii hat gesagt.:


> Und falls du es in Eile, weil du abgelenkt warst oder sonstwas mal vergisst die 2 Klammern zu setzen (nicht in deinem Beispiel), dann gehts halt schief... und es kann fies sein solche Fehler zu suchen.
> Trifft grade nicht auf deinen Fall zu, aber falls du mal was in nem String addieren musst musst du halt immer dran denken  . (Oder die Addition generell vor dem String ausführen... sind halt dann mehr Zeilen code)



Macht das eigentlich was in Java bezüglich Perfomance, ob man z.B.


```
String a = (5+10);
```

oder

```
int b = 5+10;
String a = b;
```

macht?

Oder erkennt der Compiler, was hier passieren soll und setzt das entsprechend effektiv um?


----------



## Painii (24. Jul 2009)

Snooky hat gesagt.:


> ```
> String a = (5+10);
> ```


Da gibt mir Eclipse nen Fehler (cannot convert from int to String)... müsste hier schon String.valueOf(5+10) verwenden


> ```
> int b = 5+10;
> String a = b;
> ```


Primitive Datentypen (z.b. int) kosten relativ wenig im Gegensatz zu den nicht-primitiven.
Nicht-primitive brauchen ja ihre Referenzen, eigene Variabelen etc. die erst erzeugt werden, primitive bekommen ihre paar Speicherplätze und gut ist.

In deinem Beispiel würde es denk ich wenig Unterschied machen, weil du ja in beiden Fällen einen String erzeugst, da kann man die Kosten fürs int erstellen vernachlässigen denke ich.


----------



## Ark (24. Jul 2009)

Um Das Chaos hier etwas zu erhöhen, setze sich nun meine Variante von StandByFreqAsString() hier rein. Sie ist natürlich ungetestet, wie sonst jede Methode von mir auch.  Sie ist zumindest in der Theorie deutlich schneller, da Speicherplatz genau nach Bedarf reserviert wird und keine Divisionen u.ä. anfallen.

```
public String StandByFreqAsString(){
		int freq=StandByFreq();
		return new StringBuilder(6)
			.append("1")
			.append((char)('0' + (freq >> 12 & 0x0F)))
			.append((char)('0' + (freq >> 8 & 0x0F)))
			.append(".")
			.append((char)('0' + (freq >> 4 & 0x0F)))
			.append((char)('0' + (freq >> 0 & 0x0F)))
			.toString();
	}
```
Frage: Muss die Methode zwingen so heißen? Sie verstößt ja gegen jegliche Namenskonventionen. "AsString" ist schon mal komplett überflüssig, und dass ein numerischer Wert überhaupt als String zurückgegeben wird, ist, wie schon erwähnt wurde, einfach nur schwachsinnig. Mich würde mal interessieren, wozu um alles in der Welt eine so verrückte Methode gebraucht wird. Was passiert denn dann weiter mit den Werten? Sprich: wer ruft denn diese Methode auf, und was macht derjenige damit?



Snooky hat gesagt.:


> Wenn ich
> 
> String ret = "1"+dig1+dig2+"."+dig3+dig4;
> 
> schreibe, stelle ich doch auch sicher, dass genau das passiert, was ich möchte, oder nicht?


Da hast du schon Recht. 



Snooky hat gesagt.:


> Aber warum kann er einen Int in ein 2 Byte großes Array packen? Geht das mit jedem Int, der kleiner ist als die Maximalgröße von Short?


Die Eingabe erfolgt zwar als int, berücksichtigt werden wohl aber nur die untersten zwei Bytes, ein short hätte es also auch getan.

Was mir viel mehr Sorgen bereitet, sind die Offsets wie z.B. 0x0bb2. Offensichtlich werden die Daten anders transportiert, als ich zunächst annahm. Ohne die Spezifikation des Protokolls stehen wir hier allem unerwartet früh hilflos gegenüber.

Ark


----------



## Snooky (24. Jul 2009)

Da hilft vielleicht die Spezifikation der Offsets selbst weiter.
Jedes Offset, das für FSUIPC existiert, wird in einer Liste so angegeben:
Ich meine hier eine ganz normale Auflistung der Offsets mit Größe und Funktion, keine Liste im programmiertechnischen Sinne.

1. Der "Name" des Offsets, also der Wert, z.B. 0020 oder 0x0020
2. Die Größe in Byte (für 0020 wäre das z.B. 4)
3. Die Funktion oder "was sagt die Zahl hierdrin aus?": für 0020 wäre das z.B. die Bodenhöhe in Metern*256.

Um mal bei 0BB2 zu bleiben:
Dort steht folgendes:
0BB2, 2 Byte groß, Elevator Control Input: -16383 bis +16383

Was auch immer nun Elevator Control Input ist. Das müsste ich selbst nachgucken.

Konkret werden also alle Daten, die der Flugsimulator bereitstellt, in solchen Offsets abgespeichert und können hierdrüber gelesen und geschrieben werden.
Die aktuelle Liste ist 63 Seiten lang, es gibt also ne ganze Menge davon.

Ich habe außerdem im Forum den Entwickler von FSUIPC gefragt, ob er eine Spezifikation des Protokolls oder der dll hat.
Scheinbar ist die dll, die ich momentan mit Java nutze, nur eine wrapper dll (nennt man das so?), die die Funktionen aus der C dll für Java nutzbar macht.
Leider hab ich von dlls bislang kaum eine Ahnung, habe aber von JNA gehört und werde mir das mal angucken. Vielleicht kann ich die dll für C so besser nach Java portieren und so auch rausfinden, wie die Daten transportiert werden.

Warum man sich die Frequenz als String holen will ist mir auch schleierhaft, da man damit ja überhaupt nicht mehr arbeiten kann. Vielleicht war das für eine Ausgabe auf einem LCD Bildschirm gedacht, weil ja die vorangehende 1 immer vorausgesetzt wird, aber in der Frequenz als Integer nicht auftaucht. Gibt man nun den Integer auf einem kleinen LCD aus, fehlt ja die vordere 1. Das wäre für die FLUSI Fans wohl ein unverzeihlicher Schönheitsfehler.


----------



## Ark (24. Jul 2009)

Snooky hat gesagt.:


> Konkret werden also alle Daten, die der Flugsimulator bereitstellt, in solchen Offsets abgespeichert und können hierdrüber gelesen und geschrieben werden.
> Die aktuelle Liste ist 63 Seiten lang, es gibt also ne ganze Menge davon.


Endlich! Dieses 63 Seiten starke Dokument ist das, was du brauchst. Möglicherweise steht da auch drin, wie eine Verbindung zum Simulator hergestellt wird, wie sie abgebaut wird, inwiefern es Abhängigkeiten bei der Kommunikation gibt etc. Auch die Bytereihenfolge(n) sollte(n) dort spezifiziert sein.

Bevor du aber alles neu schreibst, solltest du zunächst schauen, inwiefern du den Java-Code, der mit dem Wrapper mitgeliefert wurde (vorausgesetzt natürlich, es wird etwas mehr geboten als nur der Wrapper), bereits als "Buchse" verwenden kannst. Für meine Begriffe scheinen die Beispiele, wie du sie hier vorstellst, prinzipiell dazu geeignet, als "Buchse" verwendet zu werden, aber sie sind grottenschlecht geschrieben, da gegen formale Konventionen verstoßend, umständlich, speicherbelastend, langsam und trotz allem nicht einmal leicht(er) nachvollziehbar, sondern im Gegenteil sogar verschleiert.

Nachdem du hier den prinzipiellen Aufbau eines Eintrags in der Liste beschrieben hast, tut sich bei mir ein völlig neues Bild auf: Nach meinen Vermutungen scheint es also einen riesigen Berg an Variablen zu geben, die das Verhalten des Simulators beeinflussen. Welche Variable das ist, die man gerade auslesen oder verändern möchte, wird durch das Offset bestimmt. Man hat dabei direkten Zugriff auf die Variablen, so etwas wie Datenströme (meine erste Annahme) gibt es nicht.

Ohne Genaueres zu kennen, wäre jetzt meine Idee, eine Klasse (z.B. wie erwähnt FsuipcInterface, vielleicht ist aber wirklich ein besserer Name für die Klasse möglich) zu schreiben, die Primitive in ein Java-Programm einliest oder aus diesem zum Simulator hinausschreibt, also getInt(), setInt(), getShort() usw. Außerdem würde ich die Klasse mit allen(!) Offset-Konstanten beglücken, die in der Spezifikation genannt werden (oder zumindest diejenigen, die ich brauche) und ihnen dabei aussagekräftige Namen geben, z.B:

```
public static final int ELEVATOR_CONTROL_INPUT = 0x0BB2;
```
So etwas wäre dann, wie gesagt, in einer Klasse. Dann würde es wohl so weitergehen, dass ich z.B. alles, was mit Wetter zu tun hat, in eine Klasse Weather stecke, wobei es Methoden gibt, die z.B. so aussehen könnten:

```
public void setCeiling(int meters){
	FsuipcInterface.writeShort(FsuipcInterface.WEATHER_CEILING, (short)meters);
}
```
Eventuell wäre aber auch hier noch eine Zwischenschicht angebracht, die gewünschte Veränderungen sammelt und erst später alle auf einmal an den Simulator schreibt.


Snooky hat gesagt.:


> Das wäre für die FLUSI Fans wohl ein unverzeihlicher Schönheitsfehler.


Schönheitsfehler hin oder her: So etwas nenne ich Designfehler, denn hier fängt es an, dass Model und View miteinander verwurschtelt werden.

Ark


----------



## Snooky (26. Jul 2009)

Ich möchte eher eine Variante, die in der Lage ist alle Offsets zu beschreiben, unabhängig davon, welches Offset konkret gemeint ist.
Das Java Programm soll beim Start aus einer Exceltabelle die Definitionen für die verschiedenen Schalter einlesen, also z.B.: Beim Drücken von Schalter 12: Schreibe 1 in 0x0222, beim Loslassen von Schalter 12: Schreibe 0 in 0x0222.

Hierfür habe ich eine Hashtable angelegt, in der alle Offsets mit ihrer Größe in Byte abgespeichert sind. Zum Glück hatte ich bereits eine Liste davon als txt, so dass das Einlesen daraus relativ einfach ging. Die Offsets stehen nun in einer Exceltabelle und werden auch daraus eingelesen. Ich habe die Schleife so flexibel gelassen, dass sie bis zum Ende der Tabelle liest. Also ist das nachträgliche Einfügen von neuen Offsets auch kein Problem.

Die Schalter sollen dann am Ende folgende Funktionen haben:
Addieren (auch negatives "addieren") eines Wertes zum aktuellen Wert im Offset.
Setzen eines Wertes im Offset.
Setzen einzelner Bits in bestimmten Offsets.

Das mit den Bits kommt daher, dass einige Offsets Bitweise funktionieren.
Zum Beispiel für sämtliche Funkgeräte an Bord existiert ein Offset. Ist dort z.B. 2^4 gesetzt, steht ein bestimmtes Gerät auf Empfang, sonst eben nicht.

Um das ganze flexibel zu halten, habe ich mir nun folgendes überlegt:
Jede Schreibfunktion soll die gleiche Methode benutzen. Der Paramater der Byteanzahl wird ja aus dem Offset übergeben. Diese Angabe fordert der Wrapper ja.
Dann, so habe ich weiter überlegt, benutze ich einfach immer 8 Byte große Arrays.
Dadurch, dass die auszulesende Bytezahl ja mitgeben wird, müsste er dadurch hoffentlich die zu verwendenden Bytes finden. Das muss ich allerdings nochmal testen.
Würde ich alle Funktionen sinnvoll in Klassen zusammen fassen, hätte man hinterher riesige Wälzer an Text um das ganze zu Dokumentieren und würde am Ende sicher 85% der programmierten Funktionen nicht nutzen. Ich denke so ist das einfach flexibler.
Zuerst muss ich aber nun herausfinden, wie das Übertragen genau funktioniert 

Edit: Kann man vielleicht hieraus lesen, wie die Daten ankommen?


```
public int getInt(int aOffset)
		{
		ByteBuffer buf = ByteBuffer.allocate(4);
		buf.order(ByteOrder.LITTLE_ENDIAN);
		byte[] data = new byte[4];
		fsuipc_wrapper.ReadData(aOffset,4,data);
		buf.put(data,0,4);
		return buf.getInt(0);
		}
```
Wenn der ByteBuffer die ByteOrder Little Endian zugesprochen bekommt, bedeutet das dann, dass die Daten so ankommen? Oder dass sie eben genau nicht so ankommen und der ByteBuffer sie nach Erhalt erst neu sortiert?


----------



## Ark (26. Jul 2009)

Snooky hat gesagt.:


> Ich möchte eher eine Variante, die in der Lage ist alle Offsets zu beschreiben, unabhängig davon, welches Offset konkret gemeint ist.


Tut mir Leid, aber an mindestens einer Stelle in deinem Programmcode wirst du nicht drumrum kommen, weil die Schnittstelle (der Stecker) genau das verlangt. Oder wolltest du darauf hinaus, dass du die Offsets nicht so "hart" kodiert in deinem Programm haben möchtest, wie ich es oben vorschlug?



Snooky hat gesagt.:


> Die Schalter sollen dann am Ende folgende Funktionen haben:
> Addieren (auch negatives "addieren") eines Wertes zum aktuellen Wert im Offset.
> Setzen eines Wertes im Offset.
> Setzen einzelner Bits in bestimmten Offsets.


Dann würde ich vorschlagen, dass du dir solche Schnittstellen schaffst. Du implementierst also nicht nur getInt(), setInt(), getByte(), setByte() usw., sondern auch z.B. setBit() und getBit(). Auf jeden Fall sollte die unterste Schicht nach Möglichkeit durch eine einzige neue Klasse abgedeckt werden, die dann auch die einzige(!) ist, die direkt mit dem vorgegebenen Wrapper kommuniziert. Alle anderen Klassen (die höheren Schichten) kommunizieren über diese neue Klasse.

Eine höhere Schicht könnte dann eben wie von dir vorgeschlagen eine einzige Methode sein, die je nach Fall dann die entsprechende Methode (getInt(), setInt() usw.) der unteren Schicht aufruft. Auf alle Fälle solltest du aber zunächst einmal die Klasse für diese untere (wenn nicht sogar unterste) Schicht bauen. Diese Klasse gibt es sogar schon, dass ist nämlich die hier erwähnte Klasse FSUIPC. Wie die höheren Schichten aussehen, die darauf zurückgreifen, ist erst einmal vollkommen egal. Das erste Ziel sollte sein, eine Buchse zu schaffen, mit der du die Bytes, wie sie der Wrapper braucht, in primitive Datentypen verwandeln kannst, mit denen Java umgehen kann. (Ich würde auch Strings dazuzählen. Es sind zwar keine primitiven Datentypen, da Strings aber immutable sind, funktionieren sie wie solche.)



Snooky hat gesagt.:


> Um das ganze flexibel zu halten, habe ich mir nun folgendes überlegt:
> Jede Schreibfunktion soll die gleiche Methode benutzen. Der Paramater der Byteanzahl wird ja aus dem Offset übergeben. Diese Angabe fordert der Wrapper ja.
> Dann, so habe ich weiter überlegt, benutze ich einfach immer 8 Byte große Arrays.
> Dadurch, dass die auszulesende Bytezahl ja mitgeben wird, müsste er dadurch hoffentlich die zu verwendenden Bytes finden. Das muss ich allerdings nochmal testen.


Eigentlich sollte das Testen gar nicht nötig sein. Stattdessen sollte es (wie schon so oft gesagt) eine Spezifikation geben, in der haarklein erklärt ist, wie die Schnittstelle funktioniert. Und an diese Spezifikation solltest du dich unbedingt halten, unabhängig davon, ob du irgendwelche Hacks findest, mit denen man alles schneller machen kann etc. Es ist nämlich durchaus legitim, dass die Implementierung des Wrappers geändert wird, so dass deine Hacks nicht mehr funktionieren, die alte Spezifikation aber immer noch gültig ist. _Niemals_ irgendetwas benutzen, was nicht spezifiziert (bzw. dokumentiert) ist, ganz egal, wie verlockend es auch sein mag!



Snooky hat gesagt.:


> Würde ich alle Funktionen sinnvoll in Klassen zusammen fassen, hätte man hinterher riesige Wälzer an Text um das ganze zu Dokumentieren und würde am Ende sicher 85% der programmierten Funktionen nicht nutzen. Ich denke so ist das einfach flexibler.


Du bist ja nicht gezwungen, sofort alle Offsets zu speichern und auch auszunutzen. Du kannst ja Klassen, wie ich sie vorschlug (z.B. Weather), stückchenweise gerade an den Stellen wachsen lassen, wo es nötig ist. Meine Idee war halt, möglichst ohne große Umschweife von den Bytes zu Objekten zu kommen, wie es sich für eine OO-Sprache nun einmal gehört. Aber wenn du meinst, das ist nicht nötig und du kennst da einen für deine Anforderungen geeigneteren Weg, möchte ich dich nicht aufhalten.



Snooky hat gesagt.:


> Zuerst muss ich aber nun herausfinden, wie das Übertragen genau funktioniert


Ganz recht, und das sollte, wie gesagt, spezifiziert sein.



Snooky hat gesagt.:


> Wenn der ByteBuffer die ByteOrder Little Endian zugesprochen bekommt, bedeutet das dann, dass die Daten so ankommen? Oder dass sie eben genau nicht so ankommen und der ByteBuffer sie nach Erhalt erst neu sortiert?


Tja, das ist eine gute Frage. Da man sie überhaupt angeben kann, nehme ich mal stark an, dass die Umwandlung on the fly arbeitet und die Bytereihenfolge, die man da festlegt, gerade die Reihenfolge ist, mit der die Daten dann tatsächlich in diesem Puffer stehen. Ein weiterer Hinweis darauf ist der Umstand, dass man sich mit ByteOrder.nativeOrder() die Bytereihenfolge der Maschine holen kann.

Kann es übrigens sein, dass diese Offsets voneinander abhängig sind? Also wenn ich z.B. "ABC" in 0x1234 schreibe, schreibe ich dann in Wirklichkeit ein "A" in 0x1234, ein "B" in 0x1235 und ein "C" in 0x1236? Wenn dem so ist, sollte die Buchse auch kontrollieren, dass Strings nicht zu lang sind usw.

Noch mal kurz:

Versuche, in Schichten zu denken. Nimm dir das OSI-Schichtenmodell als Vorbild. Darauf basiert nämlich das ganze Internet, und wie du siehst, funktioniert es ganz offensichtlich. Auch Linux orientiert sich stark an einem schichtartig aufgebauten System, und gerade diesem Umstand ist es zu verdanken, dass Linux inhärent sicher ist. Ein Negativbeispiel wäre Windows, denn da verschwimmen diese Schichten, sodass ein ActiveX-Irgendetwas quasi Rechte am System haben kann etc.

Zur Orientierung in deinem Fall:


Schicht 1 sei der Wrapper. Hier werden nur noch Bytes an bestimmte Offsets transportiert.
Schicht 2 sorge dafür, dass beliebige Java-Datentypen (byte, short, int long, String etc.) an beliebige Offsets geschrieben werden können. An dieser Stelle spielt noch keine Rolle, ob für den String genug Platzt ist oder ob der Datentyp mit seiner Länge überhaupt zum konkreten Offset passt.
Schicht 3 stellt sicher, dass die richtigen Offsets mit den richtigen Daten gefüttert werden, also dass auf Offset 0x1234 nur Strings bis zur Länge 10 genutzt werden können, dass auf Offset 0x0303 nur vorzeichenbehaftete ints benutzt werden usw. An dieser Stelle könnte man z.B. eine Methode basteln, die grundsätzlich einen Offset und einen String als Eingabe bekommt und dann z.B. automatisch den String in ein int umwandelt, wenn festgestellt wird, dass am gegebenen Offset eben ein int erwartet wird.
Schicht 4 kann aus einem Zeichenstrom (Reader) einzelne interessante Strings isolieren und diese der gerade beschriebenen Offset-String-Methode der Schicht 3 zuführen.
Schicht 5 sorgt dafür, dass aus den Zeilen und Spalten eines Excel-Dokuments ein Zeichenstrom (Reader) gemacht wird, den Schicht 4 benutzen kann usw.

In diesem ganzen System greift die höhere Schicht (Idealfall: pro Schicht eine Klasse!) zur Erfüllung seiner Aufgaben nur gerade auf die Möglichkeiten der nächstniedrigeren Schicht zu. Also Schicht 3 ruft nur Methoden der Schicht 2 auf, diese wiederum nur Methoden der Schicht 1 usw.

Ark


----------



## Snooky (27. Jul 2009)

Hallo Ark,

du hast Recht, die Offsets sind irgendwie voneinander abhängig. Aber das regelt FSUIPC zum Glück intern, so dass wir nur die Offsets lesen und schreiben können, die FSUIPC nach außen öffnet. Wenn wir dann einen Wert dorthin schicken, wird er automatisch auf die passenden Offsets verteilt.

An dieser Stelle möchte ich mich auch noch einmal ganz herzlich bei dir bedanken. Auch wenn ich im Programmcode noch nicht wirklich weiter gekommen bin, habe ich viel gelernt. Ich habe verstanden, was Little und Big Endian ist. Ich weiß nun, wie die Shift Operatoren funktionieren und ich weiß, wie ich Bitmanipulation in Java angehen muss. Ich habe gelernt, wie man sich Gedanken um eine vernünftige Programmstruktur macht und wie man an Schnittstellenprobleme herangeht.

Das Problem wird später ja auch noch sein, dass der IO-Warrior noch mit drin hängt.
Sagen wir mal, du hast das Programm von unten aufgerollt. Dann versuche ich das nun von oben:

1. Der Benutzer kann Schalter am IO-Warrior betätigen.
2. Der IO-Warrior sendet einen Report (als Integer-Array), der die gerade gedrückten und/oder losgelassenen Knöpfe beinhaltet. Meistens genau einer, es sei denn, der Benutzer legt wirklich relativ genau zeitgleich 2 Schalter um.
3. Jeder Schalter wird durch ein Objekt der Klasse VirtualButton repräsentiert. Dieser virtuelle Knopf hat 2 Vektoren, für Drücken und Loslassen, in denen Actions gespeichert sind.
4. Diese Actions (set, add, setbit, clearbit) werden dann ausgeführt und senden ihren Wert an meine Buchse. Die Actions sind definiert durch den Wert, den sie addieren oder setzen sollen oder durch das Bit, dass sie setzen oder löschen sollen und "ihr" Offset.
5. Die Buchse bereitet die Daten auf und schickt sie an FSUIPC.

Sascha


----------



## Snooky (27. Jul 2009)

Hallo Ark,

ich dachte, du möchtest vielleicht sehen, wie meine Buchse mittlerweile aussieht 
Leider konnte ich immer noch nicht testen, wie die Werte hier ankommen. Der Entwickler von FSUIPC hat mir aber versichert, dass die Daten intern als Little Endian verarbeitet werden. Da die dll nur eine Wrapper dll von der orignal C dll ist, nehme ich nun bis ich das selbst testen kann erstmal an, dass die Daten als Little Endian ankommen. Daher müsste ich im Prinzip auch 8 Byte große Arrays unter Angabe der Bytezahl an weniger große Offsets schicken können.

Hier der Code:


```
public class FSUIPC
	{ 
		private offsetDatabase db = new offsetDatabase();
		private static byte[] data = new byte[8];
		private int count;
		
		public void writeData(int offset, int value)
		{
				count = db.getSize(offset);
			
			  	data[0] = (byte)(value);
		        data[1] = (byte)(value >> 8);
		        data[2] = (byte)(value >> 16);
		        data[3] = (byte)(value >> 24);
		    	data[4] = (byte)(value >> 32);
		        data[5] = (byte)(value >> 40);
		        data[6] = (byte)(value >> 48);
		        data[7] = (byte)(value >> 56);		        
		        fsuipc_wrapper.WriteData(offset, count, data);
		        fsuipc_wrapper.Process();
		}
		
		public int readData(int offset)
		{
			count = db.getSize(offset);
			fsuipc_wrapper.ReadData(offset, count, data);
			int ret = 
            (data[0] & 0xFF) |
            (data[1] & 0xFF) << 8 |
            (data[2] & 0xFF) << 16 |
            (data[3] & 0xFF) << 24 |
            (data[4] & 0xFF) << 32 |
            (data[5] & 0xFF) << 40 |
            (data[6] & 0xFF) << 48 |
            (data[7] & 0xFF) << 56;			
			
			return ret;
		}
		
		
		public void setBit(int offset, int bit)
		{
			fsuipc_wrapper.ReadData(offset, count, data);
			count = db.getSize(offset);
			
			if(bit <= 8)
			{
				data[0] = (byte) (data[0] | getSetBits(bit));		
			}
			else
			{
				data[1] = (byte) (data[1] | getSetBits((bit-8)));	
			}
			
			fsuipc_wrapper.WriteData(offset, count, data);
		}
		
		
		public void clearBit(int offset, int bit)
		{
			fsuipc_wrapper.ReadData(offset, count, data);
			
			if(bit <= 8)
			{
				data[0] = (byte) (data[0] & getClearBits(bit));		
			}
			else
			{
				data[1] = (byte) (data[1] & getClearBits((bit-8)));	
			}
		}
		
		private int getSetBits(int i)
		{
			int ret = 1;
			for(int j = 1; j <= i; j++)
			{
				ret = ret * 2;
			}
			return ret;
		}
		
		private int getClearBits(int i)
		{
			int singleBits = 1;
			int ret = 1;
			for(int j = 1; j < 8; j++)
			{
				singleBits = singleBits * 2;
				if(j != i)
				{
				ret = ret + singleBits;
				}
			}
			return ret;
		}
		
	
    }
```


----------



## Ark (27. Jul 2009)

Snooky hat gesagt.:


> Hallo Ark,
> 
> ich dachte, du möchtest vielleicht sehen, wie meine Buchse mittlerweile aussieht


Ja, das wäre interessant zu sehen. Danke. Du hast doch hoffentlich nichts dagegen, wenn ich sie dir auseinandernehme und auf Fehlersuche gehe? 



Snooky hat gesagt.:


> Daher müsste ich im Prinzip auch 8 Byte große Arrays unter Angabe der Bytezahl an weniger große Offsets schicken können.


Die Begründung dazu ist mir schleierhaft, zugegebenermaßen.



Snooky hat gesagt.:


> [JAVA=9]		public void writeData(int offset, int value)
> {
> count = db.getSize(offset);
> 
> ...


Was willst du mit diesem Code erreichen? So wird es dir jedenfalls kaum gelingen. Begründung: value ist vom Typ int, und ein int besteht aus 32 Bits. Sobald du also mindestens 32 Bit nach rechts oder links zu verschieben versuchst, kann nur 0 (oder -1, aber das ist jetzt nebensächlich) rauskommen. Wenn maximal 32 Bit drin sind, können auch nur maximal 32 Bit rausgeschoben werden. Jeder größere Schiebeversuch ist Nonsens.

Dieses "Problem" wird von der JVM so "gelöst", dass es nur die untersten 5 Bit des zweiten Operanden einer Schiebeoperation beachtet. Anders ausgedrückt: Du kannst dir bei jeder Operation [c]a >> b[/c] ein [c]a >> (b & 31)[/c] vorstellen. Aus [c]value >> 32[/c] wird so [c]value >> 0[/c], aus [c]value >> 40[/c] wird [c]value >> 8[/c] usw. Das wird wohl kaum das sein, was du erreichen wolltest, oder?  Dass das so "kompliziert" gemacht wird, liegt übrigens daran, dass genau dieses [c]& 31[/c] an besagter Stelle sehr häufig gebraucht wird. Das gilt übrigens auch für die anderen Schiebeoperatoren << und >>>.



Snooky hat gesagt.:


> [java=45]			fsuipc_wrapper.ReadData(offset, count, data);
> count = db.getSize(offset);[/code]


Bist du dir sicher, dass du diese zwei Zeilen hier nicht vertauschen solltest? Aber auch so wäre es nett, wenn du mir erklären würdest, was diese Methode (und auch die anderen Bit-Methoden) machen soll. Der Code ist etwas umständlich und für mich kaum nachvollziehbar. Ich nehme stark an, dass die Lösung sehr viel einfacher sein kann als deine.

Vielen Dank noch mal für die Einblicke. Ich hoffe, du machst weiterhin Fortschritte. 

Und nur, damit du meine Erklärung zu Big Endian und Little Endian nicht in den falschen Hals bekommst: In meiner Erklärung habe ich beispielhaft mit Ziffern rumgespielt, um den Unterschied klarzumachen. In der Praxis werden natürlich nicht einzelne Dezimalziffern, sondern ganze Bytes umsortiert. (Bytes sind gewissermaßen 256er-Ziffern, also Ziffern eines Zahlensystems mit der Basis 256.)

Ark


----------



## Snooky (27. Jul 2009)

Oh, da hast du natürlich recht.
Eigentlich sollte hier ein long hin. Dazu aber dann unten mehr.
Der Code 
	
	
	
	





```
fsuipc_wrapper.ReadData(offset, count, data);
            count = db.getSize(offset);
```
muss natürlich umgedreht werden!

Also, um nochmal mein momentanes Hauptproblem auf den Punkt zu bringen:
Wenn der spätere Nutzer meines Programms zum Beispiel dem Button 12 die Funktion schicke 16384 an 0x037C beibringen möchte und dem Button 11 die Funktion schicke 134 an 0x037D, dann sollen beide Funktionen diesselbe write Methode aufrufen können, auch wenn 0x037C 4 Byte groß ist und 0x037D nur 1 Byte groß ist.

Die Sache ist ja die, dass weder ich noch das Programm vorher wissen, welche Aktion wo landet.
Alles, was ich sagen kann, ist: 0x037C ist 4 Byte groß und 0x037D ist 1 Byte groß.
Also müssen entweder beide Aktionen die gleiche Methode zum schreiben aufrufen, oder bei jedem Aufruf der Funktion muss eine Abfrage gestartet werden, durch dessen Ergebnis festgestellt wird, ob wir die Schreibmethode für 1, 2, 4 oder 8 Byte große Arrays aufrufen müssen.
Dafür könnte ich natürlich meine Offset Database befragen, die zu jedem Key (Offset als Integer) die Größe, ebenfalls als Integer abspeichert.
Dann müsste ich aber in der Ausführmethode für die Aktion sowas wie dieses hier einführen:

```
int count = db.getSize(offset);
if(count==1) writeByte(value);
if(count==2) writeShort(value);
if(count==4) writeInt(value);
```

Daher war die Idee, dass ich einfach ALLE ankommenden Daten in ein 8 Byte Array packe und eben nur die untersten Bytes geschickt werden.

Die Bit-Methoden sollen so funktionieren:
Um ein Bit zu aktivieren, das vorher 0 war, mache ich eine Oder-Verknüpfung mit ganz vielen Nullen, außer an der Stelle des zu setzenden Bits. Dort soll eine 1 stehen.
War z.B. Bit 5 vorher 1 passiert nichts, die Methode heißt ja auch nicht swapBit(). War Bit 5 vorher eine 0, wirds zur 1. Durch die Oder Verknüpfung und die ganzen Nullen bleiben alle anderen Bits unberührt.
Bei der clearBit Methode nutze ich dann eine Und-Verknüpfung, die überall Einsen hat, außer an der Stelle des Bits, das gelöscht werden soll, da steht eine null.

Mit Integer.toBinaryString, was scheinbar auch bei Bytes funktioniert, habe ich das getestet und das hat soweit auch funktioniert.

Sascha


----------



## Ark (27. Jul 2009)

Snooky hat gesagt.:


> Daher war die Idee, dass ich einfach ALLE ankommenden Daten in ein 8 Byte Array packe und eben nur die untersten Bytes geschickt werden.


Das ergibt natürlich Sinn.



Snooky hat gesagt.:


> Die Bit-Methoden sollen so funktionieren:
> Um ein Bit zu aktivieren, das vorher 0 war, mache ich eine Oder-Verknüpfung mit ganz vielen Nullen, außer an der Stelle des zu setzenden Bits. Dort soll eine 1 stehen.
> War z.B. Bit 5 vorher 1 passiert nichts, die Methode heißt ja auch nicht swapBit(). War Bit 5 vorher eine 0, wirds zur 1. Durch die Oder Verknüpfung und die ganzen Nullen bleiben alle anderen Bits unberührt.
> Bei der clearBit Methode nutze ich dann eine Und-Verknüpfung, die überall Einsen hat, außer an der Stelle des Bits, das gelöscht werden soll, da steht eine null.


Ich glaube, du bist dir über die arithmetischen Zusammenhänge der Schiebeoperatoren noch nicht ganz im Klaren. Vielleicht wird es aber klarer, wenn du diesen Code probierst (in der Annahme natürlich, er tut genau das, was du willst ^^):

```
public void setBit(int offset,int bit){
		int count = db.getSize(offset);
		fsuipc_wrapper.ReadData(offset,count,data);
		data[bit >> 3] |= 1 << (bit & 7);
		fsuipc_wrapper.WriteData(offset,count,data);
	}
    public void clearBit(int offset,int bit){
		int count = db.getSize(offset);
		fsuipc_wrapper.ReadData(offset,count,data);
		data[bit >> 3] &= ~(1 << (bit & 7));
		fsuipc_wrapper.WriteData(offset,count,data);
	}
    public void flipBit(int offset,int bit){
		int count = db.getSize(offset);
		fsuipc_wrapper.ReadData(offset,count,data);
		data[bit >> 3] ^= 1 << (bit & 7);
		fsuipc_wrapper.WriteData(offset,count,data);
	}
```
An dieser Stelle wirkt sich übrigens der Umstand positiv aus, dass es sich um Little Endian handelt, sonst wäre noch eine Operation mehr nötig gewesen.

Die Variable count würde ich übrigens nur als lokale Variable (wie im Code gerade) benutzen. Dass ich das Array als Member deklariert habe, hat den Grund, dass es so nicht mit jedem Methodenaufruf neu erzeugt werden muss.

Warum sind die Methoden eigentlich nicht alle statisch?

Ark

*EDIT:* Quelltext korrigiert, siehe Folgebeiträge.


----------



## Snooky (28. Jul 2009)

Hallo 
Deine Methoden verstehe ich wirklich nicht so richtig. Aber sie funktionieren, ich habe gerade etwas damit rumgespielt.
Sie machen wirklich genau das, was ich haben möchte.
Was ich zum Beispiel nicht verstehe ist die Adressierung des Arrays.
Warum data[bit >> 8]? In bit kommt doch sowas an wie 5 oder 7. Also 00000101 oder 00000111. Warum macht man denn da einen Rechtsshift um 8? Dann hat man doch nur noch Nullen?
Die Methoden sollte ich tatsächlich noch static machen. Sonst müsste ich ja jeder Aktion, wovon ich am Ende pro Button optimistisch gerechnet 2 habe (also insgesamt 128), ein eigenes FSUIPC mitgeben. Also hätte ich 128 FSUIPC Objekte, eher mehr.
Darum ist die Idee mit static richtig gut 

Du weißt nicht zufällig, wie man eine C dll nach Java portieren kann, oder? Durch JNI, so habe ich gelesen, wird ja nur ein wrapper erzeugt, der die C dll aufruft. Damit ist man dann aber nicht mehr Plattformunabhängig. Diesen Anspruch hat Java aber doch.
Am liebsten hätte ich eine "echte" Java dll.


----------



## Ark (28. Jul 2009)

Snooky hat gesagt.:


> Was ich zum Beispiel nicht verstehe ist die Adressierung des Arrays.
> Warum data[bit >> 8]? In bit kommt doch sowas an wie 5 oder 7. Also 00000101 oder 00000111. Warum macht man denn da einen Rechtsshift um 8? Dann hat man doch nur noch Nullen?


Das war tatsächlich ein Fehler; beabsichtigt war [c]>> 3[/c] gewesen. Ich habe das so weit oben korrigiert. Gut, dass du aufpasst. 

So ein [c]>> 3[/c] ist identisch mit einem "durch zwei hoch 3", also einer Ganzzahldivision durch 8. Damit bekomme ich den Quotienten, also den großen Teil der Division. Das [c]& 7[/c] berechnet "modulo (7 plus eins)", also den Rest oder kleinen Teil nach der Division durch 8. Mit diesen beiden Operationen kann man die Position eines Bits in einem Array von bytes ausmachen. Der große Teil gibt die Position des Elements im Array an, der kleine Teil gibt die Position innerhalb dieses Arrayelements an.



Snooky hat gesagt.:


> Die Methoden sollte ich tatsächlich noch static machen. Sonst müsste ich ja jeder Aktion, wovon ich am Ende pro Button optimistisch gerechnet 2 habe (also insgesamt 128), ein eigenes FSUIPC mitgeben. Also hätte ich 128 FSUIPC Objekte, eher mehr.
> Darum ist die Idee mit static richtig gut


Na ja, es war nicht wirklich meine Idee. Der Stecker kennt halt nur static, und deswegen werden alle Methoden bis zu einer bestimmten Schicht auch static sein (müssen).



Snooky hat gesagt.:


> Du weißt nicht zufällig, wie man eine C dll nach Java portieren kann, oder?


Zugegeben, ich habe von so etwas wirklich keine Ahnung. Da müsstest du andere hier im Forum fragen. Mich fragt nur, warum du das wissen willst. Gibt es etwa noch mehr "Stecker", von denen ich nichts weiß und du welche brauchst?

Ark


----------

