Forum: Mikrocontroller und Digitale Elektronik Ansi C - UDP Case switch


von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Hallo, ich habe hier ein Projekt wo ein ESP32 via UDP Datenpackete
empfängt und dann Dinge tut.

ICh habe das so gelösst, dass ich den Empfangssspuffer parse und wenn es
passt entsprechend reagiere.

Mir gefällt das irgendwie nicht, weil das eine grosse Liste werden kann.
Kann man das besser machen? Case, swich?
1
                    if(strstr(rx_buffer, tasklist_start) != NULL) 
2
                    {
3
                        char *filter_msg = "tasklist_start";
4
                        char *d = strstr(rx_buffer, tasklist_start); // pointer to start of "... "
5
6
7
                            task_monitor();
8
                    }
9
10
                    if(strstr(rx_buffer, km) != NULL) 
11
                    {
12
                        char *filter_msg = "km";
13
                        char *d = strstr(rx_buffer, km); // pointer to start of "... "
14
15
                            lvgl_port_lock(0);
16
                            lv_label_set_text_fmt(ui_label2, "%.f", etappe);
17
                            lvgl_port_unlock();
18
                    }

: Verschoben durch Moderator
von Dergute W. (derguteweka)


Lesenswert?

Moin,

Welle 🧐 S. schrieb:
> Mir gefällt das irgendwie nicht, weil das eine grosse Liste werden kann.
> Kann man das besser machen? Case, swich?

Davon wird aber doch die Liste nicht kleiner oder?

Gruss
WK

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Welle 🧐 S. schrieb:
> ICh habe das so gelösst, dass ich den Empfangssspuffer parse

Du suchst strings wie "tasklist_start" im UDP-Paket? Kannst du den 
Sender modifizieren, sodass er nur einzelne Bytes statt ganzer Strings 
mit dem jeweiligen "Befehl" sendet? Das wäre einfacher und effizienter 
zu parsen und würde auch kürzere Pakete bedeuten.

von Bruno V. (bruno_v)


Lesenswert?

Welle 🧐 S. schrieb:
> Mir gefällt das irgendwie nicht, weil das eine grosse Liste werden kann.
> Kann man das besser machen? Case, swich?

Du könntest einen statt je 2 Aufrufe machen. Und die Liste verkleinern, 
wenn Du n von n Pattern ausgelesen hast. So ganz erschließt sich mir 
nicht, was Du mit filter_msg machen möchtest. Oder was z.B. km tut.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

PS: Wenn C++ eine Option ist (funktioniert problemlos mit ESP-IDF oder 
ESP-Arduino) kann man das mit Lambdas hübscher, kompakter und effizient 
umsetzen. In C ginge noch was mit Makros.

Meinst du eigentlich mit ANSI-C das, was man üblicherweise darunter 
versteht, nämlich C89? Für die ESP32 nutzt man das normalerweise 
nicht, lässt sich das ESP-IDF damit überhaupt übersetzen? Das ESP-IDF 
nutzt per Default C17. Oder meinst du einfach eine aktuelle 
standardisierte C-Version, wie eben C17?

Die ANSI hat seit C90 ja einfach die ISO-Standards für C übernommen, 
also rein technisch ist C23 (standardisiert allerdings in 2024) der 
aktuelle ANSI-C Standard, aber niemand nutzt den Begriff so, sondern 
nutzt ANSI-C als Alias für C89.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Niklas G. schrieb:
> Welle 🧐 S. schrieb:
>> ICh habe das so gelösst, dass ich den Empfangssspuffer parse
>
> Du suchst strings wie "tasklist_start" im UDP-Paket? Kannst du den
> Sender modifizieren, sodass er nur einzelne Bytes statt ganzer Strings
> mit dem jeweiligen "Befehl" sendet? Das wäre einfacher und effizienter
> zu parsen und würde auch kürzere Pakete bedeuten.

Um in die gleiche Kerbe zu schlagen: ist es wirklich möglich (vom 
Protokoll her) daß z.B. "tasklist_start" an beliebiger Stelle im 
UDP-Paket steht? Oder ist es nicht vielmehr so, daß ein UDP-Paket vom 
Typ tasklist_start mit "tasklist_start" beginnt?

von J. S. (jojos)


Lesenswert?

Man kann eine Tabelle mit Einträgen aus String und FunctionPointer 
machen. Dann im Dispatcher in der Tabelle nach dem String suchen und bei 
Erfolg den zugehörigen FP aufrufen.
In C++ etwas eleganter mit den Container Klassen, z.B. std::map was für 
so Key Value Zugehörigkeiten gemacht ist.

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Niklas G. schrieb:
> Du suchst strings wie "tasklist_start" im UDP-Paket? Kannst du den
> Sender modifizieren, sodass er nur einzelne Bytes statt ganzer Strings
> mit dem jeweiligen "Befehl" sendet? Das wäre einfacher und effizienter
> zu parsen und würde auch kürzere Pakete bedeuten.

