@Vogon:
Schönes Beispiel, aber relativ kompliziert. Das ganze ist
zwar recht universell, damit aber auch relativ aufwändig zu
implementieren. Insbesondere das variable Format von Adressen
und Datenlänge ist dabei denke ich ein Hindernis. Ob man das
RS232-Handshake implementiert, würde ich jetzt erstmal als
Teilproblem in die Übertragungsschicht schieben und da als
Option belassen. Sprich: erstmal nein, irgendwann kann mans
mal einbauen.

Ich möchte jetzt mal auch einen Vorschlag zum Format machen
und zwar jeweils zwischen den verschiedenen Schichten.

Anmerkung: Ich gehe davon aus, das die Länge eines Datenpaketes
implizit durch die Übertragungsschicht erkannt wird. Nachdem
jedes gesparte Byte bei der Übertragung ein gutes Byte ist,
ist die Länge deshalb auch nicht explizit Teil des Pakets.
In einer realen Implementierung wird die Länge vmtl immer in
der Datenstruktur stehen (als Zusatzinformation), nur taucht
sie in der Definition nicht auf und wird ergo auch nicht
explizit übertragen.

Anmerkung 2: Ich habe auch keinen speziellen Header vorgesehen.
Dieser ist in meinen Augen nur Overhead, da über das gewählte
Medium ja nur korrekte Nachrichten empfangen werden. Immerhin
ist das ganze fest verkabelt und man kennt die Gegenstelle. Wenn
ein device (aus welchen Gründen auch immer) wirklich einen Header
braucht, kann es diesen Header immer noch als Teil seines
Übertragungsprotokolls definieren.


Schicht 1 - Seriell

Auch Schicht 1 werden immer Datenpakete einer bestimmten
Länge ohne jede Bedeutung empfangen. Im folgenden mal
ein Beispiel, wie das seriell machbar ist.

Steuerzeichen
Zusätzlich zu den normalen Daten müssen Kontrollzeichen
übertragen werden. Da alle verfügbaren Zeichen (0..255)
schon belegt sind, werden einige davon als Kontrollzeichen
'abgezweigt'. Das ganze funktioniert ähnlich dem von PicNick
genannten 'Byte Stuffing':

Sei das '\0xff' das Kontrollzeichen, d.h. allen eingelesenen
Bytes mit dem Wert 0xff bzw. 255 folgt kein normales Datenbyte,
sondern ein Kontrollbyte. Im Gegensatz zum Byte Stuffing ist
dieses Zeichen aber eindeutig, d.h. das 'verschattete' Daten-
byte wird nicht durch sich selbst, sondern durch ein anderes
Zeichen übertragen.

Um also im obigen Beispiel ein Datenbyte 0xff zu übertragen,
wird das Kontrollbyte 0xff gefolgt von (z.B.) 0xfe übertragen.
Der Empfänger weis, das die Steuersequenz "0xff, 0xfe" dem
Datenzeichen 0xff entspricht und korrigiert den eingehenden
Datenstrom entsprechend.

Framing
Wie erkennt jetzt der Empfänger, wann eine Nachricht zu Ende
ist, bzw wie lange ein Datenpaket ist? Die Lösung ist Framing.
Dabei wird jede Nachricht von einer Anfangs- und einer End-
Kontrollsequenz begrenzt. Alle Datenbytes dazwischen gehören
zur Nachricht. wie lange die Nachricht genau ist, weis der
Empfänger erst, wenn er die Endsequenz empfangen hat.

Beispiel
Sei "0xff, 0xfe" ein Datenbyte mit dem Wert 0xff.
Sei "0xff, 0x00" die Startsequenz.
Sei "0xff, 0x01" die Endsequenz.

Der Sender will folgendes Paket übertragen:
<0, 0xff, 1>
Nach dem Framing und dem 'escaping' der Datenbyte mit 0xff
entsteht folgender Datenstrom:
<0xff, 0, 0, 0xff, 0xfe, 1, 0xff, 1>

Der Empfänger empfängt ein Startsequenz, erstellt einen leeren
Empfangspuffer und empfängt eine '0' die gespeichert wird. Dann
empfängt er eine Kontrollsequenz "0xff, 0xfe" und fügt dem
Empfangspuffer eine 0xff hinzu. Die folgende wird ganz normal
gespeichert. Dann empfängt er eine Endsequenz und gibt einen
Puffer mit der Länge 3 weiter.

