Forum: PC-Programmierung C++: speichern, verbinden, suchen von HEX Werten


von Michaela (Gast)


Lesenswert?

Hallo Gemeinde,

ich steige gerade von µC / C auf das BeagleBone Black in C++ um und bin 
mittlerweile ein bisschen verweifelt. Ich hoffe sehr, hier ein paar 
Ideen und Vorschläge zu bekommen.

Ich ich habe eine Funktion
1
int decodeData(char *buffer, int received_bytes)

die bekommt zeitlich versetzt die zuvor über einen Bus empfangenen 
Hex-Werte übergeben. Diese muss ich "nichtflüchtig" erstmal 
zwischenspeichern (ich denke da an "static") und sobald mehr als 14 
Bytes (= 14 Hex-Werte) übergeben wurden, muss ich innerhalb dieser 14 
Werte nach einem Hex-Muster "0x0201C1" suchen. Hier beginnt nämlich ein 
wichtiges Paket.

So, ich könnte das natürlich alles mit einem Array machen und hier mit 
diversen Schleifen usw. arbeiten und suchen lassen. Aber gibt's da in 
C++ nicht eine elegantere Lösung?? Ich hab jetzt schon viel mit strings 
und vectoren versucht, aber immer bleibe ich irgendwo hängen... Oder 
gibt's da echt keine gute Lösung außer den "alten" C-Arrays??

Tausend Dank!
Michaela

: Verschoben durch User
von sebi707 (Gast)


Lesenswert?

Das Problem ist hier wohl, dass du die letzten 14 Zeichen speichern 
willst und das auch noch der Reihenfolge nach. Wenn du vector oder 
strings benutzt hast du das Problem das du vorne ein Zeichen wegnehmen 
musst und hinten eins dranhängen. Gerade das vorne wegnehmen sind 
operationen die von beiden Typen nicht effizient umzusetzen sind. Du 
suchst wohl so etwas wie einen Ringbuffer. Da gibts in C++ direkt nichts 
aber die boost Library hat sowas: 
http://www.boost.org/doc/libs/1_55_0/doc/html/circular_buffer.html

Boost ist natürlich ziemlich komplexes C++ und eventuell ungeeignet für 
Mikrocontroller. Habe ich selbst noch nicht ausprobiert. Ich würde fast 
bei einem C Array bleiben und das ganze "von Hand" machen.

von Rolf Magnus (Gast)


Lesenswert?

sebi707 schrieb:
> Das Problem ist hier wohl, dass du die letzten 14 Zeichen speichern
> willst und das auch noch der Reihenfolge nach. Wenn du vector oder
> strings benutzt hast du das Problem das du vorne ein Zeichen wegnehmen
> musst und hinten eins dranhängen. Gerade das vorne wegnehmen sind
> operationen die von beiden Typen nicht effizient umzusetzen sind.

> Da gibts in C++ direkt nichts

Es gibt std::deque.

von CPlusPlus (Gast)


Lesenswert?

sebi707 schrieb:
> Gerade das vorne wegnehmen sind
> operationen die von beiden Typen nicht effizient umzusetzen sind.

Dafür gibts entweder std::list (Doppelt verkettete Liste). Hat aber 
(eben durch die Doppel-Verkettung) einen großen Overhead, also für 
"char" nicht so sinnvoll.

Dann gibts noch "std::deque", eine "double ended queue", die ist exakt 
für sowas da. Effizient auch für 1-byte-Datentypen, push&pop auf beiden 
Enden in (armortisierter) konstanter Zeit.
Kopiert aber oft ihren Inhalt um, d.H. nichts für Datentypen mit teurem 
Copy-Construktor...

von Michaela (Gast)


Lesenswert?

Hallo zusammen,

und Danke erstmal!

std::deque und boost

sagen mir erstmal leider gar nichts. Auch nach kurzer Suche und dem 
Überfliegen einiger Internetseiten hat sich daran nichts geändert. 
Scheint für mich wohl ohne char array doch zu kompliziert zu sein...

Dann wollte ich doch zu viel und bleibe somit wohl besser bei einem C 
array. Wenigstens habe ich es (auch mit netter Hilfe) versucht.

Danke euch trotzdem sehr für die schnelle, freundliche und kompetente 
Hilfe!

Michaela

von Dr. Sommer (Gast)


Lesenswert?