Hallo Niklas, ja der Sender ist ein Linux.
Strings fand ich auch nicht prall ich habe nur keine andere Idee und
es ist leicht zu tracken, weil klarlext.

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Bruno V. schrieb:
> wenn Du n von n Pattern ausgelesen hast. So ganz erschließt sich mir
> nicht, was Du mit filter_msg machen möchtest. Oder was z.B. km tut.

Hallo Bruno, ich habe der Übersichthalber was gegelassen ;)
1
   if(debug_level >=5){printf("filter_msg_strlen: %d\n", strlen(d)-strlen(filter_msg));}

von J. S. (jojos)


Lesenswert?

Dann könnte man auch gleich json als Standardformat nehmen, mit 
ArduinoJson gibt es ja eine sehr gute Lib die das Handling damit sehr 
angenehm macht. Die Parameter per str Funktionen aufzudröseln ist auch 
eine fehleranfällige Sache.

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Niklas G. schrieb:
> Oder meinst du einfach eine aktuelle
> standardisierte C-Version, wie eben C17?
>
> Die ANSI hat seit C90 ja einfach die ISO-Standards für C übernommen,
> also rein technisch ist C23 (standardisiert allerdings in 2024) der
> aktuelle ANSI-C Standard, aber niemand nutzt den Begriff so, sondern
> nutzt ANSI-C als Alias für C89.

Genau :) ich bin davon ausgegangen dass das keiner so im Details drauf 
hat :)

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Axel S. schrieb:
> Um in die gleiche Kerbe zu schlagen: ist es wirklich möglich (vom
> Protokoll her) daß z.B. "tasklist_start" an beliebiger Stelle im
> UDP-Paket steht? Oder ist es nicht vielmehr so, daß ein UDP-Paket vom
> Typ tasklist_start mit "tasklist_start" beginnt?

Hallo Axel, nein das kann ich frei entscheiden. Aktuell steht nur der 
Funktionsaufruf in dem Packet.

von Welle 🧐 S. (w3llschmidt)


Lesenswert?

J. S. schrieb:
> Dann könnte man auch gleich json als Standardformat nehmen, mit
> ArduinoJson gibt es ja eine sehr gute Lib die das Handling damit sehr
> angenehm macht. Die Parameter per str Funktionen aufzudröseln ist auch
> eine fehleranfällige Sache.

Ja, ich war nur zu faul auf der Absenderseite ein JSon Packet zu formen,
weil es ja nur simple Ein/Aus Anforderungen sind.

Json kann ich in dem Projekt auch verarbeiten. Aber das Parst auch nur
Strings.

https://github.com/sheredom/json.h
1
/* Loop over all keys of the root object */
2
  for (i = 1; i < r; i++) 
3
  {
4
        if (jsoneq(JSON_STRING, &t[i], "lon") == 0) 
5
        {
6
            printf("long: %.*s\n", t[i + 1].end - t[i + 1].start, JSON_STRING + t[i + 1].start);
7
        i++;
8
        } 
9
10
        else if (jsoneq(JSON_STRING, &t[i], "lat") == 0) 
11
        {
12
            printf("lat: %.*s\n", t[i + 1].end - t[i + 1].start, JSON_STRING + t[i + 1].start);
13
        i++;
14
        } 
15
16
        else if (jsoneq(JSON_STRING, &t[i], "name") == 0) 
17
        {
18
            const char*  strStart  = JSON_STRING + t[i+1].start;
19
            const size_t strLength = t[i+1].end - t[i+1].start;
20
            char wetterort[128]={0};
21
                strncpy( wetterort, strStart, strLength );
22
                lv_textarea_set_text(ui_WetterOrt, wetterort);
23
                lv_textarea_set_text(ui_WetterTimestamp, stamp_str);
24
            printf("name: %.*s\n", t[i + 1].end - t[i + 1].start, JSON_STRING + t[i + 1].start);
25
        i++;
26
        } 
27
28
        else {
29
        //printf("Unexpected key: %.*s\n", t[i].end - t[i].start,JSON_STRING + t[i].start);
30
        }
31
  }

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Welle 🧐 S. schrieb:
> Aber das Parst auch nur Strings.

Wenn Du unbedingt auf das Parsen von Strings verzichten möchtest, mach 
es mit Zahlen:

Definiere ein Funktionspointer-Array, f[] initialisiere dieses mit 
Deinen aufzurufenden Funktionen. Dann brauchst Du nur eine Zahl n zu 
übertragen - bei max. 256 Funktionen sogar nur ein Byte.

Dann ist der Aufruf auf dem Zielsystem:
1
(*f[n])();
Einfacher gehts nicht. Kryptischer aber auch nicht.

von Andras H. (andras_h)


Lesenswert?

Welle 🧐 S. schrieb:
> Mir gefällt das irgendwie nicht, weil das eine grosse Liste werden kann.
> Kann man das besser machen? Case, swich?

