Hallo, ich möchte die Kommunikation mit einem IC in .NET/C# abbilden. Der generelle Protokoll-Frame selbst ist relativ simpel: Länge, Kommando, kommandospezifische Daten, CRC-Prüfsumme. Die Antwort des ICs verwendet den gleichen Aufbau mit dem Unterschied, das zwischen Kommando-Byte und den Daten ein Statusbyte eingefügt ist, um Fehler zu signalisieren: Länge, Kommando, Status, kommandospezifische Daten, CRC-Prüfsumme. Knackig wird die ganze Sache dadurch, dass die kommandospezifischen Daten unterschiedlich aufgebaut sein können. Das heisst beispielsweise, dass die Daten in Abhängigkeit vom Kommando mal als 8-Bit- und mal als 16-Bit-Werte zu interpretieren sind und ggf. auch Kombinationen daraus auftreten (8/16-Bit in einem Kommando). Die Anzahl der Daten kann auch variieren, das heisst alleine am Kommando-Byte kann man die Länge des gesamten Frames nicht immer eindeutig festmachen. Wie geht man nun am besten vor? Die ersten Versuche habe ich mit simplen Byte-Arrays gemacht, hat auch soweit funktioniert. Jedes Kommando wird in einer Funktion bearbeitet, anhand der übergebenen Parameter wurde die Größe des Byte-Arrays berechnet und dieses danach gefüllt. Einer der Nachteile war, dass man die Größe explizit berechnen muss, bevor man die "eigentliche" Arbeit machen kann. Ich frage mich nun, ob der Ansatz an sich geschickt ist, oder ob sich das beispielsweise mit ArrayList bzw. HashTable besser lösen lässt. Bei beiden kann man einfach Elemente anfügen, die Größe ändert sich entsprechend dynamisch und die Vorberechnung der Größe entfällt. Diese benötige ich zwar für das Längenbyte, aber ich würde eher dazu tendieren, das Kommando an sich zu behandeln, und Längenbyte sowie Checksumme erst am Ende (in einer separaten Funktion) um das eigentliche Kommando zu wrappen, da Längenbyte und Prüfsumme nichts mit dem Kommando zu tun haben. Die HashTable hätte zudem den Vorteil, dass man den jeweiligen Werten auch die entsprechenden Feldnamen des Kommandos bzw. dessen Parametern geben kann, was die Übersicht vereinfacht. Hier muss ich aber prüfen ob die Reihenfolge immer eingehalten wird. Umgekehrt wird es wohl etwas schwieriger aus ArrayList/HashTable wieder ein normales Byte-Array zu machen, welches dem seriellen Port übergeben werden kann. Wie löst man sowas geschickt und flexibel für Erweiterungen? Ralf
Byte-Array zusammenbauen mache ich immer per MemoryStream (einfach rein-schreiben und anschließen .ToArray() aufrufen) Ich würds vermutlich einfach so bauen: Für Senden eine Helfer-Funktion wie z.B. sendMessage(byte commandId, byte[] payload) basteln. Beim Empfangen: Nachricht parsen, und an eine Funktion übergeben:
1 | void handleMessage(byte commandId, byte[] payload) |
2 | {
|
3 | if (commandId==XYZ) |
4 | {
|
5 | handleCommandXYZ(payload); // handleCommandXYZ darf dann den payload irgendwie zerflücken. |
6 | }
|
7 | ....
|
8 | }
|
...sofern man nicht gerade tausende oder riesige Nachrichten bekommt, dürfte das ständige Kopieren von Daten praktisch nicht bemerkbar sein.
@ bluppdidupp: > per MemoryStream Danke, ich les mir das mal durch. > ...sofern man nicht gerade tausende oder riesige Nachrichten bekommt, > dürfte das ständige Kopieren von Daten praktisch nicht bemerkbar sein. Sehe ich auch so, da bei diesem IC die UART-Übertragungsrate max. 115200Bd ist. Im SPI-Modus isser natürlich schneller, aber da ist dann eh das SPI-Interface zwischen Rechner und IC der Flaschenhals. Ralf
Hallo, die Schichtenmodelle haben schon ihre Vorteile. Start, Länge, CRC gehören in eine andere Ebene wie die Nutzlast. Z.B. muss man sich mit der Bedeutung der einzelnen Bytes erst beschäftigen, wenn eine Message mit CRC korrekt empfangen wurde, bei einem Fehler fordert die Protokoll-Ebene selbstständig ein erneutes Senden an. In embedded Systemen würde ich daher den Empfang bis zur CRC-Auswertung im Treiber lösen und nur fertig empfangene Messages an die nächsthöhere Ebene übergeben. Unter Windows will man natürlich Treiber-Programmierung vermeiden, da würde ich 3 Ebenen verwenden, den Windows-Treiber, auf den man keinen Einfluss hat, den Protokolltreiber und die Nutzlastauswertung. Dadurch kann man z.B. den Protokolltreiber wechseln ohne an der Nutzlast was ändern zu müssen. Auf die Art schreibe ich Protokolle, die je nach Einstellung mit einer seriellen Schnittstelle, Ethernet oder USB betrieben werden. Ich erstelle für Antwort-Messages normalerweise eine Klassen-Hierarchie mit einer Grundklasse und Ableitungen für jede Kommandoart. D.h. die Message ist ein Objekt. Verschiedene abgeleitete Klassen können durchaus mal ein Array, eine Struktur usw. oder eine Zusammensetzung davon als Felder haben. Für ein bestimmtes Kommando z.B. ein Zeit-Record mit Jahr (word), Monat, Tag usw. (byte), danach 10 Messwerte (IEEE float), und so weiter. Gruss Reinhard
Hallo Reinhard, das hört sich gut an. Den IC selbst wollte ich ebenfalls als Objekt implementieren, da ich evtl. mehrere gleichzeitig betreibe. Die Kommandos und deren Antworten als Objekte zu implementieren war auch ein Gedanke, den ich hatte, aber dazu fehlt mir noch ein ausgereifter Ansatz. Ich werd mich mal ransetzen und notieren, was denn ein Objekt "Kommando" so an Eigenschaften hat. Vielleicht komm ich dann der Lösung einen Schritt näher :) Ralf
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.