Ansi C - UDP Case switch

OP #7810431
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
                    }
Persönliche Seite #7810473
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.

#7810490
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?

#7810498
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.

OP #7810501
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.

OP #7810506
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 :)

OP #7810507
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.

OP #7810513
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
  }
Moderator Persönliche Seite #7810525
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.

#7810533
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) { }

#7810538
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.

#7810846
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.

#7811540
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."

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren