Forum: Mikrocontroller und Digitale Elektronik UART Kommandos elegant und schön auswerten, Beispiele?


von Suchender (Gast)


Lesenswert?

Hallo Forum,
ich schicke über UART Kommando's, also z.B. Klartext oder auch einige 
Bytes als Prefix + einige Bytes oder auch lesbare Zahlen als Parameter 
an einen Atmega328. Je nach Kommando soll dann eine Funktion ausgelöst 
werden, die dann Led's mit diversen Mustern ansteuern.

Ich krieg das auch hin, aber es ist ein unelegantes Gewürge durch 
If-Schleifen. Ich bin Hobbist und Anfänger und kenne vielleicht auch die 
richtig schönen Stringfunktionen noch nicht.

Was ich suche, ist ein Program, welches das mal so richtig elegant und 
schön löst, wo man sich was von abkucken kann. Google wirft zumindest 
mit meinen Suchbegriffen immer was knapp daneben aus.

Danke für jeden Link.

Grüsse

von Julian B. (julinho)


Lesenswert?

Was Du suchst nennt sich Parser.

von DirkZ (Gast)


Lesenswert?

Suchender schrieb:
> If-Schleifen

wenn schon: if-Abfrage

if-Abfragen kann man u.U. durch switch-case ersetzen

von Suchender (Gast)


Lesenswert?

Danke Euch beiden, aber ich glaub, was mir Parsen so an Beispielen 
schickt, ist nun schon wieder eine Stufe zu komplex, und scheint auch 
auf lesbaren Text zu gehen. Und Switch-case kenne ich, trifft es aber 
auch nicht ganz.

Um vielleicht etwas tiefer in ein Beispiel einzusteigen:

Ich empfange eine Hex-Folge, die mit 68 E0 E0 68 anfängt und irgendwo 
mit 16 68 aufhört mit einigen Bytes dazwischen mit unterschiedlicher 
Länge. Für Interessierte - das ist ein KNX FT1.2 Protokollmitschnitt.

Jetzt geh ich da mit meinen if's dran und sage so etwas wie:

if (a[0]==x68) {
    if (a[1]==xE0) {
         if (a[2)==xE0)  {   // ab hier Annahme, dass valides Kommando
            dann liess Bytes solange nicht if (a[x]==16)
                                           ... if (a[x+1]==68) ist.

Das ist grausig. Das muss und wird einfacher und besser gehen. Nur fehlt 
mir grad die erleuchtende Idee.

Kann mir jemand Hinweise geben, wo ich weiterforschen kann?
Danke

Grüsse

von Konrad S. (maybee)


Lesenswert?

Du kannst für den Start die if-Bedingungen zusammenfassen:
1
if ( a[0]==0x68 && a[1]==0xE0 && a[2]==0xE0 ) { ...
Wie es weitergeht kann ich dir nicht sagen, da ich das KNX-Protokoll 
nicht kenne.

von Lutz H. (luhe)


Lesenswert?

Du könntest die If Bedingungen mit einem "und" verknüpfen und in eine 
Zeile scheiben

etwa so:
if   ( (a[0]==x68) && (a[1]==xE0) && (a[2)==xE0) )
  {  ...}

schade, nur Zweiter.  kondadswar ein paar Sekunden schneller  :-)

von DirkZ (Gast)


Lesenswert?


von Suchender (Gast)


Lesenswert?

Danke, Konrad und Lutz. Super naheliegend, da schämt man sich ja fast.

Dirk, interessante Liste, werd ich mal checken. Merci auch!

von Falk B. (falk)


Lesenswert?


von Purzel H. (hacky)


Lesenswert?

Am besten ist eine Zustandsmaschine, die aufgrund der Konstruktion nicht 
wartet, und daher jedes empfangene byte nur einmal auswertet.

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Mal ne Frage an die C Experten:


Konrad S. schrieb:
> if ( a[0]==0x68 && a[1]==0xE0 && a[2]==0xE0 ) { ...

Setzt das nicht voraus das dein Empfangspuffer mit diesem Zeichen 
anfängt?

Ist:

pos = (strstr (ReceiveBuffer, KNXcommand));
if (pos) {....

nicht einfacher?

von Stefan Noack (Gast)


Lesenswert?

wenn es 1-byte-Befehle sind, wie bei vielen einfachen Protokollen, dann 
reicht eine Sprungtabelle (oder halt ein switch/case in C), z.B. so 
hier:
1
command_loop:
2
3
    ; ...
4
    ; befehl einlesen
5
    ; ...
6
7
    ldi     ZH, high(command_jump_table)
8
    ldi     ZL, low(command_jump_table)
9
    add     ZL, command
10
    adc     ZH, zero    
11
    icall  
12
    
13
    rjmp    command_loop
14
15
command_jump_table:
16
    rjmp    command_flush               ; 0x00
17
    rjmp    command_try_set_sid_count   ; 0x01
18
    rjmp    command_mute                ; 0x02
19
    rjmp    command_try_reset           ; 0x03
20
    rjmp    command_try_delay           ; 0x04
21
    rjmp    command_try_write           ; 0x05
22
    rjmp    command_try_read            ; 0x06
23
    rjmp    command_get_version         ; 0x07

von Svenska (Gast)


Lesenswert?

Ich würde auch den Zustandsautomaten bevorzugen. Damit lässt sich recht 
einfach ein Parser schreiben.

Realisiert sich mit einem switch/case für den Zustand recht gut. Jedes 
Byte wird dann einmal durch den Automaten gereicht, der dann weiß, was 
er damit zu tun hat (z.B. in einen Datenpuffer schreiben, Zustand 
wechseln, ...). Damit brauchst du nicht sicherstellen, dass das erste 
Zeichen von deiner UART auch der Startbefehl ist.

Die Sprungtabelle ist ein Spezialfall davon.

von Konrad S. (maybee)


Lesenswert?

Der Rächer der Transistormorde schrieb:
> Setzt das nicht voraus das dein Empfangspuffer mit diesem Zeichen
> anfängt?

Mein Vorschlag orientierte sich einfach nur an der Notation im zweiten 
Post des TE.

Und nein, der Empfangspuffer muss nicht mit dieser Sequenz beginnen. 
Z.B. mit:
1
char *a = ...
2
if ( a[0] ...
kann die Sequenz irgendwo im Empfangspuffer, aber sie muss im Speicher 
stehen. Mit einem Zustandsautomaten ist das nicht nötig.

Ist wie immer: kommt drauf an! ;-)

von Suchender (Gast)


Lesenswert?

Hallo,
erstmal herzlichen Dank für alle Beiträge!

Vermutlich ist die Zustandsmaschine eine gute Lösung, aber nach kurzer 
Lektüre von
http://www.embedded.com/design/embedded/4024591/State-machine-shortcuts
hab ich mich dran erinnert, dass das Hobby ist. Bin aus dem Studium noch 
durch das Thema Operations Research etwas verbrannt.

Ich glaube, ich werde mich mal an einer Kombination von Falk's Routine 
mit den obigen Tips versuchen. Vermutlich hilft mir auch die Idee des 
Rächers der Transistormorde weiter. Der USART schreibt mir in einen 
Ringpuffer, d.h. dort kann ich auch mit Konrad's Ansatz zugreifen.

Grüsse und Dank

von Konrad S. (maybee)


Lesenswert?

Suchender schrieb:
> Der USART schreibt mir in einen
> Ringpuffer, d.h. dort kann ich auch mit Konrad's Ansatz zugreifen.

Wobei es passieren kann, dass z.B. das erste Byte der Startsequenz am 
Ende des Ringpuffers steht, das zweite Byte am Anfang des Ringpuffers.

von Suchender (Gast)


Lesenswert?

Ja, aber dafür hat Peter Dannegger den ROLLOVER erfunden :-).

von Simon K. (simon) Benutzerseite


Lesenswert?

Du könntest die Befehle (samt Funktionszeiger) in einer Tabelle ablegen 
und zwar geordnet. Während dann die Zeichen nach für nach reinkommen, 
kannst du sozusagen mit jedem Zeichen die Auswahl an noch verbleibenden 
Befehlen eingrenzen.
Wenn das Array alphabetisch geordnet ist, dann heißt das, dass du den 
Zeiger immer weiterschieben kannst, bis du einen Treffer an der 
aktuellen Stelle gefunden hast. Alle danachfolgenden Befehle im Array 
sind dann nur noch möglich.

So oder so ähnlich würde ich das lösen. Damit sollte sich der 
Suchaufwand ziemlich linear verhalten.

von Timmo H. (masterfx)


Lesenswert?

Also ich habe mir für "lesbare" Kommandos mal eine "Debug-Konsole" 
ausgedacht... das lässt sich natürlich auch auf andere Protokolle 
anpassen.
Ich weiß nicht ob das so völlig bugfrei war, hat mir aber bisher ganz 
gute Dienste geleistet. getch, putchar, printf muss man ggf. noch 
anpassen, jenachdem was man so verwendet für die uart Ein/Ausgaben.

console.c
1
#include <stdio.h>
2
#include <stdlib.h>    
3
#include <conio.h>    
4
5
#include "console.h"
6
7
#define MAX_ARGS  10
8
/* Kommando-Struktur, einfach nach Belieben erweitern
9
 *
10
 * {"BEFEHL ... Kurzbeschreibung\n", funktionsname, "Syntaxhilfe\n"},
11
 *         ^Leerzeichen zwischen BEFEHL und "..." zwingend notwendig!!!
12
 * Prototypen der Funktionen in console.h eintragen!
13
 */
14
const debug_command_t f[] = {
15
  {"add ... add two numbers\n", cmd_add, "add <number> <number>\n"},
16
  {"mul ... multiply two numbers\n", cmd_mul, "mul <number> <number>\n"},
17
  {"help .. Command overview/help\n", cmd_help,"help [command]\n"},
18
  {"?  .... Command overview/help\n", cmd_help,"? [command]\n"},
19
  {"exit .. exit\n", cmd_exit, "x\n"}
20
};
21
22
#define NO_COMMANDS (sizeof(f)/sizeof(debug_command_t))  
23
#define COMMAND_BUFFER 80  /* Eingabepuffer */
24
25
int exit_debug = 0;      /* Wird durch cmd_exit auf 1 gesetzt */
26
27
/* Hauptfunktion, wird verlassen wenn exit_debug == 1
28
 *
29
 */
30
void debug(){
31
  char       debug[COMMAND_BUFFER];  /* Buffer für Debug-Kommando */
32
  unsigned char   i;
33
  
34
    printf("\n========= Console ==========\n");
35
    printf("Enter '?' for command overview\n");
36
  while(1){
37
    i = 0;
38
    
39
    printf("\n# ");    
40
           
41
    do{
42
      debug[i] = getch();
43
      putchar(debug[i]);      /* Echo */
44
      if(debug[i] == '\b' && i){  /* Backspace? */
45
        printf(" \b");      /* Leerzeichen ausgeben + Cursor zurück*/
46
        i--;          /* letztes Zeichen löschen */
47
      }
48
      else{
49
        i++;  
50
      }
51
      if(i >= COMMAND_BUFFER -1){  /* Kommando zu lang */
52
        i = 0;
53
        printf("\nCommand too long\n");
54
        continue;  
55
      }
56
    /* Schleife bei ENTER verlassen */  
57
    }while(debug[i-1] != '\r' && debug[i-1] != '\n');
58
    
59
    if(i == 1)      /* Kein Kommando eingegeben */
60
      continue;
61
    debug[i-1] = '\0';  /* Stringterminierung */
62
    putchar('\n');
63
    
64
    if(i > 0)
65
      parse_and_call_cmd(debug);  /* Kommando suchen und ausführen */
66
    if(exit_debug)            /* Wenn exit eingegeben */
67
      break;
68
  }  
69
}
70
71
/* Addiert zwei Zahlen */
72
void cmd_add(char argc, char **argv){
73
  int no1, no2;
74
  if(argc == 3){
75
    no1 = atoi(argv[1]);
76
    no2 = atoi(argv[2]);
77
    printf("%d + %d = %d", no1, no2, no1 + no2);
78
  }
79
}
80
81
/* Multipliziert zwei Zahlen */
82
void cmd_mul(char argc, char **argv){
83
  int no1, no2;
84
  if(argc == 3){
85
    no1 = atoi(argv[1]);
86
    no2 = atoi(argv[2]);
87
    printf("%d * %d = %d", no1, no2, no1 * no2);
88
  }
89
  else
90
    cmd_help(2,argv-1);    /* Syntax-Ausgabe */
91
}
92
93
/* beendet die Debug-Konsole */
94
void cmd_exit(char argc, char **argv){
95
  exit_debug = 1;
96
}
97
98
void cmd_help(char argc, char **argv){
99
  unsigned char  i;
100
  unsigned char cmd_pos;
101
102
  if(argc == 2){            /* Kommandospez. Hilfe? */
103
    cmd_pos = is_dbg_command(argv[1]);  
104
    /* Debug-Kommando? Ja => Syntax ausgeben */
105
    if(cmd_pos != NO_COMMANDS + 1 ){  
106
      printf("\n[..] - optional argument\n<..> - required argument\n");
107
      printf("\n%sSyntax: %s", f[cmd_pos].nptr, f[cmd_pos].syntax);
108
    }
109
    else  /* Kommando nicht gefunden */
110
      printf("Command not found\n");  
111
  }
112
  else{    /* Befehlsübersicht ausgeben */ 
113
    for(i = 0; i < NO_COMMANDS ; i++)
114
      printf(f[i].nptr);
115
  }
116
}
117
118
/* Extrahiert und zählt die Argumente, sucht das Kommando über
119
 * is_dbg_command und führt es dann aus.
120
 *
121
 */
122
void parse_and_call_cmd(char *cmdptr){
123
  unsigned char argc=0, cmd_pos;
124
  
125
#ifndef DYN_ARGS
126
  unsigned char *argv[MAX_ARGS];
127
#else
128
  unsigned char **argv = NULL;
129
  argv = (unsigned char **)malloc(sizeof(char **));
130
#endif
131
  
132
  argv[argc++] = cmdptr;  /* Kommando selbst in argv[0] */
133
  while(1){
134
    cmdptr++;
135
    if(*cmdptr == ' '){
136
      *cmdptr = '\0';  
137
      while(*++cmdptr == ' ');  /* Leerzeichen überspringen */
138
#ifdef DYN_ARGS
139
      argv = realloc(argv, (argc+1)*sizeof(char **));
140
#else
141
      if(argc > 9){
142
        printf("\nToo many arguments!\n");
143
        return;  
144
      }        
145
#endif
146
      argv[argc++] = cmdptr;
147
    }
148
    else if(*cmdptr == '\0'){
149
      break;
150
    }
151
  }
152
  cmd_pos = is_dbg_command(argv[0]);
153
  if(cmd_pos == NO_COMMANDS + 1)          /* Befehl nicht gefunden */
154
    printf("\nUnknown command\n");
155
  else{
156
    (f[cmd_pos].fptr)(argc, (char **)argv); /*  Funktion aufrufen */
157
  }
158
#ifdef DYN_ARGS
159
  free(argv);
160
#endif
161
}
162
163
/* Sucht das Kommando in dem debug_commands-Struct und liefert
164
 * die Position im Struct zurück
165
 */
166
char is_dbg_command(char *cmdptr){
167
  unsigned char i=0, j=0, error;
168
  
169
  while(i < NO_COMMANDS ){  /* Alle Kommandos durchsuchen */
170
    error = 0;
171
    while(f[i].nptr[j] != ' '){  
172
      if(f[i].nptr[j] != cmdptr[j]){
173
        error = 1;
174
        break;    /* Abweichung => nächsten Befehl testen */
175
      }
176
      j++;    
177
    }
178
      if(!(error == 0 && (cmdptr[j] == ' ' || cmdptr[j] == '\0') )){
179
      error = 1;
180
      i++;
181
      j=0;
182
    }  
183
    else        /* Kommando gefunden, error = 0 */
184
      break;
185
  }
186
  if(error==0)
187
    return i;
188
  else
189
    return NO_COMMANDS+1;
190
}

console.h
1
/* Prototypen für die Kommandos */
2
void cmd_add(char argc, char **argv);
3
void cmd_mul(char argc, char **argv);
4
void cmd_help(char argc, char **argv);
5
void cmd_exit(char argc, char **argv);
6
7
void debug();
8
void parse_and_call_cmd(char *cmdptr);
9
char is_dbg_command(char *cmdptr);
10
11
typedef void generic_f (char argc, char **argv); 
12
13
typedef struct{
14
    char  *nptr;
15
    generic_f  *fptr;
16
    char  *syntax;
17
}debug_command_t;

: Bearbeitet durch User
von Der Rächer der Transistormorde (Gast)


Lesenswert?

Konrad S. schrieb:
> Wobei es passieren kann, dass z.B. das erste Byte der Startsequenz am
> Ende des Ringpuffers steht, das zweite Byte am Anfang des Ringpuffers.

Noch mal eine Frage vom C Novizen.

Wozu braucht man einen Ringpuffer wenn das Protokoll bekannt ist?

Sind nicht zwei (oder mehr) Protokoll Buffer besser? Die Empfangsroutine 
muss doch so oder so jedes Zeichen vom Uart in einen Buffer schreiben. 
Da kann Sie doch gleich auf ETX (oder was auch auch immer) prüfen. Das 
empfangene Byte steht schon im CPU Register und sie muss nur ein 
Bufferflag toggeln (oder hochzählen).


Dann ist die Protokollsequenz vorsortiert im eigenen Buffer mit 
definiertem Anfang.

von Harald M. (mare_crisium)


Lesenswert?

Hallo, Rächer,
das läuft darauf hinaus, einen Teil des Zustandsautomaten in die 
Empfangsprozedur zu verlagern. Wenn der UART-Empfang interruptgesteuert 
ist, dann muss man aufpassen, ob das zeitlich hinhaut. Ansonsten kann 
man das auch so machen, wie Du vorschlägst.
Allerdings hat man dann mehreren Zustandsautomaten am Hals: Einen, der 
die Zeichen direkt nach dem Empfang auswertet und entscheidet, in 
welchem Puffer sie landen, und dann noch welche, die nur die Zeichen in 
den diversen Puffern verarbeiten. Alle diese Automaten müssen 
gegenseitig ihre Zustände beeinflussen. Z.B. um zu verhindern, dass ein 
Kommando, das noch nicht vollständig empfangen wurde, ausgewertet wird, 
weil Teile davon schon im Kommandopuffer stehen (die Kommandos können ja 
mehr als ein Byte lang sein).
Mehrere miteinander verknüpfte Zustandsautomaten in verschiedenen 
Programmteilen - das wird sehr schnell sehr unübersichtlich. Deshalb ist 
es programmiertechnisch praktisch, alles übersichtlich in einem 
Automaten zusammenzufassen. Dann hat man nur einen Programmteil, in dem 
man suchen und korrigieren muss ;-).

Ciao, mare_crisium

von Blackbird (Gast)


Lesenswert?

> ... Alle diese Automaten müssen gegenseitig ihre Zustände beeinflussen.

Nein, eben nicht!
Jeder Zustandsautomat arbeitet autonom, sonst ist es keiner.
Er bekommt die (vollständigen, eventuell mit einem anderen Automaten 
schon aufbereitetetn) Events nacheinander (meinetwegen aus einer Queue) 
und verarbeitet sie schön der Reihe nach. Erst wenn er einen Event 
vollständig abgearbeitet hat, holt er sich den nächsten.

Es können also mehrere Automaten unabhängig voneinander werkeln.


Blackbird

von Blackbird (Gast)


Lesenswert?

Nachtrag:
Ein Event kann (z.B.:) eine (vollständige) Nachricht sein oder ein 
Timeout oder ein Interrupt.
Der Automat kann selbst seine Eingangsqueue pollen oder per Ereignis 
aufgerufen werden.

Blackbird

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Harald M. schrieb:
> Allerdings hat man dann mehreren Zustandsautomaten am Hals:

Hallo Harald und alle anderen.

Schönen Dank für die Antwort, muss dazu sagen das ich mich an die 
Materie herantaste. Vielleicht ist einiges was ich schreibe trivial oder 
Blödsinn oder spinnert.

Sei's drum, ich sehe das so:
Das mit den Zustandsautomaten ist sicher richtig, aber nicht wenn du 
verschiedene - nennen wir es mal-  Hierarchieebenen hast. Dann ist der 
empfangene Datenstrom die eine (eine Übertragungsschicht), das in ihm 
enthaltene Protokoll aber eine ganz andere (verschiedene Bytefolgen mit 
durch die Protokolldefinition intendiertem Informationsinhalt).

Wenn du nun eine state machine auf den Empfangspuffer jagst dann ist 
aber genau anders herum als von dir geschrieben. Das vermischt die 
Ebenen. Du musst sie gleichzeitig behandeln. 1. den Datenstrom und 2. 
die intendierte Information.

Wenn du Punkt 1 in der Empfangsroutine erledigst (wo Sie meiner Meinung 
nach hingehört und mit einem Bufferpointer auch im Interrupt sehr 
Effizient behandelt werden kann.
z.B.

if (ETX) pBuffers++;
else schieb das blöde Byte in den Buffer;

dann hat deine state machine komplette Telegramme vor sich und du die 
Separierung schon erledigt.

Stimmt das oder bin ich auf dem Holzweg?

von Karl H. (kbuchegg)


Lesenswert?

Der Rächer der Transistormorde schrieb:

> Stimmt das oder bin ich auf dem Holzweg?

Stimmt schon.

Viele Wege führen nach Rom.
Hat man UART Routinen vor sich, wie zb die vom P.Fleury so enthalten die 
bereits eine Interrupt-gesteuerte Empfangsroutine samt zugehörigem 
Ringbuffer. Der Ringbuffer hat den Zweck, das restliche Programm von der 
Bürde des UART Überwachens zu befreien und eine kleine Speicherfläche zu 
bieten, so dass das Programm auch mal etwas länger brauchen kann, ohne 
dass gleich Zeichen verloren gehen.

Das ist sein Zweck. Sein einziger Zweck. Nicht mehr.

Da man diese Funktionalität praktisch gesehen in jedem Programm 
gebrauchen kann, macht es durchaus Sinn, das (und nur das) als Baustein 
verwendbar zu haben und alles darüber hinausgehende in darüberliegende 
Software-Schichten zu legen.
Was wiederrum den Nachteil hat, dass man eventuell manche Dinge doppelt 
macht. So speichert der Ringbuffer Zeichen zwischen und die darüber 
liegende Schicht muss dann wieder ihren eigenen Buffer bemühen um daraus 
die Nachrichten zusammenzusetzen (oder eine State-Maschine darauf 
ansetzen)

Du musst jetzt für dich abwägen was dir wichtiger ist. Willst du ein 
universell einsetzbares UART-Modul, dass du auch in den nächsten 10 
Projekten so wie es ist einsetzen kannst, oder willst du die 
ISR-Empfangsroutine so pimpen, dass sie dir das Aufdröseln des Inputs in 
Nachrichten auch gleich mit macht, auch auf die Gefahr hin, dass du 
damit schon im nächsten Projekt nichts mehr anfangen kannst.

Es ist deine Entscheidung, beides ist möglich.

Du wirst dich dran gewöhnen müssen, dass es in der Software mehr als nur 
einen Weg bzw. Möglichkeit gibt und diese verschiedenen Möglichkeiten 
unterschiedliche Vor- und Nachteile haben. Manchmal hat man auch nur die 
Wahl zwischen Pest und Cholera. 'One size fits all' funktioniert halt 
nur bei Baseballkappen, aber selten in Software.

: Bearbeitet durch User
von Suchender (Gast)


Lesenswert?

Genau das ist auch der pragmatische Grund, warum ich einen Ringpuffer 
verwende - code reuse vom Fachmann (Fleury).

Simon und Timmo - genau so etwas hab ich zwischenzeitlich auch gesehen, 
und zwar im Webserver von Ulrich Radig. Das ist sehr strukturiert und 
übersichtlich.

Ich hatte kurz überlegt, ob ich das verwenden soll, hab es dann aber für 
meinen Fall verworfen, und zwar weil ich ein Binärformat auswerte, in 
dem ein Protokolloverhead (mit Adressen, u.a.) von einem 4-Byte Nutzkode 
zu trennen ist. Von diesen 4 Bytes (eigentlich ein KNX 32bit Wert) ist 
dann das erste Byte ein Kommando, und die 3 nächsten jeweils Parameter. 
Da ist ein Switch einfacher, da nur ein uint8_t ausgewertet werden muss.

Ich finde das ist ein recht interessanter Thread geworden, der durchaus 
auch allgemein von Interesse ist. Ergänzend wären jetzt vielleicht 
Hinweise zu Implementierungen dieser "Zustandsautomaten" noch 
interessant - insbesondere, wenn es da möglich sein sollte, die ähnlich 
universell wie die Tabellen von Simon/Timmo aufzubauen.

Danke und Grüsse

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Viele Wege führen nach Rom.

Schon Ok, ist ja nicht nur bei Software so.

Danke auch für die Erklärung mit dem Ringbuffer und Wiederverwendbarkeit 
was man sich eventuell mit dem Nachteil mehrere Buffer erkauft.

Dazu hab ich noch eine (vielleicht etwas akademische) Frage. Was ist mit 
"Endpoints".

Fast jede Übertragung läuft doch auf einen Datensatz mit Anfang und Ende 
hinaus.

Statt in den Ringpuffer schiebt die Empfangsroutine die Zeichen in den 
Endpoint (oder Datensatz Puffer).
Sie prüft bei jedem Zeichen ob die EndeOfDatensatz Bedingung erfüllt 
ist.
Wenn ja nimmt Sie das nächste Buffer das der Parser freigegeben hat.

Wäre das nicht effizienter (weil der Ringzähler quasi in buffer 
aufgeteilt wird) und ebenso universell verwendbar?

von Svenska (Gast)


Lesenswert?

> Dazu hab ich noch eine (vielleicht etwas akademische) Frage. Was ist mit
> "Endpoints".

Gibt es. Sind dann sinnvoll, wenn man mehrere unabhängige (virtuelle) 
Kanäle über eine Leitung multiplexen möchte. Dann könnte man auch 
mehrere kleine Ringpuffer anlegen.

> Sie prüft bei jedem Zeichen ob die EndeOfDatensatz Bedingung erfüllt
> ist.

Verschiedene Protokolle haben verschiedene Datensatztrenner. Wenn du 
nachrichtenbasiert auswerten möchtest, muss dein Puffer (für jeden 
Endpoint!) mindestens so groß sein, wie die größte erlaubte Nachricht, 
während der Ringpuffer nur der Entlastung dient und daher auch klein 
bleiben kann (ein/zweimal 8 Byte plus zwei Indizes reicht oft schon).

Mit einem Zustandsautomat kannst du Pakete bereits verarbeiten, während 
du sie empfängst, verteilst im Optimalfall also den Rechenaufwand über 
die Empfangszeit und kannst bereits verarbeitete Daten direkt wegwerfen. 
Wenn dein Protokoll das ermöglicht (und du eine Strategie für 
Empfangsfehler hast), bekommst du eine gleichmäßigere CPU-Auslastung und 
damit geringe Latenzen hin.

von Der Rächer der Transistormorde (Gast)


Lesenswert?

Svenska schrieb:
> Mit einem Zustandsautomat kannst du Pakete bereits verarbeiten, während
> du sie empfängst, ...

Verstanden, danke für die Erklärung.

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
Noch kein Account? Hier anmelden.