Warum nicht schlicht std::string und std::string::find?
1
std::string buffer;
2
int decodeData(char *rec, int received_bytes) {
3
  buffer += std::string (rec, received_bytes);
4
  if (buffer.size () >= 14) {
5
    auto pos = buffer.find ("\x02\x01\xC1", 3);
6
    if (pos != std::string::npos) {
7
      std::cout << "Gefunden an Stelle " << pos << "\n";
8
    }
9
  }
10
}

von Dr. Sommer (Gast)


Lesenswert?

CPlusPlus schrieb:
> Kopiert aber oft ihren Inhalt um, d.H. nichts für Datentypen mit teurem
> Copy-Construktor...
Nicht mehr seit C++11 und Move Semantics, man muss nur an den richtigen 
Stellen std::move einstreuen.

von Michaela (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Warum nicht schlicht std::string und std::string::find?std::string
> buffer;
> int decodeData(char *rec, int received_bytes) {
>   buffer += std::string (rec, received_bytes);
>   if (buffer.size () >= 14) {
>     auto pos = buffer.find ("\x02\x01\xC1", 3);
>     if (pos != std::string::npos) {
>       std::cout << "Gefunden an Stelle " << pos << "\n";
>     }
>   }
> }

Mensch super! Danke Dr. Sommer! Funktioniert wunderbar. So muss ich doch 
keine Schleifen programmieren.
Ich war schon relativ weit mit den Strings, aber auf:
1
buffer.find ("\x02\x01\xC1", 3)

bin und wäre ich nie gekommen.

Nochmals Danke auch an die anderen! Sst einfach klasse, wie einem hier 
geholfen wird.

LG Michaela

von Dr. Sommer (Gast)


Lesenswert?

Michaela schrieb:
> bin und wäre ich nie gekommen.
Tip: Nachgucken kann man das unter
http://en.cppreference.com/w/cpp/string/basic_string

von Michaela (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Tip: Nachgucken kann man das unter
> http://en.cppreference.com/w/cpp/string/basic_string

Danke. Die Funktion "find" hatte ich schon gefunden. Aber dass man die 
hex-Werte mit "\x02\x01\xC1" maskiert, darauf wäre ich nie gekommen.

Vielleicht darf ich hier gerade noch eine Frage stellen, wo ich glaube 
ich irgendwie voll auf dem Schlauch stehe.

Dieser von mir empfangene String besteht ja aus 14 Byte, die drei 
Startbits
'0x02' '0x01' '0xC1'
konnte ich jetzt durch eure Hilfe komfortabel erkennen.

Byte vier und fünf repräsentieren zusammen eine Zweierkomplement-Zahl, 
also z.B.: 0xFFF8 (Bin 1111111111111000) = -8
Die zwei Bytes habe ich mit
1
Zahl = BufferLocal.substr (4,2);
extrahiert und habe jetzt vergeblich versucht, den String "Zahl" in 
einen signed int zu verwandeln...
U.A. habe ich es mit stringstream versucht, aber das geht ja davon aus, 
dass die zwei Bytes z.B. die Buchstaben 'FA' darstellen und gibt mir 
dann 250 aus. Beim googlen kommt natürlich immer genau dieser Fall....

Nochmals Danke!
Michaela

von Karl H. (kbuchegg)


Lesenswert?

Ich denke du merkst schön langsam selbst, dass eine Bytefolge nun mal 
kein String ist. Es ist daher nicht wirklich klug, mit Strings an 
derartige Dinge heranzugehen. Strings sind in erster Linie für Texte 
gedacht und nicht um damit beliebige Bytes zu bearbeiten. Ein paar 
Operationen mögen ja funktionieren, aber std::string ist nun mal auf 
Texte geprägt.

Du kannstz natürlich immer noch die Bytes 'per Hand' zusammensetzen

Summe = Byte1 * 256 + Byte2;

man braucht nicht wirklich für alles Spezialfunktionen.

von Dr. Sommer (Gast)


Lesenswert?

Michaela schrieb:
> extrahiert und habe jetzt vergeblich versucht, den String "Zahl" in
> einen signed int zu verwandeln...
Das Problem ist dass das signed Zahlenformat nicht standardisiert ist. 
Eine einfache Möglichkeit die mit dem GCC und x86 (ARM vermutlich auch) 
funktioniert ist:
1
int16_t out = static_cast<int16_t> ( (static_cast<uint16_t> (static_cast<uint8_t> (Zahl[0])) << 8) | static_cast<uint16_t> (static_cast<uint8_t> (Zahl[1])));
Ziemlich hässlich, was kürzeres ist mir grad nicht eingefallen... 
Funktioniert dafür auch mit deinem Big Endian signed format.

Karl Heinz schrieb:
> Ich denke du merkst schön langsam selbst, dass eine Bytefolge nun mal
> kein String ist. Es ist daher nicht wirklich klug, mit Strings an
> derartige Dinge heranzugehen. Strings sind in erster Linie für Texte
> gedacht und nicht um damit beliebige Bytes zu bearbeiten.
Warum nicht?! Ein C++-string ist wie ein vector<char> mit Extras, warum 
sollte man ihn nicht dafür verwenden? Es handelt sich schließlich nicht 
um C-Strings, die keine 0-Bytes mögen...

von Michaela (Gast)


Lesenswert?

Karl Heinz schrieb:
> Summe = Byte1 * 256 + Byte2;

Hallo Karl Heinz,

ja natürlich, das geht auch. Ich kann auch die einzelnen Bytes des 
Strings mir so holen, aber dann wird daraus trotzdem noch kein signed:
1
int Summe = Byte1 *256 + Byte2;
1
int Summe = (Byte1 << 8) + Byte2;
1
int Summe = (Byte1 << 8) | Byte2;

immer bekomme ich aus den zwei Bytes '0xFF' und '0xFA' die Zahl 65530
und nicht die negative Zahl -6...

von Dr. Sommer (Gast)


Lesenswert?

Karl Heinz schrieb:
> Du kannstz natürlich immer noch die Bytes 'per Hand' zusammensetzen
>
> Summe = Byte1 * 256 + Byte2;
Das klappt leider nicht mit den signed Typen...

von Michaela (Gast)


Lesenswert?

Dr. Sommer schrieb:
> int16_t out = static_cast<int16_t> ( (static_cast<uint16_t>
> (static_cast<uint8_t> (Zahl[0])) << 8) | static_cast<uint16_t>
> (static_cast<uint8_t> (Zahl[1])));

Ja, so klappt es!! Von solchen Lösungen bin ich mit meinem Wissen echt 
noch weit entfernt ...

Mich hat die fehlende String-Funktion bei C immer irgendwie gefehlt, es 
bleiben ja dann nur die Char-Arrays. Deshalb wollte ich jetzt beim 
Umstieg so viel wie möglich von den "mächtigen Strings" profitieren. 
Oder es zumindest mal damit versuchen.
Heute hat's mich leider wieder den ganzen Abend gekostet...

Um so mehr nochmal ein herzliches Danke!
Und guten Nacht...

Michaela

von Dr. Sommer (Gast)


Lesenswert?

Michaela schrieb:
> Ja, so klappt es!! Von solchen Lösungen bin ich mit meinem Wissen echt
> noch weit entfernt ...
Die Integer-Konversions-Geschichten sind in C/C++ ziemlich delikat, da 
sie unter allen (un)möglichen Plattformen funktionieren sollen, mit 
13-Bit-'chars', 1-Complement-Integern etc. ... Daher gibt es auch keine 
eingebauten fertigen allgemeinen Konvertierungsfunktionen. Durch ein 
paar alte aus C geerbte ungünstige Regeln wird das dann noch 
verkompliziert.

von Michaela (Gast)


Lesenswert?

Hallo zusammen,

ich trau mich ja schon fast nicht mehr zu schreiben, aber vielleicht 
könnte sich jemand mal meinen finalen Code anschauen. Das Programm hängt 
sich leider unter bestimmten Umständen immer auf:
1
int processWeather(string &Daten) {
2
    unsigned int typ = Daten[0];
3
    unsigned int adresse = Daten[1];
4
    int16_t temp = static_cast<int16_t> ( (static_cast<uint16_t> (static_cast<uint8_t> (Daten[2])) << 8) | static_cast<uint16_t> (static_cast<uint8_t> (Daten[3])));
5
    float feuchte = static_cast<float>(Daten[4]*256 + Daten[5])/10;
6
    float wind = static_cast<float>(Daten[6]*256 + Daten[7])/10;
7
    unsigned int wippen = Daten[8]*256 + Daten[9];
8
    unsigned int regen = Daten[10];
9
10
    float temp_float = static_cast<float>(temp)/10;
11
12
    cout << "Typ: " << typ << endl;
13
    cout << "adresse: " << adresse << endl;
14
    cout << "temp: " << temp_float << endl;
15
    cout << "feuchte: " << feuchte << endl;
16
    cout << "wind: " << wind << endl;
17
    cout << "wippen: " << wippen << endl;
18
    cout << "regen: " << regen << endl;
19
20
    time_t now = time(0);
21
    char timestamp[22];
22
    strftime(timestamp, 22, "%d.%m.%Y - %H:%M:%S", localtime(&now));
23
24
25
    ofstream weatherfile ("/weather.txt", ios::out | ios::app);
26
    if (weatherfile.is_open()) {
27
        weatherfile << timestamp << " >> " << typ<<"#"<<adresse<<"#"<<temp_float<<"#"<<feuchte<<"#"<<wind<<"#"<<wippen<<"#"<<regen << endl;
28
        weatherfile.close();
29
     }
30
    else {
31
        cout << "Datei konnte nicht geoeffnet werden!" << endl;
32
    }
33
34
    return 1;
35
}

Und zwar klappt das wunderbar, bis ich die Zeilen
1
    time_t now = time(0);
2
    char timestamp[22];
3
    strftime(timestamp, 22, "%d.%m.%Y - %H:%M:%S", localtime(&now));
einfüge, bzw. die Zeilen zum Abspeichern der Datei. Dann läuft das 
Programm einmal durch und bleibt dann hängen.
Auch wenn ich diese Zeilen drin lasse, aber dafür die oberen Zeilen 
(Zuordnung der Werte aus dem Datenfeld) weglasse, funktionert der 
Code...

Habe ich da irgendwelche Fehler drin, die in der Summe zu einem Absturz 
führen?

Mein Remote-Debuggen auf dem BeagelBoard läuft noch nicht, sonst hätte 
mir das vielleicht geholfen.

Zum wiederholten mal DANKE!
Michaela

von Dr. Sommer (Gast)


Lesenswert?

Michaela schrieb:
> int processWeather(string &Daten) {
Veränderst du "Daten" in der Funktion? Nein. Also wenn schon "const 
std::string& Daten".
>     unsigned int typ = Daten[0];
>     unsigned int adresse = Daten[1];
>     int16_t temp = static_cast<int16_t> ( (static_cast<uint16_t>
> (static_cast<uint8_t> (Daten[2])) << 8) | static_cast<uint16_t>
> (static_cast<uint8_t> (Daten[3])));
>     float feuchte = static_cast<float>(Daten[4]*256 + Daten[5])/10;
>     float wind = static_cast<float>(Daten[6]*256 + Daten[7])/10;
>     unsigned int wippen = Daten[8]*256 + Daten[9];
>     unsigned int regen = Daten[10];
Ja, äh, erläutere doch mal das Datenformat, es ist jetzt nicht so leicht 
zu erraten ob das tut was es sollte!
>     strftime(timestamp, 22, "%d.%m.%Y - %H:%M:%S", localtime(&now));
localtime ist nicht thread-safe, verwendest du mehrere Threads?
> einfüge, bzw. die Zeilen zum Abspeichern der Datei. Dann läuft das
> Programm einmal durch und bleibt dann hängen.
Ja wo denn genau?
> Habe ich da irgendwelche Fehler drin, die in der Summe zu einem Absturz
> führen?
Schwierig zu sagen bei der Konvertiererei.
> Mein Remote-Debuggen auf dem BeagelBoard läuft noch nicht, sonst hätte
> mir das vielleicht geholfen.
Dann debugge halt lokal durch installation und Aufruf von "gdb" auf der 
Kommandozeile, vllt auch valgrind. Dann kannst du sehen wo es stehen 
bleibt/abstürzt. Kompilieren mit "-g" nicht vergessen.

von Michaela T. (michaela)


Lesenswert?

Dr. Sommer schrieb:
> Dann debugge halt lokal durch installation und Aufruf von "gdb" auf der
> Kommandozeile, vllt auch valgrind. Dann kannst du sehen wo es stehen
> bleibt/abstürzt. Kompilieren mit "-g" nicht vergessen.

Danke für den Kommandozeilen-Tipp. Nach ein bisschen Einarbeitung konnte 
ich mit dem Debugger arbeiten und auch den Fehler finden. Hatte an einer 
ganz anderen Stelle im Programm noch einen Timer laufen, der bei 
längerer Abarbeitungszeit in anderen Funktionen einen Überlauf hatte. 
Die Zeit wurde immer gerade dadurch erreicht, wenn die obige Funktion 
mit weiteren Befehlen "verlängert" wurde.

Saudoofer Fehler...

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.