Ja kann man machen. Theoretisch, kannst alles in eine Array von Struktur 
packen, und sagen dass ein Stück Code dann alle elemente deine Array 
vorknüpft und guckt ob der String dann passt, wenn ja dann wird eine 
Funktionspointer aufgerufen.

Soetwas, mehr oder weniger:

typedef void (*Callback)(void);

typedef struct _MyDummyStruct {
    const char findString[];//bin nicht sicher ob das so compiliert
    Callback callback;
} MyDummyStruct;

MyDummyStruct myDummyStruct[2] = {
 {"tasklist_start", &task_monitor},
 {"km", &callback_2},
};


for (int x = 0; x < (sizeof(myDummyStruct) / sizeof(*myDummyStruct)); 
x++) {
  if(strstr(rx_buffer, myDummyStruct[x].findString) != NULL)
  {
     char *d = strstr(rx_buffer, myDummyStruct[x].findString);
     if (myDummyStruct[x].callback != NULL) {
       myDummyStruct[x].callback();
    }
  }
}

void task_monitor(void) {
}
void callback_2(void) {
}

von Peter D. (peda)


Lesenswert?

Ich lasse das strcmp einfach in einer Schleife über eine Tabelle aus 
Befehlswort und Funktionsadresse laufen.
Die Tabelle endet mit einem Leerstring:
1
command_t const cmd_table[] =
2
{
3
  {MODULE_CMD_UPTIME    cmd_uptime},         
4
  {MODULE_CMD_GETSTATUS cmd_status},         
5
  {MODULE_CMD_RESET     cmd_reset},
6
  {"", 0},
7
};

Das Befehlswort ist als Macro definiert. Somit ist garantiert, daß man 
sich nicht verschreiben kann, d.h. Sender und Empfänger importieren es 
aus dem gleichen h-File.

: Bearbeitet durch User
von Welle 🧐 S. (w3llschmidt)


Lesenswert?

Peter D. schrieb:
> Ich lasse das strcmp einfach in einer Schleife über eine Tabelle aus
> Befehlswort und Funktionsadresse laufen.
> Die Tabelle endet mit einem Leerstring:

Danke Peter, dass greife eich auf!

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Die ANSI hat seit C90 ja einfach die ISO-Standards für C übernommen,

Es ist genau umgekehrt: Die ISO hat den ANSI-Standard übernommen. C89 
war ein ANSI-Standard. C90 ist praktisch 1:1 das gleiche, aber von der 
ISO. Alle nachfolgenden Versionen kamen dann ebenfalls von dort.

> also rein technisch ist C23 (standardisiert allerdings in 2024) der
> aktuelle ANSI-C Standard,

Nein, es ist der aktuelle ISO-Standard.

> aber niemand nutzt den Begriff so, sondern nutzt ANSI-C als Alias für
> C89.

Weil eben nur C89 tatsächlich von ANSI kam.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Rolf M. schrieb:
> Die ISO hat den ANSI-Standard übernommen.

Den ersten, ja.

Rolf M. schrieb:
> Nein, es ist der aktuelle ISO-Standard.

Ja. Aber auch der aktuelle Standard nach ANSI, denn die ANSI hat den 
ISO-Standard ratifiziert, während sowohl ISO als auch ANSI den 
originalen Standard C89 zurückgezogen haben.

Eben so wie die DIN auch ENs übernimmt.

von 900ss (900ss)


Lesenswert?

Peter D. schrieb:
> Ich lasse das strcmp einfach in einer Schleife über eine Tabelle aus
> Befehlswort und Funktionsadresse laufen.

So hätte ich es auch gemacht und hatte jojos oben schon vorgeschlagen. 
Geht einfach, ist gut lesbar und das ohne C++ ;)

von Rolf M. (rmagnus)


Lesenswert?

Niklas G. schrieb:
> Rolf M. schrieb:
>> Nein, es ist der aktuelle ISO-Standard.
>
> Ja. Aber auch der aktuelle Standard nach ANSI, denn die ANSI hat den
> ISO-Standard ratifiziert, während sowohl ISO als auch ANSI den
> originalen Standard C89 zurückgezogen haben.

Man kann ihn auch bei ANSI im webstore kaufen, aber er heißt dort 
trotzdem
ISO/IEC 9899 und nicht ANSI X3.159. Sprich: Es ist kein ANSI-Standard, 
sondern ein ISO-Standard, den man lediglich auch bei ANSI kaufen kann.

Auf 
https://blog.ansi.org/c-programming-language-standard-iso-iec-9899-2024/ 
heißt es dann auch:

"While it originated in the 70s, the existing practices of C were 
unified and expanded upon in the standard ANSI X3.159-1989, or C89. 
Because of these origins, many people still call the C programming 
language standard ANSI C. However, the C standard was adopted 
internationally in 1990, and it has long been ISO/IEC 9899, which is 
developed by ISO/IEC JTC 1. The integration of C practices into an 
international standard sparked its association with the name ISO C."

: Bearbeitet durch User
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.