Ich muss auf einer neu entwickelten Hardware auf AVR-Basis ein bestehendes Protokoll zur Kommunikation/Steuerung implementieren. Die Kommandos, die dekodiert werden müssen, haben folgendes Schema: zB: "SH1.0;" bedeutet Block 1, Position 0, High. Auch möglich wäre beispielsweise "SL4.12;", also: Block 4, Position 12, Low. Jede Anweisung wird mit ";" beendet. Ich muss also die drei Informationen Block, Position und High/Low für die Weiterverarbeitung herausfiltern. Mir ist klar, dass ich diese Aufgabe mit strtok lösen kann, mir fehlt nur der Ansatz. Für diesen Gedankenanstoß wäre ich dankbar. Grüße AVR_Dude
Deine Beispiele nach zu urteilen lässt sich Dein Problem mit einem endlichen Automaten (FSM) schnell und einfach lösen. Der erste Zustand fängt "S" ab, der 2. prüft auf "L" oder "H" und der 3. liest eine Zahl. Dann ein Punkt und nochmal eine Zahl, final dann das Semikolon. Ist recht einfach.
Ja diese Unterteilung sollte funktionieren. Ich habe vergessen zu erwähnen: Eine fehlerhafte Eingabe soll zu einem Verwerfen des aktuellen Strings führen. Wie sähe die passende Grundstruktur in C dazu aus?
Such mal nach FSM etc. hier im Forum, da gibt's etliche Beispiele. Einfacher Ansatz: Nimm einen Pointer P auf das Erste Zeichen. P wird dann je FSM-Zustand an eine Scan-Funktion übergeben (z.B. "S" erkennen). Ist das Ergebnis positiv, dann wird P erhöht, sonst nicht. Evtl. müssen mehrere Funktionen aufgerufen werden (nicht- deterministischer Automat), wird dabei kein positives Ergebnis geliefert, dann liegt ein Fehler vor. Der erste Schritt dazu ist, erst einmal den Automaten zu modellieren. Dann die Funktionen schreiben und letztendlich die Hauptschleife, die den FSM-Ablauf steuert und die Funktionen aufruft. Fehlererkennung: Eine allgem. Bulletproof- Lösung gibt's nicht. Bei Dir hilft es, das Semikolon zu suchen und dann P dorthin zu setzen.
erscheint mir einfach, nur DU musst wissen was für dich Fehler sind. Ist nach dem ; Ende? wird ein 0 Terminator erwartet oder CR oder CR LF oder gar nur LF? Darf es nach dem ; weitergehen und wird ignoriert? Darf es genau nur bis ; gehen und jedes weitere Zeichen wäre ein Fehler? Ist S immer an erster Stelle und H oder L immer an 2ter Stelle dann wurde das Suchkriterium schon genannt. Sollte SH oder SL irgendwo in der Kette liegen dann wäre strstr "SH" oder "SL" die richtige Methode um zu starten und sich von dort bis zum Ende zu hangeln. Darf die Zahl nach dem SH/SL und vor dem . nur 1-stellig sein oder wird die evtl.(später) mehrstellig? Ist lange her da hatte ich so einen Parser in der Prüftechnik für Baugruppenrahmen mit Relais "set, 1, 3, 567" "reset, 2, 6, 4" setze 1. BG Rahmen (Baugruppen Rahmen) 2. Relaiskarte 3. Relais
AVR_Dude schrieb: > Die Kommandos, die dekodiert werden müssen, haben folgendes Schema: > > zB: "SH1.0;" bedeutet Block 1, Position 0, High. > [...] > Ich muss also die drei Informationen Block, Position und High/Low für > die Weiterverarbeitung herausfiltern. > > Mir ist klar, dass ich diese Aufgabe mit strtok lösen kann, strtok(3) braucht einen Delimiter, in Deinen Kommandos hast Du aber keinen. Du hast nur einen Delimiter zwischen Deinen Kommandos, darum kannst Du strtok(3) nur nehmen, um einen Stream von mehreren Kommandos in einzelne Kommandos zu separieren. Um dann die Werte aus den vereinzelten Kommandos auszulesen, ist strtok(3) nicht geeignet; dazu brauchst Du sowas wie sscanf(3) oder etwas Ähnliches.
okay danke erstmal für den Input. Ich habe jetzt mal eine erste (noch nicht vollständige) Version einer State Machine für meine Anwendung entworfen. Um eine erste erste lauffähige Implementierung zu haben, habe ich für eine einzelne Position eine komplizierte Struktur mit if-Verzweigungen aufgebaut:
1 | if (extern_string[0] == 'S' || extern_string[0] == 's') |
2 | {
|
3 | if (extern_string[1] == 'H' || extern_string[1] == 'h') |
4 | {
|
5 | if (extern_string[2] == '1') |
6 | {
|
7 | if (extern_string[3] == '.') |
8 | {
|
9 | if (extern_string[4] == '1') |
10 | {
|
11 | if (extern_string[5] == ';') |
12 | {
|
13 | setze_position1.1(); |
14 | put_string(extern_string); |
15 | }
|
16 | }
|
17 | }
|
18 | }
|
19 | }
|
20 | |
21 | if (extern_string[1] == 'L' || extern_string[1] == 'l') |
22 | {
|
23 | if (extern_string[2] == '1') |
24 | {
|
25 | if (extern_string[3] == '.') |
26 | {
|
27 | if (extern_string[4] == '1') |
28 | {
|
29 | if (extern_string[5] == ';') |
30 | {
|
31 | loesche_position1.1(); |
32 | put_string(extern_string); |
33 | }
|
34 | }
|
35 | }
|
36 | }
|
37 | }
|
38 | }
|
Dieses Konstrukt wird aber unfassbar kompliziert, zumal ich 4*16 Zustände abfragen würde...
AVR_Dude schrieb: > Dieses Konstrukt wird aber unfassbar kompliziert ja, aber das liegt an dir. So wie du es programmiert hast, kannst du auch gleich für jede Möglichkeit ein Stringvergleich machen. Dein Ablaufplan sieht doch gar nicht so schlecht aus, den musst du nur etwas geschickter umsetzen.
> Für diesen Gedankenanstoß wäre ich dankbar.
Vergiss den ganzen Mist und lass dir deinen Parser automatisch von flex
generieren. Das hat sich bei mir sehr gut bewaehrt.
Klar, beim erstenmal 2h zusaetzlich notwendige Einarbeitungszeit, aber
bei jedem neuen Kommando oder einer kleinen Verbesserung sind es nur
Sekunden und alles ist gut.
Olaf
Habe die Zerlegung im reinen K&R - C. Getestet auf den Arduino.
1 | int i; |
2 | int ch; |
3 | int Pos, Block; |
4 | int LowHigh; |
5 | char input [10]; |
6 | |
7 | |
8 | void setup() { |
9 | strcpy(input, "SH3.12;"); //fuer Test |
10 | if(input[1] == 'H') |
11 | LowHigh = 1; |
12 | else
|
13 | LowHigh = 0; |
14 | |
15 | Block = input[2] - '0'; |
16 | |
17 | for(i=4; input[i]; i++) |
18 | if(input[i] == ';') |
19 | input[i] = '\0'; |
20 | |
21 | sscanf(input + 4, "%d", &Pos); |
22 | |
23 | Serial.begin(9600); |
24 | Serial.print("LowHigh = "); Serial.println(LowHigh); |
25 | Serial.print("Block = "); Serial.println(Block); |
26 | Serial.print("Pos = "); Serial.println(Pos); |
27 | |
28 | |
29 | }
|
30 | |
31 | void loop() { |
32 | // put your main code here, to run repeatedly:
|
33 | |
34 | }
|
0815 schrieb: > Habe die Zerlegung im reinen ist aber wirklich 0815 was wenn if(input[0]!='S' ist? was wenn strcpy(input[10], überläuft? was wenn '.' ein ',' ist? was wenn input[2] ein '?' o.a. ist? was wenn nie ein ';' kommt? for(i=4; input[i]; i++) if(input[i] == ';') Ich würde ja die Eingabezeile erst mal auf Länge testen dann mit strstr nach "S" suchen ptr drauf setzen und auf *++ptr (wenn nicht 0) H oder L prüfen und auf *++ptr (wenn nicht 0) auf >='0' && <='9' prüfen und auf *++ptr (wenn nicht 0) auf '.' prüfen strstr(ptr; ";") und strlen(ptr) prüfen
:
Bearbeitet durch User
Man muss nicht auf Länge testen (bei ein-/mehrstelligen Zahlen geht das sowieso daneben), sondern kann zeichenweise durchgehen, z.b. gleich im Empfangsinterrupt. ANFANG: c == 'S'; ~>LESE_HOEHE sonst: ~>ANFANG LESE_HOEHE: c =='H': hoehe = high; ~>LESE_BLOCK_ERSTE c =='L': hoehe = low; ~>LESE_BLOCK_ERSTE sonst: ~>ANFANG LESE_BLOCK_ERSTE: c == '0'..'9': block = int_from(c); ~>LESE_BLOCK_REST sonst: ~>ANFANG LESE_BLOCK_REST: c == '0'..'9': block*=10; block += int_from(c); ~>LESE_BLOCK_REST c == '.': ~>LESE_POSITION_ERSTE sonst: ~>ANFANG usw.
strtok brauchst du dafür nicht, außer dem Zahlen einlesen mit strtol ist alles mit einfachen Pointervergleichen machbar. char* parse_cmd(const char *s){ /* Rückgabe zeigt auf Stringende oder auf erstes fehlerhaftes Zeichen */ int block,pos,val; if(*s=='S'){ s++; if(*s=='H')val=1; else if(*s=='L')val=0; else return s; s++; block = strtol(s, &s, 10); if (*s!='.')return s; s++; pos = strtol(s, &s, 10); if (*s!=';')return s; s++; set_output(block,pos,val); /* Befehl ausführen */ return s; } /* hier evt Befehle mit anderen Anfangsbuchstaben */ } Aufruf mit Fehlermeldung: ... s=parse_cmd(input_str); if( *s)printf( "kann nicht verstehen: %s\n",s); ...
danke für den Ansatz, Jobst Quis. ich übergebe parse_cmd meinen string, komme dann auch bis
1 | block = strtol(s, &s, 10); |
'block' hat danach allerdings den Wert 0.
1 | if (*s!='.')return s; |
wird dann aber wieder korrekt übersprungen, die Pointer Position wandert also korrekt weiter. Woran könnte es liegen, dass strtol 0 zurückliefert?
okay, <stdlib.h> hat gefehlt. Zuvor hat er aber nicht gemeckert, dass er die Funktion nicht kennt.
Die erste Version läuft soweit, vielen Dank an alle. Wenn nochmal Fragen/Ergänzungen aufkommen, melde ich mich.
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.