Link to home
Start Free TrialLog in
Avatar of ichigokurosaki
ichigokurosakiFlag for Afghanistan

asked on

[C/C++, LibSerial, Boost] How to tokenize a long string

I'm writing a mobile robot application under Ubuntu and i'm using two C/C++ applications which share information over the serial port like client and server.
In particular, client controls the sensors and sends information to the server which use these information to control the servos.
At the moment, i'm using three sonars (A,B,C), a 2-axis gyroscope and two encoders.

I thought about a communication protocol to use to send information about all the sensors in the same string by avoiding multiple writings on the serial port.

I'd like to know if it is possible to let the client to enclose information about all the sensors in a long string, such as:

#sonar@A1B1C1*gyroscope@OK*encoders@OK#

Open in new window

Where:
# character is to mark the beginning and the end of the whole string
* character is to separate the information

For example, with the previous string, server should now that:
sonar A, sonar B and sonar C are all ok if the following integer value is 1, on the contrary, if A1B0C1 then sonar B is not working correctly; if integer value is 2 then a collision has detecting by that sonar.
gyroscope and encoders are OK

The problem is when i have to send more information about encoders and gyroscope, for example, after the first check, when i run the robot, i'd like to send strings like this:
#sonar@A1B1C1*gyroscope@X12Y12.5*encoders@distance13#

Open in new window


where, for example, for the gyroscope, X=12 and Y=12,5 are the coordinates and for the encoder, distance = 13 is the distance covered by the robot.

I do not know if it is possible to do this in C/C++ because, for me, it's very difficult to imagine how to tokenize a string like this. Boost library can help in this?

Some months ago, i used a function to tokenize a string with just one command like command@1234 but i'm not able to imagine how to modify it in order to execute this new communication protocol.

Can you help me, please?
If you think the protocol is not good or it has to be changed in order to be implemented in C, there is no problem because i can change it.

The function i used was something like:
#include <string>
#include <sstream>

int GetValueFromLine(const std::string& sData) {

  std::string sName, sInteger;
  std::stringstream ss;
  int nResult;

  size_t sz = sDatas.find('@');

  sName = sData.substr(0,sz); // Just in case you need it later

  sInteger = sData.substr(sz + 1,isData.length() - sz);

  ss.str(sInteger);

  ss >> nResult;

  if (ss.fail()) {

    // something went wrong, probably not an integer
  }

  return nResult;
}

Open in new window

Avatar of Member_2_5069294
Member_2_5069294

In C this would be simple to do with the strtok function.  You could use that, though I assume there will be a similar method in one of the std:: classes, though it doesn't appear to exist for std::string.
Oh, hang on I misunderstood the question a little.  You want to parse the whole string, individual commands, then parameters from verbs.  Each with a different rule.
Avatar of ichigokurosaki

ASKER

Yes, i have to parse the whole string and extract different commands from it.

I thought to use # character to mark the beginning and the end of the string and to separate each command@value with the * character.

I think a parsing function would be something like this:

- start to read the string from # to #
- separate the comand@value by searching for the * character
- then separate comand and value by looking for the @ character

the problem is how to manage all the different values for each command.
Do you think it is not possible to do this?
SOLUTION
Avatar of sarabande
sarabande
Flag of Luxembourg image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks both Sarabande and Ambience.

I tried both the solutions and at the moment, Ambience solution seems to be simpler.

I'm trying to send a string like this (just to test it):
sonar-a-ok=1\n
sonar-b-ok=1\n
gyroscope-x=12\n
\n

Open in new window


and it works if i print the values in this way:

std::map<std::string, std::string> parseCommand(const std::string& data)
{
std::pair<std::string, std::string> kv, line; 

std::string temp = data;
do
{
   line = splitAtFirst(temp, "\n");
   if(line.second.empty())
      return result;

   kv = splitAtFirst(line.first, "=");
   result.insert( std::map<std::string, std::string>::value_type(kv.first, kv.second) );
   
   temp = line.second;
   
   std::cout << "Command: " << kv.first << " Value " << kv.second <<"\n"; <-- HERE I PRINT THE VALUES
}
while(true);

}

Open in new window


But i'm pretty sure i'm printing the values in the wrong way.
Moreover, i'd like to print these values in the main function in order to check all the received commands.
The problem is that i never used map in C so i do not know how to do this, can you give me some input, please?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
satsumo, good suggestion!

I can use the parsing function suggested by ambience and add a big switch like yours in the main function in order to manage all the received commands.

