BindingsErstellen

From Tinkerunity
Jump to: navigation, search

Funktionsweise

Die grundsätzliche Funktionsweise der Bindings ist einfach. Die Bindings kommunizieren mit dem Brick Daemon über TCP/IP und tauschen so Daten mit Bricks aus. Kommunikation findet statt mit simplen Paketen, jedes Paket besteht aus 4 Byte Header gefolgt von Nutzdaten:

struct Header {   
    uint8 stack_id; // Stack ID des anzusprechenden Bricks oder Bricklets
    uint8 type;     // Typ der Nachricht, z.B 1=SetVolocity, 2=GetVelocity, ...
    uint16 length;  // Länge der Nachricht inkl. Nutzdaten
}

Pakete dieser Art werden für Funktionen genutzt die Werte auf Bricks/Bricklets setzen, sowie für Antworten von Bricks/Bricklets auf Funktionsaufrufe und für Signale (Callbacks).

Aufbau

Die Bindings bestehen aus einer IPConnection und Klassen/Funktionen für jedes Brick/Bricklet. Die IPConnection ist für die Kommunikation mit dem Brick Daemon zuständig, Die Klassen/Funktionen der Bricks und Bricklets nur kleine autogenerierte Hilfsfunktionen, welche Daten vom Benutzer in ein passendes Format für die IPConnection bringen und antworten von der IPConnection wieder passend umwandeln, sodass sie an den Benutzer zurück gegeben werden können.

Der minimale Aufbau einer IPConnection besteht minimalst aus den Funktionen create (oft Konstruktor) und add_device. Vollständige Bindings haben auch noch die Funktionen enumerate, join_thread und destroy. In Pseudocode könnte sie wie folgt aufgebaut sein:

IPConnection(host, port):
   create socket to port 4223
   start recv loop and callback loop thread
add_device(uid):
   send message GetStackID: {0, 255, 12, uid} // 0 = Broadcast Adresse (Nachricht geht an alle Bricks/Bricklets), 255 = Typ GET_STACK_ID, Länge 12 Byte, uid des devices
   wait for answer with type 255
   if answer arrives:
       save data from answer // Firmware Version, Device Name, Stack ID
       add device to list of devices
thread recv_loop():
    while True:
        get data from socket
        get device correspondig to data by stack id // Hier wird die Stack ID genutzt die bei add_device geholt wurde
        call function of device corresponding to type // Hier werden die Funktionen der Bricks/Bricklets aufgerufen

Dies ist nur ein grober Überblick über den Aufbau einer IPConnection, es sollte aber reichen um mit Hilfe der schon vorhandenen IPConnections einen Einstieg in die Materie zu finden.

Generierung

Da es einfach zu aufwändig wäre, die kompletten Bindings für jedes Brick/Bricklet für jede Programmiersprache händisch auszuprogrammieren, wird alles bis auf die IPConnection generiert. Die Generatoren für die vorhandenen Programmiersprachen können in https://github.com/tinkerforge/generators gefunden werden. Eine Beschreibung der Kommunikationspakete für jedes Brick/Bricklet findet sich im generators/configs Ordner. Die configs sind direkt in Python geschrieben, um das Einlesen zu erleichten. Das Paket für die GetVelocity Funktion vom Servo Brick sieht z.B. wie folgt aus:

com['packets'].append({
'type': 'method', 
'name': ('GetVelocity', 'get_velocity'), 
'elements': [('servo_num', 'uint8', 1, 'in'),
             ('velocity', 'uint16', 1, 'out')], 
'doc': ['bm', {
'en':
"""
Returns the velocity of the specified servo as set by :func:`SetVelocity`.
""",
'de':
"""
"""
}] 
})

com['packets'] ist eine Liste, welche ein dict für jedes Paket beinhaltet. Die Stelle des dicts in der Liste ist die Stack ID. Folgende keys sind im dict vorhanden:

  • type: Typ des Pakets, möglich: method und signal
  • name: Name der Funktion in CamelCase und mit Unterstrich
  • elements: Nutzdaten des Pakets und Parameter der Funktion, angegeben sind Name, Variablentyp, Größe und Richtung. Elemente mit Richtung 'in' werden zum Brick/Bricklet gesendet und Elemente mit Richtung 'out' werden vom Brick/Bricklet zum brickd gesendet.

Für jedes dieser Pakete muss vom Generator eine Funktion erzeugt werden, die vom späteren Nutzer der Bindings aufgerufen werden kann (z.B. für C# hier: https://github.com/Tinkerforge/generators/blob/master/csharp/generate_csharp_bindings.py).

Erstellen von eigenen Bindings für eine neue Programmiersprache

Um Bindings für eine neue Programmiersprache zu erstellen, muss also eine IPConnection ausprogrammiert werden und ein Generator für die configs geschrieben werden. Ein grober empfohlener Ablauf dafür sieht wie folgt aus:

  • Um überhaupt erstmal einen Anfang zu finden, bietet es sich an, mit einem Minimalprogramms anzufangen, welches einen Socket zum Brick Daemon öffnet, eine GET_STACK_ID Nachricht raussendet, auf eine Antwort wartet und diese wieder ausgibt. Mit der Stack ID in der Antwort kann dann auch schon eine Funktion von einem Brick aufgerufen werden.
  • Danach sollte eine minimale IPConnection erstellt werden, ähnlich wie oben im Pseudocode. Dabei bitte genau den Aufbau der IPConnections in den anderen Sprachen angucken! Der grundsätzliche Aufbau sollte in allen Sprachen in etwa ähnlich aussehen können.
  • Dann macht es Sinn, sich eins der komplexeren Bricks oder Bricklets rauszusuchen und für dieses die komplette API auszuprogrammieren, so dass sie mit der IPConnection sprechen kann. Dabei ist es wichtig, dass alle möglichen Typen von Parameter, Methoden/Callbacks, Ein- und Ausgabe usw. abgedeckt werden.
  • Erst wenn ein Brick/Bricklet über die händisch programmierten Bindings komplett gesteuert werden kann, sollte man sich um die vielen möglichen Problemfälle der IPConnection und der Kommunikation mit dem brickd kümmern. Bedenke: Es können beliebig viele Bricks/Bricklets geaddet werden, es können API-Funktionen in Callbacks aufgerufen werden, der Benutzer der Bindings kann diese mit vielen Threads gleichzeitig benutzen etc. Bei der Absicherung der Kommunikation mit Mutexes und dem Aufbau der recv und callback loops ist es wieder empfehlenswert, sich die vorhandenen Bindings genau anzugucken! Dort sind diese Probleme schon gelöst worden.
  • Erst wenn die obigen Punkte abgearbeitet sind, macht es Sinn, mit Autogenerieren anzufangen. Dazu kann einer der vorhandenen Generatoren genommen und solange abgeändert werden, bis er den vorher händisch geschriebenen Quellcode für eines der Bricks/Bricklets generiert. Dabei macht es natürlich Sinn, den Generator einer möglichst ähnlichen Programmiersprache umzuschreiben. Zum Beispiel würde sich der Java Generator anbieten, um ihn auf C# umzuschreiben.

Generieren von Dokumentation für neue Bindings

TODO