Warum nicht Bytestuffing
Im Gegensatz zum Bytestuffing ist in diesem Verfahren das
Kontrollzeichen eindeutig. Das bietet einen Vorteil bei der
Wiederaufnahme unterbrochener Verbindungen. Wenn ich mein
Kabel aus- und wieder einstecke, dann bin ich beim Bytestuffing
nie sicher, ob ein empfangenes Kontrollzeichen wirklich ein
Kontrollzeichen oder ein Datenzeichen mit demselben Wert ist.

Warum Framing
Derselbe Grund wie oben: Wird ein Kabel im laufenden Betrieb
eingesteckt, dann können sich die Module anhand der eindeutigen
Start-Kontrollsequenz problemlos wieder synchronisieren.


Schicht 2+3 - Router+Verbindung

Empfangen/gesendet wird ein Datenpaket der Länge 'n', gebunden
an ein festgelegtes 'Segment' bzw. Übertragungs-device.
Code:
Byte 0: Message Type
        Bestimmt die restliche Struktur der Nachricht
          0x00 = User level msg, verbindungslos
          0x01 = User level msg, verbindungsorientiert, request
          0x02 = User level msg, verbindungsorientiert, response
          0x03 = RouterMsg (z.B. dynamic Routing Information)
          0x04 = RouterMsg (z.B. Adressvergabe)
          ... to be continued

Für Userlevel messages (Typ=00, 01, 02) gehts wie folgt weiter:
Byte 1: Receiver, subnet address
Byte 2: Receiver, node address

Byte 3: Sender, subnet address
Byte 4: Sender, node address 

Byte 5: Session number, low byte   (nur für verbindungsorientierte messages)
Byte 6: Session number, high byte  (nur für verbindungsorientierte messages)

Byte 5/7..n: Daten
Schicht 4 - Diensteschicht

Empfangen wird ein Datenpaket mit Länge, Addresse und evtl. Session number.
Responsenachrichten (Typ 0x02) werden direkt der entsprechenden 'Session'
zugeordnet. uCs müssen Responsenachrichten nicht unterstützen, können aber.
Nachrichten vom Typ 00 bzw. Typ 01 haben immer folgendes Format für den
Datenteil:

Byte 0: Group (Dienst)
Byte 1: Command
Byte 2..n: Command specific Data

Ein Teil der Kommandogruppen ist vordefiniert und wird von der GUI
unterstützt, der Rest der Kommandogruppen kann vom User frei definiert
werden, z.B. für eigene Kommandos.

Denkbare allgemeine Dienste wäre z.B.

1.) Infrastructure
Definiert Kommandos zum Abfragen der Namen von Knoten, zum SW-Reset
eines Knotens, zur Hardware, usw. Diese Kommandogruppe sollte in jedem
(physikalischen) Knoten implementiert sein. Damit lässt sich z.B.
herausfinden, das es einen Knoten 'RNBFRA - Roboter 1' mit den logischen
Knoten 'Motorsteuerung', 'US-Sensorsteuerung' und 'Liniensensor' gibt.
Auch Kommandos zur Überwachung der Knoten (z.B. ping, heartbeat) wären
hier gut aufgehoben.

2.) Sensors
Definiert Kommandos zum Allgemeinen Abfragen einfacher Sensoren sowie
(evtl.) zur Selbstbeschreibung von Sensoren.

3.) Motor Control
Definiert Kommandos um Allgemein eine Motorsteuerung zu kontrollieren,
z.B. Vorwärts(100cm), Rechts drehen(50grad), usw.

4.) Debugging
Definiert Kommandos zum Entwickeln neuer Komponenten. Funktionen dieses
Dienstes könnten z.B. 'virtuelle Konsolen' für den jeweiligen Knoten sein.
Auch ein 'Notstop' bzw. 'Pause' Kommando würde hier gut passen, genauso
wie das auslesen bestimmter Speicherstellen, ...

5.) Synchronised Variables
Definiert Client- und Serverfunktionalität für Synchronisierte Variablen.
Client-Befehle z.B. GetVar, SetVar, CreateVar.



Ich hoffe das war jetzt einigermaßen gut beschrieben. Wenn irgendwo noch
Fragen offen sein sollten: stellt sie jetzt.


Wie gehts weiter:
Ich werde zum obigen Protokoll "mal schnell(tm)" einen Prototypen hacken,
der das ganze demonstriert und mit dem man auch Teilbereiche wie das
Adress-DHCP und das dynamische Routing ausprobieren kann.

Sobald wir uns auf ein Protokoll für Schicht 3 bzw. Schicht 4 geeinigt haben,
können wir konkrete Schnittstellen für z.B. Java, C, Bascom definieren und
dann die Schichten entsprechend implementieren.


ciao,
Georg