I just have to know how to print the values from the map in the main function because i never used a map in C and so i do not know how to print the key and its value.
Any input?
I'd either print the actual string received (useful if the command is not recognised), or simply have an array of names that correspond to verb ID's.  I like to avoid using the more complex object if there appears to be simpler way.  Nothing wrong with using std::map, it's a coding style choice.

The switch statement can be replaced with a table of function pointers indexed by verb ID.  Thats very slightly faster in case you're doing high performance stuff.  I guess that won't make any real difference when the robot is going to be much slower than your code.
A map is like a dictionary that stores values against keys. You can assign a value to a key and later fetch it based on the key only. There are a lot of examples on using map, here are a couple

http://kengine.sourceforge.net/tutorial/g/stdmap-eng.htm
http://www.cprogramming.com/tutorial/stl/stlmap.html

If you've used any STL container before then map is pretty simple to use, say you have a map variable m

m["gyroscope-x"] = 12;

to fetch the value

std::map<std::string,  std::String>::const_iterator i = m.find("gyroscope-x");
if(i != m.end())
{
  // value was found
  std::cout << "Value is " << i->second;
}

to iterate over all

std::map<std::string,  std::String>::const_iterator i = m.begin();
for(;i!=m.emd(); ++i)
{
  std::cout << "Key is " << i->first;
  std::cout << "Value is " << i->second;
}
I think what satsumo is suggesting is to assign identifiers to commands, so that you know what particular command it is. Heres what the suggestion would look like

request-type=sonar\n
sonar-a-ok=1\n
sonar-b-ok=1\n
gyroscope-x=12\n
\n

for responses you could have

response-type=sonar\n
gyroscope-x=12\n
\n
@ambience, my suggestion was exactly what the map does, matching a string to an integer.  The command string wouldn't need to contain the ID's.

The function that parses verbs from the command string would look it up in an array and return it's ID.  This way the rest of the program deals with ID's rather than strings.  It also verifies that it's one of the programs accepted commands and is a neat way to deal with errors and end of the command string (different verb ID's).

The function could use a map to match the string to an ID in the same way.  It's main purpose is to parse the verb out of the command string.  The programmer won't need to write array lookup code if they use a map.
Thanks to all for the suggestions!

I see that there are different solutions for what i want to do.
I try to explain you how my system is at the moment.

There is a server application which is connected to a bluetooth joypad (to control the robot) and to a motor driver to control the robot direction; then, there is a client application which is connected to the sensors: three sonars, a gyroscope and a GPS (for the moment).

I was thinking to do in this way:

1. Server starts and sends to the client the command: "START"
2. then client starts the sensors check routines
3. at this point, server sends the command "check"
4. client answers with the information about the sensors;
5. If everything is ok, then Server start to take information from the joypad and sends the command "check collisions" to the client
6. now, client sends "collisions_detected" to the server only if a collision is occurred.

After having checked the sensors, in point 5, Server should enters in a while loop which takes input for the joypad and from the gyroscope (asking this information to the client) in order to compensate the robot direction with the gyroscope information and exit from the while loop or stop the servos only if a collision occurs.

Sometimes, during the while loop server can ask to the client information about encoders in order to takes the distance.

It's a little bit complicated to realize all these functions ( at least with my C knowledges), but i'm trying to do it.

Do you think it's better to use an index and a map in order to improve program performances? I'd like to develop the application in the better possible way.
A lot depends on the performance, timing and reliability characteristics of the whole system.  My idea of the command sequence would be more involved than the one you have described.  Also I might use packets of binary data rather than strings.

However, I think this is a new question. Can I suggest you close this one and ask another?  This gives an opportunity for other experts to answer.  It's considered bad form for an Expert to hijack an answer thread.
>> Do you think it's better to use an index and a map in order to improve program performances?

I'm not sure as to how many commands would eventually end up being used in the system, but I dont see a map with string keys to be that much of a performance nightmare.

Map is a balanced tree and therefore lookups are O(logN), and even string comparisons are mostly going to be pretty efficient.

Also, dont forget that with int keys you'd be doing an O(N) lookup to map string keys to int ID's. This wont be happening with string keys.

My vote is to not introduce unnecessary complexity of int ID's, unless benchmarks show its really necessary.
Thanks a lot for your advices, Sastumo and Ambience!

You have a great experience with these topics!
How I envy you!

At the moment, i'm using your advices in order to perform some tests and check if they can work!

I'll open a new question to get suggestions about possible improvement on the communication protocol.
Thanks to all, guys!
@ambience, I agree, using an array of strings or a map probably won't make a great deal of difference in this case.  If the map uses a tree it will be faster than the array lookup in a majority of cases, especially if the size of the verb set increases.  It would be a bit more complex in memory usage, but assuming it's static while the program is running that's not going to be an issue.